1//
2// Copyright 2011-2012,2014 Ettus Research LLC
3// Copyright 2018 Ettus Research, a National Instruments Company
4//
5// SPDX-License-Identifier: GPL-3.0-or-later
6//
7
8#include <uhd/types/tune_request.hpp>
9#include <uhd/usrp/multi_usrp.hpp>
10#include <uhd/utils/safe_main.hpp>
11#include <uhd/utils/thread.hpp>
12#include <boost/format.hpp>
13#include <boost/program_options.hpp>
14#include <chrono>
15#include <complex>
16#include <csignal>
17#include <fstream>
18#include <iostream>
19#include <thread>
20
21namespace po = boost::program_options;
22
23static bool stop_signal_called = false;
24void sig_int_handler(int)
25{
26 stop_signal_called = true;
27}
28
29template <typename samp_type>
30void send_from_file(
31 uhd::tx_streamer::sptr tx_stream, const std::string& file, size_t samps_per_buff)
32{
33 uhd::tx_metadata_t md;
34 md.start_of_burst = false;
35 md.end_of_burst = false;
36 std::vector<samp_type> buff(samps_per_buff);
37 std::ifstream infile(file.c_str(), std::ifstream::binary);
38
39 // loop until the entire file has been read
40
41 while (not md.end_of_burst and not stop_signal_called) {
42 infile.read((char*)&buff.front(), buff.size() * sizeof(samp_type));
43 size_t num_tx_samps = size_t(infile.gcount() / sizeof(samp_type));
44
45 md.end_of_burst = infile.eof();
46
47 const size_t samples_sent = tx_stream->send(&buff.front(), num_tx_samps, md);
48 if (samples_sent != num_tx_samps) {
49 UHD_LOG_ERROR("TX-STREAM",
50 "The tx_stream timed out sending " << num_tx_samps << " samples ("
51 << samples_sent << " sent).");
52 return;
53 }
54 }
55
56 infile.close();
57}
58
59int UHD_SAFE_MAIN(int argc, char* argv[])
60{
61 // variables to be set by po
62 std::string args, file, type, ant, subdev, ref, wirefmt, channel;
63 size_t spb;
64 double rate, freq, gain, bw, delay, lo_offset;
65
66 // setup the program options
67 po::options_description desc("Allowed options");
68 // clang-format off
69 desc.add_options()
70 ("help", "help message")
71 ("args", po::value<std::string>(&args)->default_value(""), "multi uhd device address args")
72 ("file", po::value<std::string>(&file)->default_value("usrp_samples.dat"), "name of the file to read binary samples from")
73 ("type", po::value<std::string>(&type)->default_value("short"), "sample type: double, float, or short")
74 ("spb", po::value<size_t>(&spb)->default_value(10000), "samples per buffer")
75 ("rate", po::value<double>(&rate), "rate of outgoing samples")
76 ("freq", po::value<double>(&freq), "RF center frequency in Hz")
77 ("lo-offset", po::value<double>(&lo_offset)->default_value(0.0),
78 "Offset for frontend LO in Hz (optional)")
79 ("gain", po::value<double>(&gain), "gain for the RF chain")
80 ("ant", po::value<std::string>(&ant), "antenna selection")
81 ("subdev", po::value<std::string>(&subdev), "subdevice specification")
82 ("bw", po::value<double>(&bw), "analog frontend filter bandwidth in Hz")
83 ("ref", po::value<std::string>(&ref)->default_value("internal"), "reference source (internal, external, mimo)")
84 ("wirefmt", po::value<std::string>(&wirefmt)->default_value("sc16"), "wire format (sc8 or sc16)")
85 ("delay", po::value<double>(&delay)->default_value(0.0), "specify a delay between repeated transmission of file (in seconds)")
86 ("channel", po::value<std::string>(&channel)->default_value("0"), "which channel to use")
87 ("repeat", "repeatedly transmit file")
88 ("int-n", "tune USRP with integer-n tuning")
89 ;
90 // clang-format on
91 po::variables_map vm;
92 po::store(po::parse_command_line(argc, argv, desc), vm);
93 po::notify(vm);
94
95 // print the help message
96 if (vm.count("help")) {
97 std::cout << boost::format("UHD TX samples from file %s") % desc << std::endl;
98 return ~0;
99 }
100
101 bool repeat = vm.count("repeat") > 0;
102
103 // create a usrp device
104 std::cout << std::endl;
105 std::cout << boost::format("Creating the usrp device with: %s...") % args
106 << std::endl;
107 uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(args);
108
109 // Lock mboard clocks
110 if (vm.count("ref")) {
111 usrp->set_clock_source(ref);
112 }
113
114 // always select the subdevice first, the channel mapping affects the other settings
115 if (vm.count("subdev"))
116 usrp->set_tx_subdev_spec(subdev);
117
118 std::cout << boost::format("Using Device: %s") % usrp->get_pp_string() << std::endl;
119
120 // set the sample rate
121 if (not vm.count("rate")) {
122 std::cerr << "Please specify the sample rate with --rate" << std::endl;
123 return ~0;
124 }
125 std::cout << boost::format("Setting TX Rate: %f Msps...") % (rate / 1e6) << std::endl;
126 usrp->set_tx_rate(rate);
127 std::cout << boost::format("Actual TX Rate: %f Msps...") % (usrp->get_tx_rate() / 1e6)
128 << std::endl
129 << std::endl;
130
131 // set the center frequency
132 if (not vm.count("freq")) {
133 std::cerr << "Please specify the center frequency with --freq" << std::endl;
134 return ~0;
135 }
136 std::cout << boost::format("Setting TX Freq: %f MHz...") % (freq / 1e6) << std::endl;
137 std::cout << boost::format("Setting TX LO Offset: %f MHz...") % (lo_offset / 1e6)
138 << std::endl;
139 uhd::tune_request_t tune_request;
140 tune_request = uhd::tune_request_t(freq, lo_offset);
141 if (vm.count("int-n"))
142 tune_request.args = uhd::device_addr_t("mode_n=integer");
143 usrp->set_tx_freq(tune_request);
144 std::cout << boost::format("Actual TX Freq: %f MHz...") % (usrp->get_tx_freq() / 1e6)
145 << std::endl
146 << std::endl;
147
148 // set the rf gain
149 if (vm.count("gain")) {
150 std::cout << boost::format("Setting TX Gain: %f dB...") % gain << std::endl;
151 usrp->set_tx_gain(gain);
152 std::cout << boost::format("Actual TX Gain: %f dB...") % usrp->get_tx_gain()
153 << std::endl
154 << std::endl;
155 }
156
157 // set the analog frontend filter bandwidth
158 if (vm.count("bw")) {
159 std::cout << boost::format("Setting TX Bandwidth: %f MHz...") % (bw / 1e6)
160 << std::endl;
161 usrp->set_tx_bandwidth(bw);
162 std::cout << boost::format("Actual TX Bandwidth: %f MHz...")
163 % (usrp->get_tx_bandwidth() / 1e6)
164 << std::endl
165 << std::endl;
166 }
167
168 // set the antenna
169 if (vm.count("ant"))
170 usrp->set_tx_antenna(ant);
171
172 // allow for some setup time:
173 std::this_thread::sleep_for(std::chrono::seconds(1));
174
175 // Check Ref and LO Lock detect
176 std::vector<std::string> sensor_names;
177 sensor_names = usrp->get_tx_sensor_names(0);
178 if (std::find(sensor_names.begin(), sensor_names.end(), "lo_locked")
179 != sensor_names.end()) {
180 uhd::sensor_value_t lo_locked = usrp->get_tx_sensor("lo_locked", 0);
181 std::cout << boost::format("Checking TX: %s ...") % lo_locked.to_pp_string()
182 << std::endl;
183 UHD_ASSERT_THROW(lo_locked.to_bool());
184 }
185 sensor_names = usrp->get_mboard_sensor_names(0);
186 if ((ref == "mimo")
187 and (std::find(sensor_names.begin(), sensor_names.end(), "mimo_locked")
188 != sensor_names.end())) {
189 uhd::sensor_value_t mimo_locked = usrp->get_mboard_sensor("mimo_locked", 0);
190 std::cout << boost::format("Checking TX: %s ...") % mimo_locked.to_pp_string()
191 << std::endl;
192 UHD_ASSERT_THROW(mimo_locked.to_bool());
193 }
194 if ((ref == "external")
195 and (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked")
196 != sensor_names.end())) {
197 uhd::sensor_value_t ref_locked = usrp->get_mboard_sensor("ref_locked", 0);
198 std::cout << boost::format("Checking TX: %s ...") % ref_locked.to_pp_string()
199 << std::endl;
200 UHD_ASSERT_THROW(ref_locked.to_bool());
201 }
202
203 // set sigint if user wants to receive
204 if (repeat) {
205 std::signal(SIGINT, &sig_int_handler);
206 std::cout << "Press Ctrl + C to stop streaming..." << std::endl;
207 }
208
209 // create a transmit streamer
210 std::string cpu_format;
211 std::vector<size_t> channel_nums;
212 if (type == "double")
213 cpu_format = "fc64";
214 else if (type == "float")
215 cpu_format = "fc32";
216 else if (type == "short")
217 cpu_format = "sc16";
218 uhd::stream_args_t stream_args(cpu_format, wirefmt);
219 channel_nums.push_back(boost::lexical_cast<size_t>(channel));
220 stream_args.channels = channel_nums;
221 uhd::tx_streamer::sptr tx_stream = usrp->get_tx_stream(stream_args);
222
223 // send from file
224 do {
225 if (type == "double")
226 send_from_file<std::complex<double>>(tx_stream, file, spb);
227 else if (type == "float")
228 send_from_file<std::complex<float>>(tx_stream, file, spb);
229 else if (type == "short")
230 send_from_file<std::complex<short>>(tx_stream, file, spb);
231 else
232 throw std::runtime_error("Unknown type " + type);
233
234 if (repeat and delay > 0.0) {
235 std::this_thread::sleep_for(std::chrono::milliseconds(int64_t(delay * 1000)));
236 }
237 } while (repeat and not stop_signal_called);
238
239 // finished
240 std::cout << std::endl << "Done!" << std::endl << std::endl;
241
242 return EXIT_SUCCESS;
243}