1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
|
//
// 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/device3.hpp>
#include <uhd/rfnoc/radio_ctrl.hpp>
#include <uhd/utils/math.hpp>
#include <uhd/utils/safe_main.hpp>
#include <boost/program_options.hpp>
#include <chrono>
#include <csignal>
#include <iostream>
#include <thread>
namespace po = boost::program_options;
using uhd::rfnoc::radio_ctrl;
/****************************************************************************
* 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_args, tx_args, rx_ant, tx_ant, rx_blockid, tx_blockid, ref;
size_t total_num_samps, spp, rx_chan, tx_chan, tx_delay;
double rate, rx_freq, tx_freq, rx_gain, tx_gain, 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")
("rx_args", po::value<std::string>(&rx_args)->default_value(""), "Block args for the receive radio")
("tx_args", po::value<std::string>(&tx_args)->default_value(""), "Block args for the transmit radio")
("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-timestamps", po::value<bool>(&rx_timestamps)->default_value(false), "Set timestamps on RX")
("tx-delay", po::value<size_t>(&tx_delay)->default_value(10), "Ticks delay")
("setup", po::value<double>(&setup_time)->default_value(1.0), "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")
("bw", po::value<double>(&bw), "Analog frontend filter bandwidth in Hz (Rx and Tx)")
("ref", po::value<std::string>(&ref)->default_value("internal"), "reference source (internal, external, mimo)")
;
// 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 a device session
std::cout << std::endl;
std::cout << boost::format("Creating the usrp device with: %s...") % args
<< std::endl;
auto dev = boost::dynamic_pointer_cast<uhd::device3>(uhd::device::make(args));
if (not dev) {
std::cout << "Error: Could not find an RFNoC-compatible device." << std::endl;
return EXIT_FAILURE;
}
// Access block controllers
if (not dev->has_block<uhd::rfnoc::radio_ctrl>(rx_blockid)
or not dev->has_block<uhd::rfnoc::radio_ctrl>(tx_blockid)) {
std::cout << "Error: Could not access at least one of these blocks:\n"
<< "- " << rx_blockid << "- " << tx_blockid << std::endl;
std::cout << "Please confirm these blocks are actually available on the current "
"loaded device."
<< std::endl;
return EXIT_FAILURE;
}
auto rx_radio_ctrl = dev->get_block_ctrl<radio_ctrl>(rx_blockid);
auto tx_radio_ctrl = dev->get_block_ctrl<radio_ctrl>(tx_blockid);
// Configure Rx radio
std::cout << "Configuring Rx radio..." << std::endl;
rx_radio_ctrl->set_args(rx_args);
if (spp) {
rx_radio_ctrl->set_arg<int>("spp", spp, rx_chan);
}
std::cout << "Setting Rx rate: " << (rate / 1e6) << " Msps" << std::endl;
double actual_rx_rate = rx_radio_ctrl->set_rate(rate);
std::cout << "Actual Rx rate: " << (actual_rx_rate / 1e6) << " Msps" << std::endl;
std::cout << "Setting Rx frequency: " << (rx_freq / 1e6) << " MHz." << std::endl;
std::cout << "Actual Rx frequency: "
<< (rx_radio_ctrl->set_rx_frequency(rx_freq, rx_chan) / 1e6) << " MHz."
<< std::endl;
if (rx_gain) {
std::cout << "Setting Rx gain: " << (rx_gain) << " dB." << std::endl;
std::cout << "Actual Rx gain: " << (rx_radio_ctrl->set_rx_gain(rx_gain, rx_chan))
<< " dB." << std::endl;
}
if (not rx_ant.empty()) {
std::cout << "Setting Rx antenna: " << (rx_ant) << "." << std::endl;
rx_radio_ctrl->set_rx_antenna(rx_ant, rx_chan);
std::cout << "Actual Rx antenna: " << rx_radio_ctrl->get_rx_antenna(rx_chan)
<< "." << std::endl;
}
if (!rx_timestamps) {
std::cout << "Disabling timestamps on RX... (direct loopback, may underrun)"
<< std::endl;
}
rx_radio_ctrl->enable_rx_timestamps(rx_timestamps, 0);
// Configure Tx radio
std::cout << "Configuring Tx radio..." << std::endl;
tx_radio_ctrl->set_args(tx_args);
std::cout << "Setting Tx rate: " << (rate / 1e6) << " Msps" << std::endl;
double actual_tx_rate = tx_radio_ctrl->set_rate(rate);
std::cout << "Actual Tx rate: " << (actual_tx_rate / 1e6) << " Msps" << std::endl;
std::cout << "Setting Tx frequency: " << (tx_freq / 1e6) << " MHz." << std::endl;
std::cout << "Actual Tx frequency: "
<< (tx_radio_ctrl->set_tx_frequency(tx_freq, tx_chan) / 1e6) << " MHz."
<< std::endl;
if (tx_gain) {
std::cout << "Setting Tx gain: " << (tx_gain) << " dB." << std::endl;
std::cout << "Actual Tx gain: " << (tx_radio_ctrl->set_tx_gain(tx_gain, tx_chan))
<< " dB." << std::endl;
}
if (not tx_ant.empty()) {
std::cout << "Setting Tx antenna: " << (tx_ant) << "." << std::endl;
tx_radio_ctrl->set_tx_antenna(tx_ant, tx_chan);
std::cout << "Actual Tx antenna: " << tx_radio_ctrl->get_tx_antenna(tx_chan)
<< "." << std::endl;
}
// Compare rates
if (not uhd::math::frequencies_are_equal(actual_rx_rate, actual_tx_rate)) {
std::cout
<< "Error: Failed to set receive and transmit radios to same sampling rate!"
<< std::endl;
return EXIT_FAILURE;
}
// Create graph and connect blocks
uhd::rfnoc::graph::sptr graph = dev->create_graph("radio_loopback");
std::cout << "Connecting radios..." << std::endl;
try {
graph->connect(rx_blockid, rx_chan, tx_blockid, tx_chan);
} catch (const uhd::runtime_error& ex) {
std::cout << "Error connecting blocks: " << std::endl;
std::cout << ex.what() << std::endl;
return EXIT_FAILURE;
}
tx_radio_ctrl->set_tx_streamer(true, tx_chan);
rx_radio_ctrl->set_rx_streamer(true, rx_chan);
// Allow for some setup time
std::this_thread::sleep_for(std::chrono::milliseconds(int64_t(setup_time * 1000)));
// Arm SIGINT handler
std::signal(SIGINT, &sig_int_handler);
// Calculate timeout and set timers
if (total_time == 0 and total_num_samps > 0) {
const double buffer_time = 1.0; // seconds
total_time = (1.0 / rate) * total_num_samps + buffer_time;
}
// 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 = true;
stream_cmd.time_spec = uhd::time_spec_t();
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
while (not stop_signal_called) {
std::this_thread::sleep_for(std::chrono::milliseconds(100));
// FIXME honour --duration
}
// 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);
std::cout << "Done" << std::endl;
return EXIT_SUCCESS;
}
|