blob: 6fc0c192ad966c3d7bfe2f50073d40612e16bae3 [file] [log] [blame]
kurtis.heimerl965e7572011-11-26 03:16:54 +00001/*
kurtis.heimerl119ca9c2011-11-26 03:18:26 +00002 * Device support for Ettus Research UHD driver
3 * Written by Thomas Tsou <ttsou@vt.edu>
4 *
5 * Copyright 2010,2011 Free Software Foundation, Inc.
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU Affero General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Affero General Public License for more details.
16 *
17 * You should have received a copy of the GNU Affero General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 * See the COPYING file in the main directory for details.
20 */
kurtis.heimerl965e7572011-11-26 03:16:54 +000021
22#include "radioDevice.h"
23#include "Threads.h"
24#include "Logger.h"
ttsoub371ed52012-01-09 18:11:34 +000025#include <uhd/version.hpp>
kurtis.heimerle380af32011-11-26 03:18:55 +000026#include <uhd/property_tree.hpp>
kurtis.heimerl856bbbe2011-12-15 00:50:16 +000027#include <uhd/usrp/multi_usrp.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000028#include <uhd/utils/thread_priority.hpp>
kurtis.heimerl0803ad92011-11-26 03:18:51 +000029#include <uhd/utils/msg.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000030
kurtis.heimerlce317332011-11-26 03:18:39 +000031#ifdef HAVE_CONFIG_H
32#include "config.h"
33#endif
34
kurtis.heimerlac0ee122011-11-28 06:25:58 +000035#define U1_DEFAULT_CLK_RT 64e6
36#define U2_DEFAULT_CLK_RT 100e6
37
kurtis.heimerl965e7572011-11-26 03:16:54 +000038/*
kurtis.heimerlce317332011-11-26 03:18:39 +000039 master_clk_rt - Master clock frequency - ignored if host resampling is
40 enabled
kurtis.heimerl965e7572011-11-26 03:16:54 +000041
42 rx_smpl_offset - Timing correction in seconds between receive and
43 transmit timestamps. This value corrects for delays on
44 on the RF side of the timestamping point of the device.
kurtis.heimerlbe6abe12011-11-26 03:17:05 +000045 This value is generally empirically measured.
kurtis.heimerl965e7572011-11-26 03:16:54 +000046
kurtis.heimerl7ac54b12011-11-26 03:17:49 +000047 smpl_buf_sz - The receive sample buffer size in bytes.
48
49 tx_ampl - Transmit amplitude must be between 0 and 1.0
kurtis.heimerl965e7572011-11-26 03:16:54 +000050*/
kurtis.heimerl965e7572011-11-26 03:16:54 +000051const double master_clk_rt = 52e6;
kurtis.heimerl965e7572011-11-26 03:16:54 +000052const size_t smpl_buf_sz = (1 << 20);
kurtis.heimerl7ac54b12011-11-26 03:17:49 +000053const float tx_ampl = .3;
kurtis.heimerl965e7572011-11-26 03:16:54 +000054
kurtis.heimerlce317332011-11-26 03:18:39 +000055#ifdef RESAMPLE
56const double rx_smpl_offset = .00005;
57#else
58const double rx_smpl_offset = .0000869;
59#endif
60
kurtis.heimerl965e7572011-11-26 03:16:54 +000061/** Timestamp conversion
62 @param timestamp a UHD or OpenBTS timestamp
63 @param rate sample rate
64 @return the converted timestamp
65*/
66uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
67{
68 double secs = (double) ticks / rate;
69 return uhd::time_spec_t(secs);
70}
71
72TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
73{
kurtis.heimerlc7cb8172011-11-26 03:17:26 +000074 TIMESTAMP ticks = ts.get_full_secs() * rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +000075 return ts.get_tick_count(rate) + ticks;
76}
77
78/*
79 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
80 or UHD style timestamps. Time conversions are handled
81 internally or accessable through the static convert calls.
82*/
83class smpl_buf {
84public:
85 /** Sample buffer constructor
86 @param len number of 32-bit samples the buffer should hold
87 @param rate sample clockrate
88 @param timestamp
89 */
90 smpl_buf(size_t len, double rate);
91 ~smpl_buf();
92
93 /** Query number of samples available for reading
94 @param timestamp time of first sample
95 @return number of available samples or error
96 */
97 ssize_t avail_smpls(TIMESTAMP timestamp) const;
98 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
99
100 /** Read and write
101 @param buf pointer to buffer
102 @param len number of samples desired to read or write
103 @param timestamp time of first stample
104 @return number of actual samples read or written or error
105 */
106 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
107 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
108 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
109 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
110
111 /** Buffer status string
112 @return a formatted string describing internal buffer state
113 */
114 std::string str_status() const;
115
116 /** Formatted error string
117 @param code an error code
118 @return a formatted error string
119 */
120 static std::string str_code(ssize_t code);
121
122 enum err_code {
123 ERROR_TIMESTAMP = -1,
124 ERROR_READ = -2,
125 ERROR_WRITE = -3,
126 ERROR_OVERFLOW = -4
127 };
128
129private:
130 uint32_t *data;
131 size_t buf_len;
132
133 double clk_rt;
134
135 TIMESTAMP time_start;
136 TIMESTAMP time_end;
137
138 size_t data_start;
139 size_t data_end;
140};
141
142/*
143 uhd_device - UHD implementation of the Device interface. Timestamped samples
144 are sent to and received from the device. An intermediate buffer
145 on the receive side collects and aligns packets of samples.
146 Events and errors such as underruns are reported asynchronously
147 by the device and received in a separate thread.
148*/
149class uhd_device : public RadioDevice {
150public:
151 uhd_device(double rate, bool skip_rx);
152 ~uhd_device();
153
ttsouf60dafa2012-10-22 00:07:14 +0000154 bool open(const std::string &args);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000155 bool start();
156 bool stop();
kurtis.heimerl68292102011-11-26 03:17:28 +0000157 void restart(uhd::time_spec_t ts);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000158 void setPriority();
kurtis.heimerle380af32011-11-26 03:18:55 +0000159 enum busType getBus() { return bus; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000160
161 int readSamples(short *buf, int len, bool *overrun,
162 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
163
164 int writeSamples(short *buf, int len, bool *underrun,
165 TIMESTAMP timestamp, bool isControl);
166
167 bool updateAlignment(TIMESTAMP timestamp);
168
169 bool setTxFreq(double wFreq);
170 bool setRxFreq(double wFreq);
171
172 inline TIMESTAMP initialWriteTimestamp() { return 0; }
173 inline TIMESTAMP initialReadTimestamp() { return 0; }
174
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000175 inline double fullScaleInputValue() { return 32000 * tx_ampl; }
176 inline double fullScaleOutputValue() { return 32000; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000177
kurtis.heimerl02d04052011-11-26 03:17:10 +0000178 double setRxGain(double db);
179 double getRxGain(void) { return rx_gain; }
180 double maxRxGain(void) { return rx_gain_max; }
181 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000182
kurtis.heimerl02d04052011-11-26 03:17:10 +0000183 double setTxGain(double db);
184 double maxTxGain(void) { return tx_gain_max; }
185 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000186
kurtis.heimerl02d04052011-11-26 03:17:10 +0000187 double getTxFreq() { return tx_freq; }
188 double getRxFreq() { return rx_freq; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000189
190 inline double getSampleRate() { return actual_smpl_rt; }
191 inline double numberRead() { return rx_pkt_cnt; }
192 inline double numberWritten() { return 0; }
193
194 /** Receive and process asynchronous message
195 @return true if message received or false on timeout or error
196 */
197 bool recv_async_msg();
198
kurtis.heimerld4be0742011-11-26 03:17:46 +0000199 enum err_code {
200 ERROR_TIMING = -1,
201 ERROR_UNRECOVERABLE = -2,
202 ERROR_UNHANDLED = -3,
203 };
204
kurtis.heimerl965e7572011-11-26 03:16:54 +0000205private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000206 uhd::usrp::multi_usrp::sptr usrp_dev;
kurtis.heimerle380af32011-11-26 03:18:55 +0000207 enum busType bus;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000208
kurtis.heimerl02d04052011-11-26 03:17:10 +0000209 double desired_smpl_rt, actual_smpl_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000210
kurtis.heimerl02d04052011-11-26 03:17:10 +0000211 double tx_gain, tx_gain_min, tx_gain_max;
212 double rx_gain, rx_gain_min, rx_gain_max;
213
214 double tx_freq, rx_freq;
215 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000216
217 bool started;
218 bool aligned;
219 bool skip_rx;
220
221 size_t rx_pkt_cnt;
222 size_t drop_cnt;
223 uhd::time_spec_t prev_ts;
224
225 TIMESTAMP ts_offset;
226 smpl_buf *rx_smpl_buf;
227
kurtis.heimerl02d04052011-11-26 03:17:10 +0000228 void init_gains();
kurtis.heimerl24481de2011-11-26 03:17:18 +0000229 void set_ref_clk(bool ext_clk);
230 double set_rates(double rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000231 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000232 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000233 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000234
kurtis.heimerl965e7572011-11-26 03:16:54 +0000235 std::string str_code(uhd::rx_metadata_t metadata);
236 std::string str_code(uhd::async_metadata_t metadata);
237
238 Thread async_event_thrd;
239};
240
241void *async_event_loop(uhd_device *dev)
242{
243 while (1) {
244 dev->recv_async_msg();
245 pthread_testcancel();
246 }
247}
248
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000249/*
250 Catch and drop underrun 'U' and overrun 'O' messages from stdout
251 since we already report using the logging facility. Direct
252 everything else appropriately.
253 */
254void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
255{
256 switch (type) {
257 case uhd::msg::status:
258 LOG(INFO) << msg;
259 break;
260 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000261 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000262 break;
263 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000264 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000265 break;
266 case uhd::msg::fastpath:
267 break;
268 }
269}
270
kurtis.heimerl965e7572011-11-26 03:16:54 +0000271uhd_device::uhd_device(double rate, bool skip_rx)
kurtis.heimerl02d04052011-11-26 03:17:10 +0000272 : desired_smpl_rt(rate), actual_smpl_rt(0),
273 tx_gain(0.0), tx_gain_min(0.0), tx_gain_max(0.0),
274 rx_gain(0.0), rx_gain_min(0.0), rx_gain_max(0.0),
275 tx_freq(0.0), rx_freq(0.0), tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000276 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
kurtis.heimerl965e7572011-11-26 03:16:54 +0000277 prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
278{
279 this->skip_rx = skip_rx;
280}
281
282uhd_device::~uhd_device()
283{
284 stop();
285
286 if (rx_smpl_buf)
287 delete rx_smpl_buf;
288}
289
kurtis.heimerl24481de2011-11-26 03:17:18 +0000290void uhd_device::init_gains()
291{
292 uhd::gain_range_t range;
293
294 range = usrp_dev->get_tx_gain_range();
295 tx_gain_min = range.start();
296 tx_gain_max = range.stop();
297
298 range = usrp_dev->get_rx_gain_range();
299 rx_gain_min = range.start();
300 rx_gain_max = range.stop();
301
302 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2);
303 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2);
304
305 tx_gain = usrp_dev->get_tx_gain();
306 rx_gain = usrp_dev->get_rx_gain();
307
308 return;
309}
310
311void uhd_device::set_ref_clk(bool ext_clk)
312{
313 uhd::clock_config_t clk_cfg;
314
315 clk_cfg.pps_source = uhd::clock_config_t::PPS_SMA;
kurtis.heimerl24481de2011-11-26 03:17:18 +0000316
317 if (ext_clk)
318 clk_cfg.ref_source = uhd::clock_config_t::REF_SMA;
319 else
320 clk_cfg.ref_source = uhd::clock_config_t::REF_INT;
321
322 usrp_dev->set_clock_config(clk_cfg);
323
324 return;
325}
326
327double uhd_device::set_rates(double rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000328{
kurtis.heimerld3e25902011-11-26 03:18:13 +0000329 double actual_rt, actual_clk_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000330
kurtis.heimerlce317332011-11-26 03:18:39 +0000331#ifndef RESAMPLE
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000332 // Make sure we can set the master clock rate on this device
333 actual_clk_rt = usrp_dev->get_master_clock_rate();
334 if (actual_clk_rt > U1_DEFAULT_CLK_RT) {
335 LOG(ALERT) << "Cannot set clock rate on this device";
336 LOG(ALERT) << "Please compile with host resampling support";
337 return -1.0;
338 }
339
kurtis.heimerld3e25902011-11-26 03:18:13 +0000340 // Set master clock rate
341 usrp_dev->set_master_clock_rate(master_clk_rt);
342 actual_clk_rt = usrp_dev->get_master_clock_rate();
343
344 if (actual_clk_rt != master_clk_rt) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000345 LOG(ALERT) << "Failed to set master clock rate";
346 LOG(ALERT) << "Actual clock rate " << actual_clk_rt;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000347 return -1.0;
348 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000349#endif
kurtis.heimerld3e25902011-11-26 03:18:13 +0000350
351 // Set sample rates
kurtis.heimerl24481de2011-11-26 03:17:18 +0000352 usrp_dev->set_tx_rate(rate);
353 usrp_dev->set_rx_rate(rate);
kurtis.heimerld3e25902011-11-26 03:18:13 +0000354 actual_rt = usrp_dev->get_tx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000355
kurtis.heimerld3e25902011-11-26 03:18:13 +0000356 if (actual_rt != rate) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000357 LOG(ALERT) << "Actual sample rate differs from desired rate";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000358 return -1.0;
359 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000360 if (usrp_dev->get_rx_rate() != actual_rt) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000361 LOG(ALERT) << "Transmit and receive sample rates do not match";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000362 return -1.0;
363 }
364
kurtis.heimerld3e25902011-11-26 03:18:13 +0000365 return actual_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000366}
367
kurtis.heimerl02d04052011-11-26 03:17:10 +0000368double uhd_device::setTxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000369{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000370 usrp_dev->set_tx_gain(db);
371 tx_gain = usrp_dev->get_tx_gain();
372
373 LOG(INFO) << "Set TX gain to " << tx_gain << "dB";
374
375 return tx_gain;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000376}
377
kurtis.heimerl02d04052011-11-26 03:17:10 +0000378double uhd_device::setRxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000379{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000380 usrp_dev->set_rx_gain(db);
381 rx_gain = usrp_dev->get_rx_gain();
382
383 LOG(INFO) << "Set RX gain to " << rx_gain << "dB";
384
385 return rx_gain;
386}
387
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000388/*
389 Parse the UHD device tree and mboard name to find out what device we're
390 dealing with. We need the bus type so that the transceiver knows how to
391 deal with the transport latency. Reject the USRP1 because UHD doesn't
392 support timestamped samples with it.
393 */
394bool uhd_device::parse_dev_type()
395{
396 std::string mboard_str, dev_str;
397 uhd::property_tree::sptr prop_tree;
398 size_t usrp1_str, usrp2_str, b100_str1, b100_str2;
399
400 prop_tree = usrp_dev->get_device()->get_tree();
401 dev_str = prop_tree->access<std::string>("/name").get();
402 mboard_str = usrp_dev->get_mboard_name();
403
404 usrp1_str = dev_str.find("USRP1");
405 b100_str1 = dev_str.find("B-Series");
406 b100_str2 = mboard_str.find("B100");
407
408 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000409 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
410 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000411 return false;
412 }
413
414 if ((b100_str1 != std::string::npos) || (b100_str2 != std::string::npos)) {
415 bus = USB;
416 LOG(INFO) << "Using USB bus for " << dev_str;
417 } else {
418 bus = NET;
419 LOG(INFO) << "Using network bus for " << dev_str;
420 }
421
422 return true;
423}
424
ttsouf60dafa2012-10-22 00:07:14 +0000425bool uhd_device::open(const std::string &args)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000426{
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000427 // Register msg handler
428 uhd::msg::register_handler(&uhd_msg_handler);
429
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000430 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000431 uhd::device_addr_t addr(args);
432 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000433 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000434 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000435 return false;
436 }
437
438 // Use the first found device
439 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000440 try {
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000441 usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
kurtis.heimerle380af32011-11-26 03:18:55 +0000442 } catch(...) {
ttsou3b5c0c12012-02-14 17:58:11 +0000443 LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000444 return false;
445 }
446
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000447 // Check for a valid device type and set bus type
448 if (!parse_dev_type())
449 return false;
450
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000451#ifdef EXTREF
452 set_ref_clk(true);
453#endif
454
kurtis.heimerl965e7572011-11-26 03:16:54 +0000455 // Number of samples per over-the-wire packet
456 tx_spp = usrp_dev->get_device()->get_max_send_samps_per_packet();
457 rx_spp = usrp_dev->get_device()->get_max_recv_samps_per_packet();
458
459 // Set rates
kurtis.heimerl24481de2011-11-26 03:17:18 +0000460 actual_smpl_rt = set_rates(desired_smpl_rt);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000461 if (actual_smpl_rt < 0)
462 return false;
463
464 // Create receive buffer
465 size_t buf_len = smpl_buf_sz / sizeof(uint32_t);
466 rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
467
468 // Set receive chain sample offset
469 ts_offset = (TIMESTAMP)(rx_smpl_offset * actual_smpl_rt);
470
kurtis.heimerl02d04052011-11-26 03:17:10 +0000471 // Initialize and shadow gain values
472 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000473
kurtis.heimerl965e7572011-11-26 03:16:54 +0000474 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000475 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000476
477 return true;
478}
479
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000480bool uhd_device::flush_recv(size_t num_pkts)
481{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000482 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000483 size_t num_smpls;
484 uint32_t buff[rx_spp];
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000485 float timeout;
486
487 // Use .01 sec instead of the default .1 sec
488 timeout = .01;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000489
490 for (size_t i = 0; i < num_pkts; i++) {
491 num_smpls = usrp_dev->get_device()->recv(
492 buff,
493 rx_spp,
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000494 md,
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000495 uhd::io_type_t::COMPLEX_INT16,
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000496 uhd::device::RECV_MODE_ONE_PACKET,
497 timeout);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000498
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000499 if (!num_smpls) {
500 switch (md.error_code) {
501 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
502 return true;
503 default:
504 continue;
505 }
506 }
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000507 }
508
509 return true;
510}
511
kurtis.heimerl68292102011-11-26 03:17:28 +0000512void uhd_device::restart(uhd::time_spec_t ts)
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000513{
kurtis.heimerl68292102011-11-26 03:17:28 +0000514 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
515 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000516
kurtis.heimerl68292102011-11-26 03:17:28 +0000517 flush_recv(50);
518
519 usrp_dev->set_time_now(ts);
520 aligned = false;
521
522 cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
523 cmd.stream_now = true;
524 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000525}
526
kurtis.heimerl965e7572011-11-26 03:16:54 +0000527bool uhd_device::start()
528{
529 LOG(INFO) << "Starting USRP...";
530
531 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000532 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000533 return false;
534 }
535
536 setPriority();
537
538 // Start asynchronous event (underrun check) loop
539 async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
540
541 // Start streaming
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000542 restart(uhd::time_spec_t(0.0));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000543
kurtis.heimerl965e7572011-11-26 03:16:54 +0000544 // Display usrp time
545 double time_now = usrp_dev->get_time_now().get_real_secs();
546 LOG(INFO) << "The current time is " << time_now << " seconds";
547
548 started = true;
549 return true;
550}
551
552bool uhd_device::stop()
553{
554 uhd::stream_cmd_t stream_cmd =
555 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
556
557 usrp_dev->issue_stream_cmd(stream_cmd);
558
559 started = false;
560 return true;
561}
562
563void uhd_device::setPriority()
564{
565 uhd::set_thread_priority_safe();
566 return;
567}
568
kurtis.heimerld4be0742011-11-26 03:17:46 +0000569int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000570{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000571 uhd::time_spec_t ts;
572
kurtis.heimerld4be0742011-11-26 03:17:46 +0000573 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000574 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000575
576 switch (md.error_code) {
577 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000578 LOG(ALERT) << "UHD: Receive timed out";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000579 return ERROR_UNRECOVERABLE;
580 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
581 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
582 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
583 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
584 default:
585 return ERROR_UNHANDLED;
586 }
587 }
588
kurtis.heimerl965e7572011-11-26 03:16:54 +0000589 // Missing timestamp
590 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000591 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000592 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000593 }
594
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000595 ts = md.time_spec;
596
kurtis.heimerl965e7572011-11-26 03:16:54 +0000597 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000598 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000599 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000600 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
601 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000602 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000603 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000604 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000605 }
606
607 return 0;
608}
609
610int uhd_device::readSamples(short *buf, int len, bool *overrun,
611 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
612{
613 ssize_t rc;
614 uhd::time_spec_t ts;
615 uhd::rx_metadata_t metadata;
616 uint32_t pkt_buf[rx_spp];
617
618 if (skip_rx)
619 return 0;
620
621 // Shift read time with respect to transmit clock
622 timestamp += ts_offset;
623
624 ts = convert_time(timestamp, actual_smpl_rt);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000625 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000626
627 // Check that timestamp is valid
628 rc = rx_smpl_buf->avail_smpls(timestamp);
629 if (rc < 0) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000630 LOG(ERR) << rx_smpl_buf->str_code(rc);
631 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000632 return 0;
633 }
634
635 // Receive samples from the usrp until we have enough
636 while (rx_smpl_buf->avail_smpls(timestamp) < len) {
637 size_t num_smpls = usrp_dev->get_device()->recv(
638 (void*)pkt_buf,
639 rx_spp,
640 metadata,
641 uhd::io_type_t::COMPLEX_INT16,
642 uhd::device::RECV_MODE_ONE_PACKET);
643
644 rx_pkt_cnt++;
645
kurtis.heimerld4be0742011-11-26 03:17:46 +0000646 // Check for errors
647 rc = check_rx_md_err(metadata, num_smpls);
648 switch (rc) {
649 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000650 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
651 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000652 exit(-1);
653 case ERROR_TIMING:
654 restart(prev_ts);
655 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000656 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000657 }
658
kurtis.heimerl965e7572011-11-26 03:16:54 +0000659
660 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000661 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000662
663 rc = rx_smpl_buf->write(pkt_buf,
664 num_smpls,
665 metadata.time_spec);
666
667 // Continue on local overrun, exit on other errors
668 if ((rc < 0)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000669 LOG(ERR) << rx_smpl_buf->str_code(rc);
670 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000671 if (rc != smpl_buf::ERROR_OVERFLOW)
672 return 0;
673 }
674 }
675
676 // We have enough samples
677 rc = rx_smpl_buf->read(buf, len, timestamp);
678 if ((rc < 0) || (rc != len)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000679 LOG(ERR) << rx_smpl_buf->str_code(rc);
680 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000681 return 0;
682 }
683
684 return len;
685}
686
687int uhd_device::writeSamples(short *buf, int len, bool *underrun,
688 unsigned long long timestamp,bool isControl)
689{
690 uhd::tx_metadata_t metadata;
691 metadata.has_time_spec = true;
692 metadata.start_of_burst = false;
693 metadata.end_of_burst = false;
694 metadata.time_spec = convert_time(timestamp, actual_smpl_rt);
695
696 // No control packets
697 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000698 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000699 return 0;
700 }
701
702 // Drop a fixed number of packets (magic value)
703 if (!aligned) {
704 drop_cnt++;
705
706 if (drop_cnt == 1) {
707 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000708 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000709 metadata.end_of_burst = true;
710 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000711 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000712 return len;
713 } else {
714 LOG(DEBUG) << "Aligning transmitter: start burst";
715 metadata.start_of_burst = true;
716 aligned = true;
717 drop_cnt = 0;
718 }
719 }
720
721 size_t num_smpls = usrp_dev->get_device()->send(buf,
722 len,
723 metadata,
724 uhd::io_type_t::COMPLEX_INT16,
725 uhd::device::SEND_MODE_FULL_BUFF);
726
ttsoub371ed52012-01-09 18:11:34 +0000727 if (num_smpls != (unsigned) len) {
728 LOG(ALERT) << "UHD: Device send timed out";
729 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
730 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
731 exit(-1);
732 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000733
734 return num_smpls;
735}
736
737bool uhd_device::updateAlignment(TIMESTAMP timestamp)
738{
kurtis.heimerl13074c92011-11-26 03:17:43 +0000739 aligned = false;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000740 return true;
741}
742
743bool uhd_device::setTxFreq(double wFreq)
744{
745 uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000746 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000747 tx_freq = usrp_dev->get_tx_freq();
748
kurtis.heimerl965e7572011-11-26 03:16:54 +0000749 return true;
750}
751
752bool uhd_device::setRxFreq(double wFreq)
753{
754 uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000755 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000756 rx_freq = usrp_dev->get_rx_freq();
757
kurtis.heimerl965e7572011-11-26 03:16:54 +0000758 return true;
759}
760
761bool uhd_device::recv_async_msg()
762{
ttsou60dc4c92012-08-08 23:30:23 +0000763 uhd::async_metadata_t md;
764 if (!usrp_dev->get_device()->recv_async_msg(md))
kurtis.heimerl965e7572011-11-26 03:16:54 +0000765 return false;
766
767 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +0000768 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +0000769 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +0000770
771 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
772 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
773 LOG(ERR) << str_code(md);
774 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000775 }
776
777 return true;
778}
779
780std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
781{
782 std::ostringstream ost("UHD: ");
783
784 switch (metadata.error_code) {
785 case uhd::rx_metadata_t::ERROR_CODE_NONE:
786 ost << "No error";
787 break;
788 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
789 ost << "No packet received, implementation timed-out";
790 break;
791 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
792 ost << "A stream command was issued in the past";
793 break;
794 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
795 ost << "Expected another stream command";
796 break;
797 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
798 ost << "An internal receive buffer has filled";
799 break;
800 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
801 ost << "The packet could not be parsed";
802 break;
803 default:
804 ost << "Unknown error " << metadata.error_code;
805 }
806
807 if (metadata.has_time_spec)
808 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
809
810 return ost.str();
811}
812
813std::string uhd_device::str_code(uhd::async_metadata_t metadata)
814{
815 std::ostringstream ost("UHD: ");
816
817 switch (metadata.event_code) {
818 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
819 ost << "A packet was successfully transmitted";
820 break;
821 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
822 ost << "An internal send buffer has emptied";
823 break;
824 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
825 ost << "Packet loss between host and device";
826 break;
827 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
828 ost << "Packet time was too late or too early";
829 break;
830 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
831 ost << "Underflow occurred inside a packet";
832 break;
833 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
834 ost << "Packet loss within a burst";
835 break;
836 default:
837 ost << "Unknown error " << metadata.event_code;
838 }
839
840 if (metadata.has_time_spec)
841 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
842
843 return ost.str();
844}
845
846smpl_buf::smpl_buf(size_t len, double rate)
847 : buf_len(len), clk_rt(rate),
848 time_start(0), time_end(0), data_start(0), data_end(0)
849{
850 data = new uint32_t[len];
851}
852
853smpl_buf::~smpl_buf()
854{
855 delete[] data;
856}
857
858ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
859{
860 if (timestamp < time_start)
861 return ERROR_TIMESTAMP;
862 else if (timestamp >= time_end)
863 return 0;
864 else
865 return time_end - timestamp;
866}
867
868ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
869{
870 return avail_smpls(convert_time(timespec, clk_rt));
871}
872
873ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
874{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000875 int type_sz = 2 * sizeof(short);
876
kurtis.heimerl965e7572011-11-26 03:16:54 +0000877 // Check for valid read
878 if (timestamp < time_start)
879 return ERROR_TIMESTAMP;
880 if (timestamp >= time_end)
881 return 0;
882 if (len >= buf_len)
883 return ERROR_READ;
884
885 // How many samples should be copied
886 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +0000887 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000888 num_smpls = len;
889
890 // Starting index
891 size_t read_start = data_start + (timestamp - time_start);
892
893 // Read it
894 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000895 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000896 memcpy(buf, data + read_start, numBytes);
897 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000898 size_t first_cp = (buf_len - read_start) * type_sz;
899 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000900
901 memcpy(buf, data + read_start, first_cp);
902 memcpy((char*) buf + first_cp, data, second_cp);
903 }
904
905 data_start = (read_start + len) % buf_len;
906 time_start = timestamp + len;
907
908 if (time_start > time_end)
909 return ERROR_READ;
910 else
911 return num_smpls;
912}
913
914ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
915{
916 return read(buf, len, convert_time(ts, clk_rt));
917}
918
919ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
920{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000921 int type_sz = 2 * sizeof(short);
922
kurtis.heimerl965e7572011-11-26 03:16:54 +0000923 // Check for valid write
924 if ((len == 0) || (len >= buf_len))
925 return ERROR_WRITE;
926 if ((timestamp + len) <= time_end)
927 return ERROR_TIMESTAMP;
928
929 // Starting index
930 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
931
932 // Write it
933 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000934 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000935 memcpy(data + write_start, buf, numBytes);
936 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000937 size_t first_cp = (buf_len - write_start) * type_sz;
938 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000939
940 memcpy(data + write_start, buf, first_cp);
941 memcpy(data, (char*) buf + first_cp, second_cp);
942 }
943
944 data_end = (write_start + len) % buf_len;
945 time_end = timestamp + len;
946
947 if (((write_start + len) > buf_len) && (data_end > data_start))
948 return ERROR_OVERFLOW;
949 else if (time_end <= time_start)
950 return ERROR_WRITE;
951 else
952 return len;
953}
954
955ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
956{
957 return write(buf, len, convert_time(ts, clk_rt));
958}
959
960std::string smpl_buf::str_status() const
961{
962 std::ostringstream ost("Sample buffer: ");
963
964 ost << "length = " << buf_len;
965 ost << ", time_start = " << time_start;
966 ost << ", time_end = " << time_end;
967 ost << ", data_start = " << data_start;
968 ost << ", data_end = " << data_end;
969
970 return ost.str();
971}
972
973std::string smpl_buf::str_code(ssize_t code)
974{
975 switch (code) {
976 case ERROR_TIMESTAMP:
977 return "Sample buffer: Requested timestamp is not valid";
978 case ERROR_READ:
979 return "Sample buffer: Read error";
980 case ERROR_WRITE:
981 return "Sample buffer: Write error";
982 case ERROR_OVERFLOW:
983 return "Sample buffer: Overrun";
984 default:
985 return "Sample buffer: Unknown error";
986 }
987}
988
989RadioDevice *RadioDevice::make(double smpl_rt, bool skip_rx)
990{
991 return new uhd_device(smpl_rt, skip_rx);
992}