Hi,

We have an X300 with LFRX on slot A, and LFTX on both slots (uhd_usrp_probe 
output attached).

The idea was to do signal processing on two synchronously sampled channels from 
the same LFRX board, using a custom RFNoC OOT. So something along the lines of:

Radio0,0 LFRX(A) -> DDC0,0 -> |in_0 OOT-block out | -> DUC1,0 -> LFTX(A) 
Radio1,0
Radio0,1 LFRX(B) -> DDC0,1 -> |in_1               |

I tried to test dual-channel processing with RFNoC blocks shipped with UHD, so 
I built a custom FPGA-bitstream with various blocks connected to individual 
stream points (as listed in the uhd_usrp_probe output, yml file is also 
attached).

So the first obvious test was to loopback two RX channel to two TX channels, 
like this:

Radio0 LFRX(A) -> DDC0,0 -> DUC1,0 -> LFTX(A) Radio1
Radio0 LFRX(B) -> DDC0,1 -> DUC0,0 -> LFTX(A) Radio0

First channel goes from Radio0 to Radio1, and it seems to work. But I can't get 
the second channel to work. So is it even possible to loop back data from RX to 
TX on the same radio? Each radio instance has only one TX DSP chain, as I 
understood, so I had to use the TX chain on the Radio0 side, too. The custom 
FPGA image has separate SEPs for each DDC and DUC, so traffic should be 
correctly routed through the CHDR crossbar. Please see the attached minimal C++ 
example (loopback_test.cpp, modeled after the loopback example shipped with UHD 
source code).

Active connections formed in the test program are like so:

0/Radio#0:0==>0/DDC#0:0
0/Radio#0:1==>0/DDC#0:1
0/DDC#0:0-->0/DUC#1:0
0/DDC#0:1-->0/DUC#0:0
0/DUC#1:0==>0/Radio#1:0
0/DUC#0:0==>0/Radio#0:0

Graph is committed without problems.

The program outputs something like:

Issuing start stream cmd...
OWait...
U

Test signal propagates fine from Radio0 to Radio1, but the second one does not 
seem to work. The overflow indicator "O" always appears when trying use the 
second channel.

In an attempt to eliminate the possibility of loopback problems, I also tried 
the addsub -block with the subtract-output statically connected to a null sink.

The test program (addsub.cpp) does not seem to stream anything, and outputs the 
following warnings:

[WARNING] [RFNOC::GRAPH] One or more blocks timed out during flush!
O[WARNING] [RFNOC::GRAPH::DETAIL] Cannot forward action rx_event from 
0/Radio#0:OUTPUT_EDGE:1, no neighbour found!
O[WARNING] [RFNOC::GRAPH::DETAIL] Cannot forward action rx_event from 
0/Radio#0:OUTPUT_EDGE:0, no neighbour found!

Active connections for the addsub-test are:

0/Radio#0:0==>0/DDC#0:0
0/Radio#0:1==>0/DDC#0:1
0/DDC#0:0-->0/AddSub#0:0
0/DDC#0:1-->0/AddSub#0:1
0/AddSub#0:0-->0/DUC#1:0
0/AddSub#0:1==>0/NullSrcSink#0:0
0/DUC#1:0==>0/Radio#1:0

So am I missing something trivial here? I guess there are at least three things 
that could be wrong here:

- How to correctly configure synchronous dual-channel sampling?
- Is it possible to loop back data from Radio0 RX to Radio0 TX through CHDR 
crossbar?
- How to correctly configure and use the addsub-block?

Is it necessary to issue stream commands to both RX radio channels, like so 
(with identical time spec):

rx_radio_ctrl->issue_stream_cmd(stream_cmd, 0);
rx_radio_ctrl->issue_stream_cmd(stream_cmd, 1);

One more thing to note: using Gnuradio UHD/RFNoC interface (major version 3.10) 
to stream 2 channels to host though DDCs and RX streamers works just fine.

Thank you in advance.

With best regards,
Kalle Hanhijärvi

Attachment: x300_dualchannel.yml
Description: x300_dualchannel.yml

