blob: 17e7b359a271d37364a9f502d0127ff8973233c3 [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"
kurtis.heimerle380af32011-11-26 03:18:55 +000025#include <uhd/property_tree.hpp>
kurtis.heimerl856bbbe2011-12-15 00:50:16 +000026#include <uhd/usrp/multi_usrp.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000027#include <uhd/utils/thread_priority.hpp>
kurtis.heimerl0803ad92011-11-26 03:18:51 +000028#include <uhd/utils/msg.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000029
kurtis.heimerlce317332011-11-26 03:18:39 +000030#ifdef HAVE_CONFIG_H
31#include "config.h"
32#endif
33
kurtis.heimerlac0ee122011-11-28 06:25:58 +000034#define U1_DEFAULT_CLK_RT 64e6
35#define U2_DEFAULT_CLK_RT 100e6
36
kurtis.heimerl965e7572011-11-26 03:16:54 +000037/*
kurtis.heimerlce317332011-11-26 03:18:39 +000038 master_clk_rt - Master clock frequency - ignored if host resampling is
39 enabled
kurtis.heimerl965e7572011-11-26 03:16:54 +000040
41 rx_smpl_offset - Timing correction in seconds between receive and
42 transmit timestamps. This value corrects for delays on
43 on the RF side of the timestamping point of the device.
kurtis.heimerlbe6abe12011-11-26 03:17:05 +000044 This value is generally empirically measured.
kurtis.heimerl965e7572011-11-26 03:16:54 +000045
kurtis.heimerl7ac54b12011-11-26 03:17:49 +000046 smpl_buf_sz - The receive sample buffer size in bytes.
47
48 tx_ampl - Transmit amplitude must be between 0 and 1.0
kurtis.heimerl965e7572011-11-26 03:16:54 +000049*/
kurtis.heimerl965e7572011-11-26 03:16:54 +000050const double master_clk_rt = 52e6;
kurtis.heimerl965e7572011-11-26 03:16:54 +000051const size_t smpl_buf_sz = (1 << 20);
kurtis.heimerl7ac54b12011-11-26 03:17:49 +000052const float tx_ampl = .3;
kurtis.heimerl965e7572011-11-26 03:16:54 +000053
kurtis.heimerlce317332011-11-26 03:18:39 +000054#ifdef RESAMPLE
55const double rx_smpl_offset = .00005;
56#else
57const double rx_smpl_offset = .0000869;
58#endif
59
kurtis.heimerl965e7572011-11-26 03:16:54 +000060/** Timestamp conversion
61 @param timestamp a UHD or OpenBTS timestamp
62 @param rate sample rate
63 @return the converted timestamp
64*/
65uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
66{
67 double secs = (double) ticks / rate;
68 return uhd::time_spec_t(secs);
69}
70
71TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
72{
kurtis.heimerlc7cb8172011-11-26 03:17:26 +000073 TIMESTAMP ticks = ts.get_full_secs() * rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +000074 return ts.get_tick_count(rate) + ticks;
75}
76
77/*
78 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
79 or UHD style timestamps. Time conversions are handled
80 internally or accessable through the static convert calls.
81*/
82class smpl_buf {
83public:
84 /** Sample buffer constructor
85 @param len number of 32-bit samples the buffer should hold
86 @param rate sample clockrate
87 @param timestamp
88 */
89 smpl_buf(size_t len, double rate);
90 ~smpl_buf();
91
92 /** Query number of samples available for reading
93 @param timestamp time of first sample
94 @return number of available samples or error
95 */
96 ssize_t avail_smpls(TIMESTAMP timestamp) const;
97 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
98
99 /** Read and write
100 @param buf pointer to buffer
101 @param len number of samples desired to read or write
102 @param timestamp time of first stample
103 @return number of actual samples read or written or error
104 */
105 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
106 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
107 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
108 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
109
110 /** Buffer status string
111 @return a formatted string describing internal buffer state
112 */
113 std::string str_status() const;
114
115 /** Formatted error string
116 @param code an error code
117 @return a formatted error string
118 */
119 static std::string str_code(ssize_t code);
120
121 enum err_code {
122 ERROR_TIMESTAMP = -1,
123 ERROR_READ = -2,
124 ERROR_WRITE = -3,
125 ERROR_OVERFLOW = -4
126 };
127
128private:
129 uint32_t *data;
130 size_t buf_len;
131
132 double clk_rt;
133
134 TIMESTAMP time_start;
135 TIMESTAMP time_end;
136
137 size_t data_start;
138 size_t data_end;
139};
140
141/*
142 uhd_device - UHD implementation of the Device interface. Timestamped samples
143 are sent to and received from the device. An intermediate buffer
144 on the receive side collects and aligns packets of samples.
145 Events and errors such as underruns are reported asynchronously
146 by the device and received in a separate thread.
147*/
148class uhd_device : public RadioDevice {
149public:
150 uhd_device(double rate, bool skip_rx);
151 ~uhd_device();
152
153 bool open();
154 bool start();
155 bool stop();
kurtis.heimerl68292102011-11-26 03:17:28 +0000156 void restart(uhd::time_spec_t ts);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000157 void setPriority();
kurtis.heimerle380af32011-11-26 03:18:55 +0000158 enum busType getBus() { return bus; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000159
160 int readSamples(short *buf, int len, bool *overrun,
161 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
162
163 int writeSamples(short *buf, int len, bool *underrun,
164 TIMESTAMP timestamp, bool isControl);
165
166 bool updateAlignment(TIMESTAMP timestamp);
167
168 bool setTxFreq(double wFreq);
169 bool setRxFreq(double wFreq);
170
171 inline TIMESTAMP initialWriteTimestamp() { return 0; }
172 inline TIMESTAMP initialReadTimestamp() { return 0; }
173
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000174 inline double fullScaleInputValue() { return 32000 * tx_ampl; }
175 inline double fullScaleOutputValue() { return 32000; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000176
kurtis.heimerl02d04052011-11-26 03:17:10 +0000177 double setRxGain(double db);
178 double getRxGain(void) { return rx_gain; }
179 double maxRxGain(void) { return rx_gain_max; }
180 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000181
kurtis.heimerl02d04052011-11-26 03:17:10 +0000182 double setTxGain(double db);
183 double maxTxGain(void) { return tx_gain_max; }
184 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000185
kurtis.heimerl02d04052011-11-26 03:17:10 +0000186 double getTxFreq() { return tx_freq; }
187 double getRxFreq() { return rx_freq; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000188
189 inline double getSampleRate() { return actual_smpl_rt; }
190 inline double numberRead() { return rx_pkt_cnt; }
191 inline double numberWritten() { return 0; }
192
193 /** Receive and process asynchronous message
194 @return true if message received or false on timeout or error
195 */
196 bool recv_async_msg();
197
kurtis.heimerld4be0742011-11-26 03:17:46 +0000198 enum err_code {
199 ERROR_TIMING = -1,
200 ERROR_UNRECOVERABLE = -2,
201 ERROR_UNHANDLED = -3,
202 };
203
kurtis.heimerl965e7572011-11-26 03:16:54 +0000204private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000205 uhd::usrp::multi_usrp::sptr usrp_dev;
kurtis.heimerle380af32011-11-26 03:18:55 +0000206 enum busType bus;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000207
kurtis.heimerl02d04052011-11-26 03:17:10 +0000208 double desired_smpl_rt, actual_smpl_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000209
kurtis.heimerl02d04052011-11-26 03:17:10 +0000210 double tx_gain, tx_gain_min, tx_gain_max;
211 double rx_gain, rx_gain_min, rx_gain_max;
212
213 double tx_freq, rx_freq;
214 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000215
216 bool started;
217 bool aligned;
218 bool skip_rx;
219
220 size_t rx_pkt_cnt;
221 size_t drop_cnt;
222 uhd::time_spec_t prev_ts;
223
224 TIMESTAMP ts_offset;
225 smpl_buf *rx_smpl_buf;
226
kurtis.heimerl02d04052011-11-26 03:17:10 +0000227 void init_gains();
kurtis.heimerl24481de2011-11-26 03:17:18 +0000228 void set_ref_clk(bool ext_clk);
229 double set_rates(double rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000230 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000231 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000232 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000233
kurtis.heimerl965e7572011-11-26 03:16:54 +0000234 std::string str_code(uhd::rx_metadata_t metadata);
235 std::string str_code(uhd::async_metadata_t metadata);
236
237 Thread async_event_thrd;
238};
239
240void *async_event_loop(uhd_device *dev)
241{
242 while (1) {
243 dev->recv_async_msg();
244 pthread_testcancel();
245 }
246}
247
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000248/*
249 Catch and drop underrun 'U' and overrun 'O' messages from stdout
250 since we already report using the logging facility. Direct
251 everything else appropriately.
252 */
253void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
254{
255 switch (type) {
256 case uhd::msg::status:
257 LOG(INFO) << msg;
258 break;
259 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000260 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000261 break;
262 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000263 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000264 break;
265 case uhd::msg::fastpath:
266 break;
267 }
268}
269
kurtis.heimerl965e7572011-11-26 03:16:54 +0000270uhd_device::uhd_device(double rate, bool skip_rx)
kurtis.heimerl02d04052011-11-26 03:17:10 +0000271 : desired_smpl_rt(rate), actual_smpl_rt(0),
272 tx_gain(0.0), tx_gain_min(0.0), tx_gain_max(0.0),
273 rx_gain(0.0), rx_gain_min(0.0), rx_gain_max(0.0),
274 tx_freq(0.0), rx_freq(0.0), tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000275 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
kurtis.heimerl965e7572011-11-26 03:16:54 +0000276 prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
277{
278 this->skip_rx = skip_rx;
279}
280
281uhd_device::~uhd_device()
282{
283 stop();
284
285 if (rx_smpl_buf)
286 delete rx_smpl_buf;
287}
288
kurtis.heimerl24481de2011-11-26 03:17:18 +0000289void uhd_device::init_gains()
290{
291 uhd::gain_range_t range;
292
293 range = usrp_dev->get_tx_gain_range();
294 tx_gain_min = range.start();
295 tx_gain_max = range.stop();
296
297 range = usrp_dev->get_rx_gain_range();
298 rx_gain_min = range.start();
299 rx_gain_max = range.stop();
300
301 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2);
302 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2);
303
304 tx_gain = usrp_dev->get_tx_gain();
305 rx_gain = usrp_dev->get_rx_gain();
306
307 return;
308}
309
310void uhd_device::set_ref_clk(bool ext_clk)
311{
312 uhd::clock_config_t clk_cfg;
313
314 clk_cfg.pps_source = uhd::clock_config_t::PPS_SMA;
kurtis.heimerl24481de2011-11-26 03:17:18 +0000315
316 if (ext_clk)
317 clk_cfg.ref_source = uhd::clock_config_t::REF_SMA;
318 else
319 clk_cfg.ref_source = uhd::clock_config_t::REF_INT;
320
321 usrp_dev->set_clock_config(clk_cfg);
322
323 return;
324}
325
326double uhd_device::set_rates(double rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000327{
kurtis.heimerld3e25902011-11-26 03:18:13 +0000328 double actual_rt, actual_clk_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000329
kurtis.heimerlce317332011-11-26 03:18:39 +0000330#ifndef RESAMPLE
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000331 // Make sure we can set the master clock rate on this device
332 actual_clk_rt = usrp_dev->get_master_clock_rate();
333 if (actual_clk_rt > U1_DEFAULT_CLK_RT) {
334 LOG(ALERT) << "Cannot set clock rate on this device";
335 LOG(ALERT) << "Please compile with host resampling support";
336 return -1.0;
337 }
338
kurtis.heimerld3e25902011-11-26 03:18:13 +0000339 // Set master clock rate
340 usrp_dev->set_master_clock_rate(master_clk_rt);
341 actual_clk_rt = usrp_dev->get_master_clock_rate();
342
343 if (actual_clk_rt != master_clk_rt) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000344 LOG(ALERT) << "Failed to set master clock rate";
345 LOG(ALERT) << "Actual clock rate " << actual_clk_rt;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000346 return -1.0;
347 }
kurtis.heimerlce317332011-11-26 03:18:39 +0000348#endif
kurtis.heimerld3e25902011-11-26 03:18:13 +0000349
350 // Set sample rates
kurtis.heimerl24481de2011-11-26 03:17:18 +0000351 usrp_dev->set_tx_rate(rate);
352 usrp_dev->set_rx_rate(rate);
kurtis.heimerld3e25902011-11-26 03:18:13 +0000353 actual_rt = usrp_dev->get_tx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000354
kurtis.heimerld3e25902011-11-26 03:18:13 +0000355 if (actual_rt != rate) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000356 LOG(ALERT) << "Actual sample rate differs from desired rate";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000357 return -1.0;
358 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000359 if (usrp_dev->get_rx_rate() != actual_rt) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000360 LOG(ALERT) << "Transmit and receive sample rates do not match";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000361 return -1.0;
362 }
363
kurtis.heimerld3e25902011-11-26 03:18:13 +0000364 return actual_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000365}
366
kurtis.heimerl02d04052011-11-26 03:17:10 +0000367double uhd_device::setTxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000368{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000369 usrp_dev->set_tx_gain(db);
370 tx_gain = usrp_dev->get_tx_gain();
371
372 LOG(INFO) << "Set TX gain to " << tx_gain << "dB";
373
374 return tx_gain;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000375}
376
kurtis.heimerl02d04052011-11-26 03:17:10 +0000377double uhd_device::setRxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000378{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000379 usrp_dev->set_rx_gain(db);
380 rx_gain = usrp_dev->get_rx_gain();
381
382 LOG(INFO) << "Set RX gain to " << rx_gain << "dB";
383
384 return rx_gain;
385}
386
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000387/*
388 Parse the UHD device tree and mboard name to find out what device we're
389 dealing with. We need the bus type so that the transceiver knows how to
390 deal with the transport latency. Reject the USRP1 because UHD doesn't
391 support timestamped samples with it.
392 */
393bool uhd_device::parse_dev_type()
394{
395 std::string mboard_str, dev_str;
396 uhd::property_tree::sptr prop_tree;
397 size_t usrp1_str, usrp2_str, b100_str1, b100_str2;
398
399 prop_tree = usrp_dev->get_device()->get_tree();
400 dev_str = prop_tree->access<std::string>("/name").get();
401 mboard_str = usrp_dev->get_mboard_name();
402
403 usrp1_str = dev_str.find("USRP1");
404 b100_str1 = dev_str.find("B-Series");
405 b100_str2 = mboard_str.find("B100");
406
407 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000408 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
409 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000410 return false;
411 }
412
413 if ((b100_str1 != std::string::npos) || (b100_str2 != std::string::npos)) {
414 bus = USB;
415 LOG(INFO) << "Using USB bus for " << dev_str;
416 } else {
417 bus = NET;
418 LOG(INFO) << "Using network bus for " << dev_str;
419 }
420
421 return true;
422}
423
kurtis.heimerl965e7572011-11-26 03:16:54 +0000424bool uhd_device::open()
425{
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000426 // Register msg handler
427 uhd::msg::register_handler(&uhd_msg_handler);
428
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000429 // Find UHD devices
430 uhd::device_addr_t args("");
431 uhd::device_addrs_t dev_addrs = uhd::device::find(args);
432 if (dev_addrs.size() == 0) {
433 LOG(ALERT) << "No UHD devices found";
434 return false;
435 }
436
437 // Use the first found device
438 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000439 try {
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000440 usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
kurtis.heimerle380af32011-11-26 03:18:55 +0000441 } catch(...) {
kurtis.heimerle417c5a2011-11-26 03:19:25 +0000442 LOG(ALERT) << "UHD make failed";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000443 return false;
444 }
445
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000446 // Check for a valid device type and set bus type
447 if (!parse_dev_type())
448 return false;
449
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000450#ifdef EXTREF
451 set_ref_clk(true);
452#endif
453
kurtis.heimerl965e7572011-11-26 03:16:54 +0000454 // Number of samples per over-the-wire packet
455 tx_spp = usrp_dev->get_device()->get_max_send_samps_per_packet();
456 rx_spp = usrp_dev->get_device()->get_max_recv_samps_per_packet();
457
458 // Set rates
kurtis.heimerl24481de2011-11-26 03:17:18 +0000459 actual_smpl_rt = set_rates(desired_smpl_rt);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000460 if (actual_smpl_rt < 0)
461 return false;
462
463 // Create receive buffer
464 size_t buf_len = smpl_buf_sz / sizeof(uint32_t);
465 rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
466
467 // Set receive chain sample offset
468 ts_offset = (TIMESTAMP)(rx_smpl_offset * actual_smpl_rt);
469
kurtis.heimerl02d04052011-11-26 03:17:10 +0000470 // Initialize and shadow gain values
471 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000472
kurtis.heimerl965e7572011-11-26 03:16:54 +0000473 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000474 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000475
476 return true;
477}
478
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000479bool uhd_device::flush_recv(size_t num_pkts)
480{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000481 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000482 size_t num_smpls;
483 uint32_t buff[rx_spp];
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000484 float timeout;
485
486 // Use .01 sec instead of the default .1 sec
487 timeout = .01;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000488
489 for (size_t i = 0; i < num_pkts; i++) {
490 num_smpls = usrp_dev->get_device()->recv(
491 buff,
492 rx_spp,
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000493 md,
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000494 uhd::io_type_t::COMPLEX_INT16,
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000495 uhd::device::RECV_MODE_ONE_PACKET,
496 timeout);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000497
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000498 if (!num_smpls) {
499 switch (md.error_code) {
500 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
501 return true;
502 default:
503 continue;
504 }
505 }
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000506 }
507
508 return true;
509}
510
kurtis.heimerl68292102011-11-26 03:17:28 +0000511void uhd_device::restart(uhd::time_spec_t ts)
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000512{
kurtis.heimerl68292102011-11-26 03:17:28 +0000513 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
514 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000515
kurtis.heimerl68292102011-11-26 03:17:28 +0000516 flush_recv(50);
517
518 usrp_dev->set_time_now(ts);
519 aligned = false;
520
521 cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
522 cmd.stream_now = true;
523 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000524}
525
kurtis.heimerl965e7572011-11-26 03:16:54 +0000526bool uhd_device::start()
527{
528 LOG(INFO) << "Starting USRP...";
529
530 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000531 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000532 return false;
533 }
534
535 setPriority();
536
537 // Start asynchronous event (underrun check) loop
538 async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
539
540 // Start streaming
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000541 restart(uhd::time_spec_t(0.0));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000542
kurtis.heimerl965e7572011-11-26 03:16:54 +0000543 // Display usrp time
544 double time_now = usrp_dev->get_time_now().get_real_secs();
545 LOG(INFO) << "The current time is " << time_now << " seconds";
546
547 started = true;
548 return true;
549}
550
551bool uhd_device::stop()
552{
553 uhd::stream_cmd_t stream_cmd =
554 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
555
556 usrp_dev->issue_stream_cmd(stream_cmd);
557
558 started = false;
559 return true;
560}
561
562void uhd_device::setPriority()
563{
564 uhd::set_thread_priority_safe();
565 return;
566}
567
kurtis.heimerld4be0742011-11-26 03:17:46 +0000568int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000569{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000570 uhd::time_spec_t ts;
571
kurtis.heimerld4be0742011-11-26 03:17:46 +0000572 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000573 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000574
575 switch (md.error_code) {
576 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
577 return ERROR_UNRECOVERABLE;
578 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
579 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
580 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
581 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
582 default:
583 return ERROR_UNHANDLED;
584 }
585 }
586
kurtis.heimerl965e7572011-11-26 03:16:54 +0000587 // Missing timestamp
588 if (!md.has_time_spec) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000589 LOG(ERR) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000590 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000591 }
592
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000593 ts = md.time_spec;
594
kurtis.heimerl965e7572011-11-26 03:16:54 +0000595 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000596 if (ts < prev_ts) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000597 LOG(ERR) << "UHD: Loss of monotonic: " << ts.get_real_secs();
598 LOG(ERR) << "UHD: Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000599 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000600 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000601 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000602 }
603
604 return 0;
605}
606
607int uhd_device::readSamples(short *buf, int len, bool *overrun,
608 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
609{
610 ssize_t rc;
611 uhd::time_spec_t ts;
612 uhd::rx_metadata_t metadata;
613 uint32_t pkt_buf[rx_spp];
614
615 if (skip_rx)
616 return 0;
617
618 // Shift read time with respect to transmit clock
619 timestamp += ts_offset;
620
621 ts = convert_time(timestamp, actual_smpl_rt);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000622 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000623
624 // Check that timestamp is valid
625 rc = rx_smpl_buf->avail_smpls(timestamp);
626 if (rc < 0) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000627 LOG(ERR) << rx_smpl_buf->str_code(rc);
628 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000629 return 0;
630 }
631
632 // Receive samples from the usrp until we have enough
633 while (rx_smpl_buf->avail_smpls(timestamp) < len) {
634 size_t num_smpls = usrp_dev->get_device()->recv(
635 (void*)pkt_buf,
636 rx_spp,
637 metadata,
638 uhd::io_type_t::COMPLEX_INT16,
639 uhd::device::RECV_MODE_ONE_PACKET);
640
641 rx_pkt_cnt++;
642
kurtis.heimerld4be0742011-11-26 03:17:46 +0000643 // Check for errors
644 rc = check_rx_md_err(metadata, num_smpls);
645 switch (rc) {
646 case ERROR_UNRECOVERABLE:
kurtis.heimerle3320322011-11-28 06:26:08 +0000647 LOG(ALERT) << "Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000648 exit(-1);
649 case ERROR_TIMING:
650 restart(prev_ts);
651 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000652 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000653 }
654
kurtis.heimerl965e7572011-11-26 03:16:54 +0000655
656 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000657 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000658
659 rc = rx_smpl_buf->write(pkt_buf,
660 num_smpls,
661 metadata.time_spec);
662
663 // Continue on local overrun, exit on other errors
664 if ((rc < 0)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000665 LOG(ERR) << rx_smpl_buf->str_code(rc);
666 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000667 if (rc != smpl_buf::ERROR_OVERFLOW)
668 return 0;
669 }
670 }
671
672 // We have enough samples
673 rc = rx_smpl_buf->read(buf, len, timestamp);
674 if ((rc < 0) || (rc != len)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000675 LOG(ERR) << rx_smpl_buf->str_code(rc);
676 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000677 return 0;
678 }
679
680 return len;
681}
682
683int uhd_device::writeSamples(short *buf, int len, bool *underrun,
684 unsigned long long timestamp,bool isControl)
685{
686 uhd::tx_metadata_t metadata;
687 metadata.has_time_spec = true;
688 metadata.start_of_burst = false;
689 metadata.end_of_burst = false;
690 metadata.time_spec = convert_time(timestamp, actual_smpl_rt);
691
692 // No control packets
693 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000694 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000695 return 0;
696 }
697
698 // Drop a fixed number of packets (magic value)
699 if (!aligned) {
700 drop_cnt++;
701
702 if (drop_cnt == 1) {
703 LOG(DEBUG) << "Aligning transmitter: stop burst";
704 metadata.end_of_burst = true;
705 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000706 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000707 *underrun = true;
708 return len;
709 } else {
710 LOG(DEBUG) << "Aligning transmitter: start burst";
711 metadata.start_of_burst = true;
712 aligned = true;
713 drop_cnt = 0;
714 }
715 }
716
717 size_t num_smpls = usrp_dev->get_device()->send(buf,
718 len,
719 metadata,
720 uhd::io_type_t::COMPLEX_INT16,
721 uhd::device::SEND_MODE_FULL_BUFF);
722
723 if (num_smpls != (unsigned)len)
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000724 LOG(ERR) << "UHD: Sent fewer samples than requested";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000725
726 return num_smpls;
727}
728
729bool uhd_device::updateAlignment(TIMESTAMP timestamp)
730{
kurtis.heimerl13074c92011-11-26 03:17:43 +0000731 aligned = false;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000732 return true;
733}
734
735bool uhd_device::setTxFreq(double wFreq)
736{
737 uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000738 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000739 tx_freq = usrp_dev->get_tx_freq();
740
kurtis.heimerl965e7572011-11-26 03:16:54 +0000741 return true;
742}
743
744bool uhd_device::setRxFreq(double wFreq)
745{
746 uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000747 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000748 rx_freq = usrp_dev->get_rx_freq();
749
kurtis.heimerl965e7572011-11-26 03:16:54 +0000750 return true;
751}
752
753bool uhd_device::recv_async_msg()
754{
755 uhd::async_metadata_t metadata;
756 if (!usrp_dev->get_device()->recv_async_msg(metadata))
757 return false;
758
759 // Assume that any error requires resynchronization
760 if (metadata.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
761 aligned = false;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000762 LOG(ERR) << str_code(metadata);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000763 }
764
765 return true;
766}
767
768std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
769{
770 std::ostringstream ost("UHD: ");
771
772 switch (metadata.error_code) {
773 case uhd::rx_metadata_t::ERROR_CODE_NONE:
774 ost << "No error";
775 break;
776 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
777 ost << "No packet received, implementation timed-out";
778 break;
779 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
780 ost << "A stream command was issued in the past";
781 break;
782 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
783 ost << "Expected another stream command";
784 break;
785 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
786 ost << "An internal receive buffer has filled";
787 break;
788 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
789 ost << "The packet could not be parsed";
790 break;
791 default:
792 ost << "Unknown error " << metadata.error_code;
793 }
794
795 if (metadata.has_time_spec)
796 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
797
798 return ost.str();
799}
800
801std::string uhd_device::str_code(uhd::async_metadata_t metadata)
802{
803 std::ostringstream ost("UHD: ");
804
805 switch (metadata.event_code) {
806 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
807 ost << "A packet was successfully transmitted";
808 break;
809 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
810 ost << "An internal send buffer has emptied";
811 break;
812 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
813 ost << "Packet loss between host and device";
814 break;
815 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
816 ost << "Packet time was too late or too early";
817 break;
818 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
819 ost << "Underflow occurred inside a packet";
820 break;
821 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
822 ost << "Packet loss within a burst";
823 break;
824 default:
825 ost << "Unknown error " << metadata.event_code;
826 }
827
828 if (metadata.has_time_spec)
829 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
830
831 return ost.str();
832}
833
834smpl_buf::smpl_buf(size_t len, double rate)
835 : buf_len(len), clk_rt(rate),
836 time_start(0), time_end(0), data_start(0), data_end(0)
837{
838 data = new uint32_t[len];
839}
840
841smpl_buf::~smpl_buf()
842{
843 delete[] data;
844}
845
846ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
847{
848 if (timestamp < time_start)
849 return ERROR_TIMESTAMP;
850 else if (timestamp >= time_end)
851 return 0;
852 else
853 return time_end - timestamp;
854}
855
856ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
857{
858 return avail_smpls(convert_time(timespec, clk_rt));
859}
860
861ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
862{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000863 int type_sz = 2 * sizeof(short);
864
kurtis.heimerl965e7572011-11-26 03:16:54 +0000865 // Check for valid read
866 if (timestamp < time_start)
867 return ERROR_TIMESTAMP;
868 if (timestamp >= time_end)
869 return 0;
870 if (len >= buf_len)
871 return ERROR_READ;
872
873 // How many samples should be copied
874 size_t num_smpls = time_end - timestamp;
875 if (num_smpls > len);
876 num_smpls = len;
877
878 // Starting index
879 size_t read_start = data_start + (timestamp - time_start);
880
881 // Read it
882 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000883 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000884 memcpy(buf, data + read_start, numBytes);
885 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000886 size_t first_cp = (buf_len - read_start) * type_sz;
887 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000888
889 memcpy(buf, data + read_start, first_cp);
890 memcpy((char*) buf + first_cp, data, second_cp);
891 }
892
893 data_start = (read_start + len) % buf_len;
894 time_start = timestamp + len;
895
896 if (time_start > time_end)
897 return ERROR_READ;
898 else
899 return num_smpls;
900}
901
902ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
903{
904 return read(buf, len, convert_time(ts, clk_rt));
905}
906
907ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
908{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000909 int type_sz = 2 * sizeof(short);
910
kurtis.heimerl965e7572011-11-26 03:16:54 +0000911 // Check for valid write
912 if ((len == 0) || (len >= buf_len))
913 return ERROR_WRITE;
914 if ((timestamp + len) <= time_end)
915 return ERROR_TIMESTAMP;
916
917 // Starting index
918 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
919
920 // Write it
921 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000922 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000923 memcpy(data + write_start, buf, numBytes);
924 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000925 size_t first_cp = (buf_len - write_start) * type_sz;
926 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000927
928 memcpy(data + write_start, buf, first_cp);
929 memcpy(data, (char*) buf + first_cp, second_cp);
930 }
931
932 data_end = (write_start + len) % buf_len;
933 time_end = timestamp + len;
934
935 if (((write_start + len) > buf_len) && (data_end > data_start))
936 return ERROR_OVERFLOW;
937 else if (time_end <= time_start)
938 return ERROR_WRITE;
939 else
940 return len;
941}
942
943ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
944{
945 return write(buf, len, convert_time(ts, clk_rt));
946}
947
948std::string smpl_buf::str_status() const
949{
950 std::ostringstream ost("Sample buffer: ");
951
952 ost << "length = " << buf_len;
953 ost << ", time_start = " << time_start;
954 ost << ", time_end = " << time_end;
955 ost << ", data_start = " << data_start;
956 ost << ", data_end = " << data_end;
957
958 return ost.str();
959}
960
961std::string smpl_buf::str_code(ssize_t code)
962{
963 switch (code) {
964 case ERROR_TIMESTAMP:
965 return "Sample buffer: Requested timestamp is not valid";
966 case ERROR_READ:
967 return "Sample buffer: Read error";
968 case ERROR_WRITE:
969 return "Sample buffer: Write error";
970 case ERROR_OVERFLOW:
971 return "Sample buffer: Overrun";
972 default:
973 return "Sample buffer: Unknown error";
974 }
975}
976
977RadioDevice *RadioDevice::make(double smpl_rt, bool skip_rx)
978{
979 return new uhd_device(smpl_rt, skip_rx);
980}