mirror of
https://github.com/saymrwulf/uhd.git
synced 2026-05-16 21:10:10 +00:00
Utilize the skip_property_propagation option in the case of looping back to a single radio block. Looping back to the same block causes a cycle in the graph, which is not supported with property propagation. To work around this, remove the forward property edge on the radio connection, which breaks the cycle. The properties will then propagate forward once through each block in the chain and then back. Signed-off-by: mattprost <matt.prost@ni.com>
295 lines
12 KiB
C++
295 lines
12 KiB
C++
//
|
|
// Copyright 2016 Ettus Research LLC
|
|
// Copyright 2018 Ettus Research, a National Instruments Company
|
|
//
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
//
|
|
|
|
// Example UHD/RFNoC application: Connect an rx radio to a tx radio and
|
|
// run a loopback.
|
|
|
|
#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 <boost/program_options.hpp>
|
|
#include <chrono>
|
|
#include <csignal>
|
|
#include <iostream>
|
|
#include <thread>
|
|
|
|
namespace po = boost::program_options;
|
|
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[])
|
|
{
|
|
// variables to be set by po
|
|
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_gain, tx_gain, rx_bw, tx_bw, total_time, setup_time;
|
|
bool rx_timestamps;
|
|
|
|
// setup the program options
|
|
po::options_description desc("Allowed options");
|
|
// clang-format off
|
|
desc.add_options()
|
|
("help", "help message")
|
|
("args", po::value<std::string>(&args)->default_value(""), "UHD device address args")
|
|
("spp", po::value<size_t>(&spp)->default_value(0), "Samples per packet (reduce for lower latency)")
|
|
("rx-freq", po::value<double>(&rx_freq)->default_value(0.0), "Rx RF center frequency in Hz")
|
|
("tx-freq", po::value<double>(&tx_freq)->default_value(0.0), "Tx RF center frequency in Hz")
|
|
("rx-gain", po::value<double>(&rx_gain)->default_value(0.0), "Rx RF center gain in Hz")
|
|
("tx-gain", po::value<double>(&tx_gain)->default_value(0.0), "Tx RF center gain in Hz")
|
|
("rx-ant", po::value<std::string>(&rx_ant), "Receive antenna selection")
|
|
("tx-ant", po::value<std::string>(&tx_ant), "Transmit antenna selection")
|
|
("rx-blockid", po::value<std::string>(&rx_blockid)->default_value("0/Radio#0"), "Receive radio block ID")
|
|
("tx-blockid", po::value<std::string>(&tx_blockid)->default_value("0/Radio#1"), "Transmit radio block ID")
|
|
("rx-chan", po::value<size_t>(&rx_chan)->default_value(0), "Channel index on receive radio")
|
|
("tx-chan", po::value<size_t>(&tx_chan)->default_value(0), "Channel index on transmit radio")
|
|
("rx-bw", po::value<double>(&rx_bw), "RX analog frontend filter bandwidth in Hz")
|
|
("tx-bw", po::value<double>(&tx_bw), "TX analog frontend filter bandwidth in Hz")
|
|
("rx-timestamps", po::value<bool>(&rx_timestamps)->default_value(false), "Set timestamps on RX")
|
|
("setup", po::value<double>(&setup_time)->default_value(0.1), "seconds of setup time")
|
|
("nsamps", po::value<size_t>(&total_num_samps)->default_value(0), "total number of samples to receive")
|
|
("rate", po::value<double>(&rate)->default_value(0.0), "Sampling rate")
|
|
("duration", po::value<double>(&total_time)->default_value(0), "total number of seconds to receive")
|
|
("int-n", "Tune USRP with integer-N tuning")
|
|
("ref", po::value<std::string>(&ref)->default_value("internal"), "clock reference (internal, external, mimo, gpsdo)")
|
|
("pps", po::value<std::string>(&pps)->default_value("internal"), "PPS source (internal, external, mimo, gpsdo)")
|
|
;
|
|
// clang-format on
|
|
po::variables_map vm;
|
|
po::store(po::parse_command_line(argc, argv, desc), vm);
|
|
po::notify(vm);
|
|
|
|
// print the help message
|
|
if (vm.count("help")) {
|
|
std::cout << boost::format("RFNoC: Radio loopback test %s") % desc << std::endl;
|
|
std::cout
|
|
<< std::endl
|
|
<< "This application streams data from one radio to another using RFNoC.\n"
|
|
<< std::endl;
|
|
return ~0;
|
|
}
|
|
|
|
/************************************************************************
|
|
* 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;
|
|
// Connect the RX radio to the TX radio
|
|
uhd::rfnoc::connect_through_blocks(
|
|
graph, rx_radio_ctrl_id, rx_chan, tx_radio_ctrl_id, tx_chan, skip_pp);
|
|
graph->commit();
|
|
|
|
rx_radio_ctrl->enable_rx_timestamps(rx_timestamps, rx_chan);
|
|
|
|
// Set time and clock reference
|
|
if (vm.count("ref")) {
|
|
// Lock mboard clocks
|
|
for (size_t i = 0; i < graph->get_num_mboards(); ++i) {
|
|
graph->get_mb_controller(i)->set_clock_source(ref);
|
|
}
|
|
}
|
|
if (vm.count("pps")) {
|
|
// Lock mboard clocks
|
|
for (size_t i = 0; i < graph->get_num_mboards(); ++i) {
|
|
graph->get_mb_controller(i)->set_time_source(pps);
|
|
}
|
|
}
|
|
|
|
// set the sample rate
|
|
if (rate <= 0.0) {
|
|
rate = rx_radio_ctrl->get_rate();
|
|
} else {
|
|
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
|
|
if (vm.count("rx-freq")) {
|
|
std::cout << boost::format("Setting RX Freq: %f MHz...") % (rx_freq / 1e6)
|
|
<< std::endl;
|
|
uhd::tune_request_t tune_request(rx_freq);
|
|
if (vm.count("int-n")) {
|
|
tune_request.args = uhd::device_addr_t("mode_n=integer");
|
|
}
|
|
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;
|
|
}
|
|
if (vm.count("tx-freq")) {
|
|
std::cout << boost::format("Setting TX Freq: %f MHz...") % (tx_freq / 1e6)
|
|
<< std::endl;
|
|
uhd::tune_request_t tune_request(tx_freq);
|
|
if (vm.count("int-n")) {
|
|
tune_request.args = uhd::device_addr_t("mode_n=integer");
|
|
}
|
|
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;
|
|
}
|
|
|
|
// set the rf gain
|
|
if (vm.count("rx-gain")) {
|
|
std::cout << boost::format("Setting RX Gain: %f dB...") % rx_gain << std::endl;
|
|
rx_radio_ctrl->set_rx_gain(rx_gain, rx_chan);
|
|
std::cout << boost::format("Actual RX Gain: %f dB...")
|
|
% rx_radio_ctrl->get_rx_gain(rx_chan)
|
|
<< std::endl
|
|
<< std::endl;
|
|
}
|
|
if (vm.count("tx-gain")) {
|
|
std::cout << boost::format("Setting TX Gain: %f dB...") % tx_gain << std::endl;
|
|
tx_radio_ctrl->set_tx_gain(tx_gain, tx_chan);
|
|
std::cout << boost::format("Actual TX Gain: %f dB...")
|
|
% tx_radio_ctrl->get_tx_gain(tx_chan)
|
|
<< std::endl
|
|
<< std::endl;
|
|
}
|
|
|
|
// set the IF filter bandwidth
|
|
if (vm.count("rx-bw")) {
|
|
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;
|
|
}
|
|
if (vm.count("tx-bw")) {
|
|
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;
|
|
}
|
|
|
|
// set the antennas
|
|
if (vm.count("rx-ant")) {
|
|
rx_radio_ctrl->set_rx_antenna(rx_ant, rx_chan);
|
|
}
|
|
if (vm.count("tx-ant")) {
|
|
tx_radio_ctrl->set_tx_antenna(tx_ant, tx_chan);
|
|
}
|
|
|
|
// check Ref and LO Lock detect
|
|
if (not vm.count("skip-lo")) {
|
|
// TODO
|
|
// check_locked_sensor(usrp->get_rx_sensor_names(0), "lo_locked",
|
|
// boost::bind(&uhd::usrp::multi_usrp::get_rx_sensor, usrp, _1, radio_id),
|
|
// setup_time); if (ref == "external")
|
|
// check_locked_sensor(usrp->get_mboard_sensor_names(0), "ref_locked",
|
|
// boost::bind(&uhd::usrp::multi_usrp::get_mboard_sensor, usrp, _1, radio_id),
|
|
// setup_time);
|
|
}
|
|
|
|
if (vm.count("spp")) {
|
|
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((total_num_samps == 0)
|
|
? uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS
|
|
: uhd::stream_cmd_t::STREAM_MODE_NUM_SAMPS_AND_DONE);
|
|
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;
|
|
rx_radio_ctrl->issue_stream_cmd(stream_cmd, rx_chan);
|
|
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;
|
|
}
|