mirror of
https://github.com/saymrwulf/uhd.git
synced 2026-05-14 20:58:09 +00:00
291 lines
12 KiB
C++
291 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
|
|
***********************************************************************/
|
|
// 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);
|
|
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;
|
|
}
|