$ uhd_usrp_probe 
[INFO] [UHD] linux; GNU C++ version 9.4.0; Boost_107100; DPDK_19.11; 
UHD_4.4.0.0-33-g4a77791c
[INFO] [X300] X300 initialization sequence...
[INFO] [X300] Maximum frame size: 1472 bytes.
[INFO] [X300] Radio 1x clock: 200 MHz
[INFO] [0/FIR#0] Setting default MTU forward policy.
[INFO] [0/FIR#1] Setting default MTU forward policy.
  _____________________________________________________
 /
|       Device: X-Series Device
|     _____________________________________________________
|    /
|   |       Mboard: X300
|   |   revision: 12
|   |   revision_compat: 7
|   |   product: 30817
|   |   mac-addr0: 00:80:2f:37:db:e5
|   |   mac-addr1: 00:80:2f:37:db:e6
|   |   gateway: 192.168.10.1
|   |   ip-addr0: 192.168.10.2
|   |   subnet0: 255.255.255.0
|   |   ip-addr1: 192.168.20.2
|   |   subnet1: 255.255.255.0
|   |   ip-addr2: 192.168.30.2
|   |   subnet2: 255.255.255.0
|   |   ip-addr3: 192.168.40.2
|   |   subnet3: 255.255.255.0
|   |   serial: 328C972
|   |   FW Version: 6.1
|   |   FPGA Version: 39.2
|   |   FPGA git hash: fffffff-dirty
|   |   RFNoC capable: Yes
|   |   
|   |   Time sources:  internal, external, gpsdo
|   |   Clock sources: internal, external, gpsdo
|   |   Sensors: ref_locked
|     _____________________________________________________
|    /
|   |       RFNoC blocks on this device:
|   |   
|   |   * 0/AddSub#0
|   |   * 0/DDC#0
|   |   * 0/DDC#1
|   |   * 0/DUC#0
|   |   * 0/DUC#1
|   |   * 0/FIR#0
|   |   * 0/FIR#1
|   |   * 0/NullSrcSink#0
|   |   * 0/Radio#0
|   |   * 0/Radio#1
|     _____________________________________________________
|    /
|   |       Static connections on this device:
|   |   
|   |   * 0/SEP#3:0==>0/DUC#0:0
|   |   * 0/DUC#0:0==>0/Radio#0:0
|   |   * 0/Radio#0:0==>0/DDC#0:0
|   |   * 0/DDC#0:0==>0/SEP#0:0
|   |   * 0/Radio#0:1==>0/DDC#0:1
|   |   * 0/DDC#0:1==>0/SEP#1:0
|   |   * 0/SEP#2:0==>0/DUC#1:0
|   |   * 0/DUC#1:0==>0/Radio#1:0
|   |   * 0/Radio#1:0==>0/DDC#1:0
|   |   * 0/DDC#1:0==>0/SEP#2:0
|   |   * 0/SEP#4:0==>0/FIR#0:0
|   |   * 0/SEP#5:0==>0/FIR#1:0
|   |   * 0/FIR#0:0==>0/SEP#4:0
|   |   * 0/FIR#1:0==>0/SEP#5:0
|   |   * 0/SEP#6:0==>0/AddSub#0:0
|   |   * 0/SEP#7:0==>0/AddSub#0:1
|   |   * 0/AddSub#0:0==>0/SEP#8:0
|   |   * 0/AddSub#0:1==>0/NullSrcSink#0:0
|     _____________________________________________________
|    /
|   |       TX Dboard: 0/Radio#0
|   |   ID: LF TX (0x630e)
|   |   Serial: 3253F17
|   |     _____________________________________________________
|   |    /
|   |   |       TX Frontend: 0
|   |   |   Name: LFTX (0)
|   |   |   Antennas: AB, BA, A, B
|   |   |   Sensors: 
|   |   |   Freq range: -32.000 to 32.000 MHz
|   |   |   Gain Elements: None
|   |   |   Bandwidth range: 64000000.0 to 64000000.0 step 0.0 Hz
|   |   |   Connection Type: IQ
|   |   |   Uses LO offset: No
|     _____________________________________________________
|    /
|   |       RX Dboard: 0/Radio#0
|   |   ID: LF RX (0x630f)
|   |   Serial: 30DDF1D
|   |     _____________________________________________________
|   |    /
|   |   |       RX Frontend: 0
|   |   |   Name: LFRX (0)
|   |   |   Antennas: AB, BA, A, B
|   |   |   Sensors: 
|   |   |   Freq range: -32.000 to 32.000 MHz
|   |   |   Gain Elements: None
|   |   |   Bandwidth range: 64000000.0 to 64000000.0 step 0.0 Hz
|   |   |   Connection Type: IQ
|   |   |   Uses LO offset: No
|   |     _____________________________________________________
|   |    /
|   |   |       RX Frontend: 1
|   |   |   Name: LFRX (1)
|   |   |   Antennas: AB, BA, A, B
|   |   |   Sensors: 
|   |   |   Freq range: -32.000 to 32.000 MHz
|   |   |   Gain Elements: None
|   |   |   Bandwidth range: 64000000.0 to 64000000.0 step 0.0 Hz
|   |   |   Connection Type: IQ
|   |   |   Uses LO offset: No
|     _____________________________________________________
|    /
|   |       TX Dboard: 0/Radio#1
|   |   ID: LF TX (0x630e)
|   |   Serial: 324FED4
|   |     _____________________________________________________
|   |    /
|   |   |       TX Frontend: 0
|   |   |   Name: LFTX (0)
|   |   |   Antennas: AB, BA, A, B
|   |   |   Sensors: 
|   |   |   Freq range: -32.000 to 32.000 MHz
|   |   |   Gain Elements: None
|   |   |   Bandwidth range: 64000000.0 to 64000000.0 step 0.0 Hz
|   |   |   Connection Type: IQ
|   |   |   Uses LO offset: No
|     _____________________________________________________
|    /
|   |       RX Dboard: 0/Radio#1
|   |     _____________________________________________________
|   |    /
|   |   |       RX Frontend: 0
|   |   |   Name: Unknown (0xffff) - 0
|   |   |   Antennas: 
|   |   |   Sensors: 
|   |   |   Freq range: 0.000 to 0.000 MHz
|   |   |   Gain Elements: None
|   |   |   Bandwidth range: 0.0 to 0.0 step 0.0 Hz
|   |   |   Connection Type: IQ
|   |   |   Uses LO offset: No
#include <uhd/rfnoc/block_id.hpp>
#include <uhd/rfnoc/mb_controller.hpp>
#include <uhd/rfnoc/radio_control.hpp>
#include <uhd/rfnoc_graph.hpp>
#include <uhd/types/tune_request.hpp>
#include <uhd/utils/graph_utils.hpp>
#include <uhd/utils/math.hpp>
#include <uhd/utils/safe_main.hpp>
#include <boost/format.hpp>
#include <chrono>
#include <csignal>
#include <iostream>
#include <thread>

using uhd::rfnoc::radio_control;
using namespace std::chrono_literals;

/****************************************************************************
 * SIGINT handling
 ***************************************************************************/
static bool stop_signal_called = false;
void sig_int_handler(int)
{
    stop_signal_called = true;
}

/****************************************************************************
 * main
 ***************************************************************************/
int UHD_SAFE_MAIN(int argc, char* argv[])
{
    std::string args, rx_ant, tx_ant, rx_blockid, tx_blockid, ref, pps;
    size_t total_num_samps, spp, rx_chan, tx_chan;
    double rate, rx_freq, tx_freq, rx_bw, tx_bw, total_time, setup_time;
    bool rx_timestamps;
    
    rate = 200e6;
    
    spp = 0;
    
    rx_freq = 0.0;
    tx_freq = 0.0;
    
    rx_bw = 32e6;
    tx_bw = 32e6;
    
    rx_chan = 0;
    tx_chan = 0;
    
    total_num_samps = 0;
    total_time = 0.0;
    
    rx_blockid = "0/Radio#0";
    tx_blockid = "0/Radio#1";
    
    // Set timestamps on RX
    rx_timestamps = false;
    
    // Setup time
    setup_time = 0.1;
    
    ref = "internal";
    pps = "internal";
    

    /************************************************************************
     * Create device and block controls
     ***********************************************************************/
    std::cout << std::endl;
    std::cout << boost::format("Creating the RFNoC graph with args: %s...") % 
args
              << std::endl;
    uhd::rfnoc::rfnoc_graph::sptr graph = uhd::rfnoc::rfnoc_graph::make(args);

    // Create handles for radio objects
    uhd::rfnoc::block_id_t rx_radio_ctrl_id(rx_blockid);
    uhd::rfnoc::block_id_t tx_radio_ctrl_id(tx_blockid);
    // This next line will fail if the radio is not actually available
    uhd::rfnoc::radio_control::sptr rx_radio_ctrl =
        graph->get_block<uhd::rfnoc::radio_control>(rx_radio_ctrl_id);
    uhd::rfnoc::radio_control::sptr tx_radio_ctrl =
        graph->get_block<uhd::rfnoc::radio_control>(tx_radio_ctrl_id);
    std::cout << "Using RX radio " << rx_radio_ctrl_id << ", channel " << 
rx_chan
              << std::endl;
    std::cout << "Using TX radio " << tx_radio_ctrl_id << ", channel " << 
tx_chan
              << std::endl;
    size_t rx_mb_idx = rx_radio_ctrl_id.get_device_no();

    /************************************************************************
     * Set up radio
     ***********************************************************************/
    // Only forward properties once per block in the chain. In the case of
    // looping back to a single radio block, skip property propagation after
    // traversing back to the starting point of the chain.
    const bool skip_pp = rx_radio_ctrl_id == tx_radio_ctrl_id;

    std::cout << "skip_pp: " << skip_pp << std::endl;

    // Connect manually
    
    uhd::rfnoc::block_id_t rx_radio_ddc_id("0/DDC#0");
    uhd::rfnoc::block_id_t rx_radio_duc_id("0/DUC#0");
    
    uhd::rfnoc::block_id_t tx_radio_duc_id("0/DUC#1");
    
    uhd::rfnoc::block_id_t addsub_id("0/AddSub#0");
    
    uhd::rfnoc::block_id_t null_id("0/NullSrcSink#0");
    
    // RX radio channels to DDCs
    graph->connect(rx_blockid, 0, rx_radio_ddc_id, 0, false);
    graph->connect(rx_blockid, 1, rx_radio_ddc_id, 1, false);
    
    // DDCs to addsub
    graph->connect(rx_radio_ddc_id, 0, addsub_id, 0, false);
    graph->connect(rx_radio_ddc_id, 1, addsub_id, 1, false);
    
    // Addsub to DUC and null sink
    graph->connect(addsub_id, 0, tx_radio_duc_id, 0, false);
    graph->connect(addsub_id, 1, null_id, 0, false);
    
    // DUCs to radios
    graph->connect(tx_radio_duc_id, 0, tx_radio_ctrl_id, 0, false);
    
    graph->commit();

    std::vector<uhd::rfnoc::graph_edge_t> active_connections = 
graph->enumerate_active_connections();

    std::vector<uhd::rfnoc::graph_edge_t>::iterator iter = 
active_connections.begin();

    // Print active connections
    for(iter; iter < active_connections.end(); iter++)
    {
            std::cout << (*iter).to_string() << std::endl;;
    }

    rx_radio_ctrl->enable_rx_timestamps(rx_timestamps, rx_chan);

    // Set time and clock reference
    for (size_t i = 0; i < graph->get_num_mboards(); ++i) {
        graph->get_mb_controller(i)->set_clock_source(ref);
    }
    
    // Lock mboard clocks
    for (size_t i = 0; i < graph->get_num_mboards(); ++i) {
        graph->get_mb_controller(i)->set_time_source(pps);
    }
    
    rate = rx_radio_ctrl->set_rate(rate);

    // set the sample rate
    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate / 1e6)
              << std::endl;
    rate = rx_radio_ctrl->set_rate(rate);
    std::cout << boost::format("Actual RX Rate: %f Msps...") % (rate / 1e6)
              << std::endl
              << std::endl;

    // set the center frequency

    std::cout << boost::format("Setting RX Freq: %f MHz...") % (rx_freq / 1e6)
              << std::endl;
    
    rx_radio_ctrl->set_rx_frequency(rx_freq, rx_chan);
    std::cout << boost::format("Actual RX Freq: %f MHz...")
                     % (rx_radio_ctrl->get_rx_frequency(rx_chan) / 1e6)
              << std::endl
              << std::endl;

    std::cout << boost::format("Setting TX Freq: %f MHz...") % (tx_freq / 1e6)
              << std::endl;

    tx_radio_ctrl->set_tx_frequency(tx_freq, tx_chan);
    std::cout << boost::format("Actual TX Freq: %f MHz...")
                     % (tx_radio_ctrl->get_tx_frequency(tx_chan) / 1e6)
              << std::endl
              << std::endl;

    std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % (rx_bw / 
1e6)
              << std::endl;
    rx_radio_ctrl->set_rx_bandwidth(rx_bw, rx_chan);
    std::cout << boost::format("Actual RX Bandwidth: %f MHz...")
                     % (rx_radio_ctrl->get_rx_bandwidth(rx_chan) / 1e6)
              << std::endl
              << std::endl;

    std::cout << boost::format("Setting TX Bandwidth: %f MHz...") % (tx_bw / 
1e6)
              << std::endl;
    tx_radio_ctrl->set_tx_bandwidth(tx_bw, tx_chan);
    std::cout << boost::format("Actual TX Bandwidth: %f MHz...")
                     % (tx_radio_ctrl->get_tx_bandwidth(tx_chan) / 1e6)
              << std::endl
              << std::endl;

    // Antennas

    rx_radio_ctrl->set_rx_antenna("A", 0);
    rx_radio_ctrl->set_rx_antenna("B", 1);
    
    tx_radio_ctrl->set_tx_antenna("A", 0);    
    rx_radio_ctrl->set_tx_antenna("A", 0);


    std::cout << "Setting samples per packet to: " << spp << std::endl;
    rx_radio_ctrl->set_property<int>("spp", spp, 0);
    spp = rx_radio_ctrl->get_property<int>("spp", 0);
    std::cout << "Actual samples per packet = " << spp << std::endl;


    // Allow for some setup time
    std::this_thread::sleep_for(1s * setup_time);

    // Arm SIGINT handler
    std::signal(SIGINT, &sig_int_handler);

    // Calculate timeout and set timers
    // We just need to check is nsamps was set, otherwise we'll use the duration
    if (total_num_samps > 0) {
        total_time = total_num_samps / rate;
        std::cout << boost::format("Expected streaming time: %.3f") % total_time
                  << std::endl;
    }

    // Start streaming
    uhd::stream_cmd_t 
stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
    stream_cmd.num_samps  = size_t(total_num_samps);
    stream_cmd.stream_now = false;
    stream_cmd.time_spec =
        
graph->get_mb_controller(rx_mb_idx)->get_timekeeper(rx_mb_idx)->get_time_now()
        + setup_time;
    std::cout << "Issuing start stream cmd..." << std::endl;
    
    // Are two stream commands required for 2 channel RX?
    rx_radio_ctrl->issue_stream_cmd(stream_cmd, 0);
    rx_radio_ctrl->issue_stream_cmd(stream_cmd, 1);
    
    std::this_thread::sleep_for(1s * setup_time);
    
    std::cout << "Wait..." << std::endl;

    // Wait until we can exit
    uhd::time_spec_t elapsed_time = 0.0;
    while (not stop_signal_called) {
        std::this_thread::sleep_for(100ms);
        if (total_time > 0.0) {
            elapsed_time += 0.1;
            if (elapsed_time > total_time) {
                break;
            }
        }
    }

    // Stop radio
    stream_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
    std::cout << "Issuing stop stream cmd..." << std::endl;
    rx_radio_ctrl->issue_stream_cmd(stream_cmd, rx_chan);
    std::cout << "Done" << std::endl;
    // Allow for the samples and ACKs to propagate
    std::this_thread::sleep_for(100ms);

    return EXIT_SUCCESS;
}
#include <uhd/rfnoc/block_id.hpp>
#include <uhd/rfnoc/mb_controller.hpp>
#include <uhd/rfnoc/radio_control.hpp>
#include <uhd/rfnoc_graph.hpp>
#include <uhd/types/tune_request.hpp>
#include <uhd/utils/graph_utils.hpp>
#include <uhd/utils/math.hpp>
#include <uhd/utils/safe_main.hpp>
#include <boost/format.hpp>
#include <chrono>
#include <csignal>
#include <iostream>
#include <thread>

using uhd::rfnoc::radio_control;
using namespace std::chrono_literals;

/****************************************************************************
 * SIGINT handling
 ***************************************************************************/
static bool stop_signal_called = false;
void sig_int_handler(int)
{
    stop_signal_called = true;
}

/****************************************************************************
 * main
 ***************************************************************************/
int UHD_SAFE_MAIN(int argc, char* argv[])
{
    std::string args, rx_ant, tx_ant, rx_blockid, tx_blockid, ref, pps;
    size_t total_num_samps, spp, rx_chan, tx_chan;
    double rate, rx_freq, tx_freq, rx_bw, tx_bw, total_time, setup_time;
    bool rx_timestamps;
    
    rate = 200e6;
    
    spp = 0;
    
    rx_freq = 0.0;
    tx_freq = 0.0;
    
    rx_bw = 32e6;
    tx_bw = 32e6;
    
    rx_chan = 0;
    tx_chan = 0;
    
    total_num_samps = 0;
    total_time = 0.0;
    
    rx_blockid = "0/Radio#0";
    tx_blockid = "0/Radio#1";
    
    // Set timestamps on RX
    rx_timestamps = false;
    
    // Setup time
    setup_time = 0.1;
    
    ref = "internal";
    pps = "internal";
    

    /************************************************************************
     * Create device and block controls
     ***********************************************************************/
    std::cout << std::endl;
    std::cout << boost::format("Creating the RFNoC graph with args: %s...") % 
args
              << std::endl;
    uhd::rfnoc::rfnoc_graph::sptr graph = uhd::rfnoc::rfnoc_graph::make(args);

    // Create handles for radio objects
    uhd::rfnoc::block_id_t rx_radio_ctrl_id(rx_blockid);
    uhd::rfnoc::block_id_t tx_radio_ctrl_id(tx_blockid);
    // This next line will fail if the radio is not actually available
    uhd::rfnoc::radio_control::sptr rx_radio_ctrl =
        graph->get_block<uhd::rfnoc::radio_control>(rx_radio_ctrl_id);
    uhd::rfnoc::radio_control::sptr tx_radio_ctrl =
        graph->get_block<uhd::rfnoc::radio_control>(tx_radio_ctrl_id);
    std::cout << "Using RX radio " << rx_radio_ctrl_id << ", channel " << 
rx_chan
              << std::endl;
    std::cout << "Using TX radio " << tx_radio_ctrl_id << ", channel " << 
tx_chan
              << std::endl;
    size_t rx_mb_idx = rx_radio_ctrl_id.get_device_no();

    /************************************************************************
     * Set up radio
     ***********************************************************************/
    // Only forward properties once per block in the chain. In the case of
    // looping back to a single radio block, skip property propagation after
    // traversing back to the starting point of the chain.
    const bool skip_pp = rx_radio_ctrl_id == tx_radio_ctrl_id;

    std::cout << "skip_pp: " << skip_pp << std::endl;

    // Connect manually
    
    uhd::rfnoc::block_id_t rx_radio_ddc_id("0/DDC#0");
    uhd::rfnoc::block_id_t rx_radio_duc_id("0/DUC#0");
    
    uhd::rfnoc::block_id_t tx_radio_duc_id("0/DUC#1");
    
    // RX radio channels to DDCs
    graph->connect(rx_blockid, 0, rx_radio_ddc_id, 0, false);
    graph->connect(rx_blockid, 1, rx_radio_ddc_id, 1, false);
    
    // DDCs to DUCs
    graph->connect(rx_radio_ddc_id, 0, tx_radio_duc_id, 0, false);
    graph->connect(rx_radio_ddc_id, 1, rx_radio_duc_id, 0, false);
    
    // DUCs to radios
    graph->connect(tx_radio_duc_id, 0, tx_radio_ctrl_id, 0, false);
    graph->connect(rx_radio_duc_id, 0, rx_radio_ctrl_id, 0, true);

    graph->commit();

    std::vector<uhd::rfnoc::graph_edge_t> active_connections = 
graph->enumerate_active_connections();

    std::vector<uhd::rfnoc::graph_edge_t>::iterator iter = 
active_connections.begin();

    // Print active connections
    for(iter; iter < active_connections.end(); iter++)
    {
            std::cout << (*iter).to_string() << std::endl;;
    }

    rx_radio_ctrl->enable_rx_timestamps(rx_timestamps, rx_chan);

    // Set time and clock reference
    for (size_t i = 0; i < graph->get_num_mboards(); ++i) {
        graph->get_mb_controller(i)->set_clock_source(ref);
    }
    
    // Lock mboard clocks
    for (size_t i = 0; i < graph->get_num_mboards(); ++i) {
        graph->get_mb_controller(i)->set_time_source(pps);
    }
    
    rate = rx_radio_ctrl->set_rate(rate);

    // set the sample rate
    std::cout << boost::format("Setting RX Rate: %f Msps...") % (rate / 1e6)
              << std::endl;
    rate = rx_radio_ctrl->set_rate(rate);
    std::cout << boost::format("Actual RX Rate: %f Msps...") % (rate / 1e6)
              << std::endl
              << std::endl;

    // set the center frequency

    std::cout << boost::format("Setting RX Freq: %f MHz...") % (rx_freq / 1e6)
              << std::endl;
    
    rx_radio_ctrl->set_rx_frequency(rx_freq, rx_chan);
    std::cout << boost::format("Actual RX Freq: %f MHz...")
                     % (rx_radio_ctrl->get_rx_frequency(rx_chan) / 1e6)
              << std::endl
              << std::endl;

    std::cout << boost::format("Setting TX Freq: %f MHz...") % (tx_freq / 1e6)
              << std::endl;

    tx_radio_ctrl->set_tx_frequency(tx_freq, tx_chan);
    std::cout << boost::format("Actual TX Freq: %f MHz...")
                     % (tx_radio_ctrl->get_tx_frequency(tx_chan) / 1e6)
              << std::endl
              << std::endl;

    std::cout << boost::format("Setting RX Bandwidth: %f MHz...") % (rx_bw / 
1e6)
              << std::endl;
    rx_radio_ctrl->set_rx_bandwidth(rx_bw, rx_chan);
    std::cout << boost::format("Actual RX Bandwidth: %f MHz...")
                     % (rx_radio_ctrl->get_rx_bandwidth(rx_chan) / 1e6)
              << std::endl
              << std::endl;

    std::cout << boost::format("Setting TX Bandwidth: %f MHz...") % (tx_bw / 
1e6)
              << std::endl;
    tx_radio_ctrl->set_tx_bandwidth(tx_bw, tx_chan);
    std::cout << boost::format("Actual TX Bandwidth: %f MHz...")
                     % (tx_radio_ctrl->get_tx_bandwidth(tx_chan) / 1e6)
              << std::endl
              << std::endl;

    // Antennas

    rx_radio_ctrl->set_rx_antenna("A", 0);
    rx_radio_ctrl->set_rx_antenna("B", 1);
    
    tx_radio_ctrl->set_tx_antenna("A", 0);    
    rx_radio_ctrl->set_tx_antenna("A", 0);


    std::cout << "Setting samples per packet to: " << spp << std::endl;
    rx_radio_ctrl->set_property<int>("spp", spp, 0);
    spp = rx_radio_ctrl->get_property<int>("spp", 0);
    std::cout << "Actual samples per packet = " << spp << std::endl;


    // Allow for some setup time
    std::this_thread::sleep_for(1s * setup_time);

    // Arm SIGINT handler
    std::signal(SIGINT, &sig_int_handler);

    // Calculate timeout and set timers
    // We just need to check is nsamps was set, otherwise we'll use the duration
    if (total_num_samps > 0) {
        total_time = total_num_samps / rate;
        std::cout << boost::format("Expected streaming time: %.3f") % total_time
                  << std::endl;
    }

    // Start streaming
    uhd::stream_cmd_t 
stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
    stream_cmd.num_samps  = size_t(total_num_samps);
    stream_cmd.stream_now = false;
    stream_cmd.time_spec =
        
graph->get_mb_controller(rx_mb_idx)->get_timekeeper(rx_mb_idx)->get_time_now()
        + setup_time;
    std::cout << "Issuing start stream cmd..." << std::endl;
    
    // Are two stream commands required for 2 channel RX?
    rx_radio_ctrl->issue_stream_cmd(stream_cmd, 0);
    rx_radio_ctrl->issue_stream_cmd(stream_cmd, 1);
    
    std::this_thread::sleep_for(1s * setup_time);
    
    std::cout << "Wait..." << std::endl;

    // Wait until we can exit
    uhd::time_spec_t elapsed_time = 0.0;
    while (not stop_signal_called) {
        std::this_thread::sleep_for(100ms);
        if (total_time > 0.0) {
            elapsed_time += 0.1;
            if (elapsed_time > total_time) {
                break;
            }
        }
    }

    // Stop radio
    stream_cmd.stream_mode = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
    std::cout << "Issuing stop stream cmd..." << std::endl;
    rx_radio_ctrl->issue_stream_cmd(stream_cmd, rx_chan);
    std::cout << "Done" << std::endl;
    // Allow for the samples and ACKs to propagate
    std::this_thread::sleep_for(100ms);

    return EXIT_SUCCESS;
}
_______________________________________________
USRP-users mailing list -- usrp-users@lists.ettus.com
To unsubscribe send an email to usrp-users-le...@lists.ettus.com

Reply via email to