blob: 6ce3380bdd971095392dcfd750e3b1d0266325b8 [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
Thomas Tsoue3e88142013-04-05 20:42:41 -040035#define B100_CLK_RT 52e6
36#define TX_AMPL 0.3
37#define SAMPLE_BUF_SZ (1 << 20)
Thomas Tsou02d88d12013-04-05 15:36:30 -040038
39enum uhd_dev_type {
40 USRP1,
41 USRP2,
42 B100,
43 NUM_USRP_TYPES,
44};
kurtis.heimerlac0ee122011-11-28 06:25:58 +000045
Thomas Tsoue3e88142013-04-05 20:42:41 -040046struct uhd_dev_offset {
47 enum uhd_dev_type type;
48 int sps;
49 double offset;
50};
51
kurtis.heimerl965e7572011-11-26 03:16:54 +000052/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040053 * Tx / Rx sample offset values. In a perfect world, there is no group delay
54 * though analog components, and behaviour through digital filters exactly
55 * matches calculated values. In reality, there are unaccounted factors,
56 * which are captured in these empirically measured (using a loopback test)
57 * timing correction values.
58 *
59 * Notes:
60 * USRP1 with timestamps is not supported by UHD.
61 */
62static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 3] = {
63 { USRP1, 1, 0.0 },
64 { USRP1, 2, 0.0 },
65 { USRP1, 4, 0.0 },
66 { USRP2, 1, 5.4394e-5 },
67 { USRP2, 2, 0.0 },
68 { USRP2, 4, 0.0 },
69 { B100, 1, 9.4778e-5 },
70 { B100, 2, 5.1100e-5 },
71 { B100, 4, 2.9418e-5 },
72};
kurtis.heimerl965e7572011-11-26 03:16:54 +000073
Thomas Tsoue3e88142013-04-05 20:42:41 -040074static double get_dev_offset(enum uhd_dev_type type, int sps)
75{
76 if (type == USRP1) {
77 LOG(ERR) << "Invalid device type";
78 return 0.0;
79 }
kurtis.heimerl965e7572011-11-26 03:16:54 +000080
Thomas Tsoue3e88142013-04-05 20:42:41 -040081 switch (sps) {
82 case 1:
83 return uhd_offsets[3 * type + 0].offset;
84 case 2:
85 return uhd_offsets[3 * type + 1].offset;
86 case 4:
87 return uhd_offsets[3 * type + 2].offset;
88 }
kurtis.heimerl7ac54b12011-11-26 03:17:49 +000089
Thomas Tsoue3e88142013-04-05 20:42:41 -040090 LOG(ERR) << "Unsupported samples-per-symbols: " << sps;
91 return 0.0;
92}
kurtis.heimerlce317332011-11-26 03:18:39 +000093
kurtis.heimerl965e7572011-11-26 03:16:54 +000094/** Timestamp conversion
95 @param timestamp a UHD or OpenBTS timestamp
96 @param rate sample rate
97 @return the converted timestamp
98*/
99uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
100{
101 double secs = (double) ticks / rate;
102 return uhd::time_spec_t(secs);
103}
104
105TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
106{
kurtis.heimerlc7cb8172011-11-26 03:17:26 +0000107 TIMESTAMP ticks = ts.get_full_secs() * rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000108 return ts.get_tick_count(rate) + ticks;
109}
110
111/*
112 Sample Buffer - Allows reading and writing of timed samples using OpenBTS
113 or UHD style timestamps. Time conversions are handled
114 internally or accessable through the static convert calls.
115*/
116class smpl_buf {
117public:
118 /** Sample buffer constructor
119 @param len number of 32-bit samples the buffer should hold
120 @param rate sample clockrate
121 @param timestamp
122 */
123 smpl_buf(size_t len, double rate);
124 ~smpl_buf();
125
126 /** Query number of samples available for reading
127 @param timestamp time of first sample
128 @return number of available samples or error
129 */
130 ssize_t avail_smpls(TIMESTAMP timestamp) const;
131 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
132
133 /** Read and write
134 @param buf pointer to buffer
135 @param len number of samples desired to read or write
136 @param timestamp time of first stample
137 @return number of actual samples read or written or error
138 */
139 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
140 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
141 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
142 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
143
144 /** Buffer status string
145 @return a formatted string describing internal buffer state
146 */
147 std::string str_status() const;
148
149 /** Formatted error string
150 @param code an error code
151 @return a formatted error string
152 */
153 static std::string str_code(ssize_t code);
154
155 enum err_code {
156 ERROR_TIMESTAMP = -1,
157 ERROR_READ = -2,
158 ERROR_WRITE = -3,
159 ERROR_OVERFLOW = -4
160 };
161
162private:
163 uint32_t *data;
164 size_t buf_len;
165
166 double clk_rt;
167
168 TIMESTAMP time_start;
169 TIMESTAMP time_end;
170
171 size_t data_start;
172 size_t data_end;
173};
174
175/*
176 uhd_device - UHD implementation of the Device interface. Timestamped samples
177 are sent to and received from the device. An intermediate buffer
178 on the receive side collects and aligns packets of samples.
179 Events and errors such as underruns are reported asynchronously
180 by the device and received in a separate thread.
181*/
182class uhd_device : public RadioDevice {
183public:
Thomas Tsoue3e88142013-04-05 20:42:41 -0400184 uhd_device(double rate, int sps, bool skip_rx);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000185 ~uhd_device();
186
ttsouf60dafa2012-10-22 00:07:14 +0000187 bool open(const std::string &args);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000188 bool start();
189 bool stop();
kurtis.heimerl68292102011-11-26 03:17:28 +0000190 void restart(uhd::time_spec_t ts);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000191 void setPriority();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400192 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000193
194 int readSamples(short *buf, int len, bool *overrun,
195 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
196
197 int writeSamples(short *buf, int len, bool *underrun,
198 TIMESTAMP timestamp, bool isControl);
199
200 bool updateAlignment(TIMESTAMP timestamp);
201
202 bool setTxFreq(double wFreq);
203 bool setRxFreq(double wFreq);
204
205 inline TIMESTAMP initialWriteTimestamp() { return 0; }
206 inline TIMESTAMP initialReadTimestamp() { return 0; }
207
Thomas Tsoue3e88142013-04-05 20:42:41 -0400208 inline double fullScaleInputValue() { return 32000 * TX_AMPL; }
kurtis.heimerl7ac54b12011-11-26 03:17:49 +0000209 inline double fullScaleOutputValue() { return 32000; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000210
kurtis.heimerl02d04052011-11-26 03:17:10 +0000211 double setRxGain(double db);
212 double getRxGain(void) { return rx_gain; }
213 double maxRxGain(void) { return rx_gain_max; }
214 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000215
kurtis.heimerl02d04052011-11-26 03:17:10 +0000216 double setTxGain(double db);
217 double maxTxGain(void) { return tx_gain_max; }
218 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000219
kurtis.heimerl02d04052011-11-26 03:17:10 +0000220 double getTxFreq() { return tx_freq; }
221 double getRxFreq() { return rx_freq; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000222
223 inline double getSampleRate() { return actual_smpl_rt; }
224 inline double numberRead() { return rx_pkt_cnt; }
225 inline double numberWritten() { return 0; }
226
227 /** Receive and process asynchronous message
228 @return true if message received or false on timeout or error
229 */
230 bool recv_async_msg();
231
kurtis.heimerld4be0742011-11-26 03:17:46 +0000232 enum err_code {
233 ERROR_TIMING = -1,
234 ERROR_UNRECOVERABLE = -2,
235 ERROR_UNHANDLED = -3,
236 };
237
kurtis.heimerl965e7572011-11-26 03:16:54 +0000238private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000239 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400240 uhd::tx_streamer::sptr tx_stream;
241 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400242 enum TxWindowType tx_window;
243 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000244
Thomas Tsoue3e88142013-04-05 20:42:41 -0400245 int sps;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000246 double desired_smpl_rt, actual_smpl_rt;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000247
kurtis.heimerl02d04052011-11-26 03:17:10 +0000248 double tx_gain, tx_gain_min, tx_gain_max;
249 double rx_gain, rx_gain_min, rx_gain_max;
250
251 double tx_freq, rx_freq;
252 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000253
254 bool started;
255 bool aligned;
256 bool skip_rx;
257
258 size_t rx_pkt_cnt;
259 size_t drop_cnt;
260 uhd::time_spec_t prev_ts;
261
262 TIMESTAMP ts_offset;
263 smpl_buf *rx_smpl_buf;
264
kurtis.heimerl02d04052011-11-26 03:17:10 +0000265 void init_gains();
kurtis.heimerl24481de2011-11-26 03:17:18 +0000266 void set_ref_clk(bool ext_clk);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400267 int set_master_clk(double rate);
268 int set_rates(double rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000269 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000270 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000271 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000272
kurtis.heimerl965e7572011-11-26 03:16:54 +0000273 std::string str_code(uhd::rx_metadata_t metadata);
274 std::string str_code(uhd::async_metadata_t metadata);
275
276 Thread async_event_thrd;
277};
278
279void *async_event_loop(uhd_device *dev)
280{
281 while (1) {
282 dev->recv_async_msg();
283 pthread_testcancel();
284 }
285}
286
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000287/*
288 Catch and drop underrun 'U' and overrun 'O' messages from stdout
289 since we already report using the logging facility. Direct
290 everything else appropriately.
291 */
292void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
293{
294 switch (type) {
295 case uhd::msg::status:
296 LOG(INFO) << msg;
297 break;
298 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000299 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000300 break;
301 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000302 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000303 break;
304 case uhd::msg::fastpath:
305 break;
306 }
307}
308
Thomas Tsoue3e88142013-04-05 20:42:41 -0400309uhd_device::uhd_device(double rate, int sps, bool skip_rx)
kurtis.heimerl02d04052011-11-26 03:17:10 +0000310 : desired_smpl_rt(rate), actual_smpl_rt(0),
311 tx_gain(0.0), tx_gain_min(0.0), tx_gain_max(0.0),
312 rx_gain(0.0), rx_gain_min(0.0), rx_gain_max(0.0),
313 tx_freq(0.0), rx_freq(0.0), tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000314 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
kurtis.heimerl965e7572011-11-26 03:16:54 +0000315 prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
316{
Thomas Tsoue3e88142013-04-05 20:42:41 -0400317 this->sps = sps;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000318 this->skip_rx = skip_rx;
319}
320
321uhd_device::~uhd_device()
322{
323 stop();
324
325 if (rx_smpl_buf)
326 delete rx_smpl_buf;
327}
328
kurtis.heimerl24481de2011-11-26 03:17:18 +0000329void uhd_device::init_gains()
330{
331 uhd::gain_range_t range;
332
333 range = usrp_dev->get_tx_gain_range();
334 tx_gain_min = range.start();
335 tx_gain_max = range.stop();
336
337 range = usrp_dev->get_rx_gain_range();
338 rx_gain_min = range.start();
339 rx_gain_max = range.stop();
340
341 usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2);
342 usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2);
343
344 tx_gain = usrp_dev->get_tx_gain();
345 rx_gain = usrp_dev->get_rx_gain();
346
347 return;
348}
349
350void uhd_device::set_ref_clk(bool ext_clk)
351{
kurtis.heimerl24481de2011-11-26 03:17:18 +0000352 if (ext_clk)
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400353 usrp_dev->set_clock_source("external");
kurtis.heimerl24481de2011-11-26 03:17:18 +0000354
355 return;
356}
357
Thomas Tsou02d88d12013-04-05 15:36:30 -0400358int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000359{
Thomas Tsou02d88d12013-04-05 15:36:30 -0400360 double actual_clk_rt;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000361
kurtis.heimerld3e25902011-11-26 03:18:13 +0000362 // Set master clock rate
Thomas Tsou02d88d12013-04-05 15:36:30 -0400363 usrp_dev->set_master_clock_rate(clk_rate);
kurtis.heimerld3e25902011-11-26 03:18:13 +0000364 actual_clk_rt = usrp_dev->get_master_clock_rate();
365
Thomas Tsou02d88d12013-04-05 15:36:30 -0400366 if (actual_clk_rt != clk_rate) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000367 LOG(ALERT) << "Failed to set master clock rate";
368 LOG(ALERT) << "Actual clock rate " << actual_clk_rt;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400369 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000370 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400371
372 return 0;
373}
374
375int uhd_device::set_rates(double rate)
376{
377 // B100 is the only device where we set FPGA clocking
378 if (dev_type == B100) {
379 if (set_master_clk(B100_CLK_RT) < 0)
380 return -1;
381 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000382
383 // Set sample rates
kurtis.heimerl24481de2011-11-26 03:17:18 +0000384 usrp_dev->set_tx_rate(rate);
385 usrp_dev->set_rx_rate(rate);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400386 actual_smpl_rt = usrp_dev->get_tx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000387
Thomas Tsou02d88d12013-04-05 15:36:30 -0400388 if (actual_smpl_rt != rate) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000389 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400390 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000391 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400392 if (usrp_dev->get_rx_rate() != actual_smpl_rt) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000393 LOG(ALERT) << "Transmit and receive sample rates do not match";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000394 return -1.0;
395 }
396
Thomas Tsou02d88d12013-04-05 15:36:30 -0400397 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000398}
399
kurtis.heimerl02d04052011-11-26 03:17:10 +0000400double uhd_device::setTxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000401{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000402 usrp_dev->set_tx_gain(db);
403 tx_gain = usrp_dev->get_tx_gain();
404
405 LOG(INFO) << "Set TX gain to " << tx_gain << "dB";
406
407 return tx_gain;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000408}
409
kurtis.heimerl02d04052011-11-26 03:17:10 +0000410double uhd_device::setRxGain(double db)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000411{
kurtis.heimerl02d04052011-11-26 03:17:10 +0000412 usrp_dev->set_rx_gain(db);
413 rx_gain = usrp_dev->get_rx_gain();
414
415 LOG(INFO) << "Set RX gain to " << rx_gain << "dB";
416
417 return rx_gain;
418}
419
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000420/*
421 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400422 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000423 deal with the transport latency. Reject the USRP1 because UHD doesn't
424 support timestamped samples with it.
425 */
426bool uhd_device::parse_dev_type()
427{
428 std::string mboard_str, dev_str;
429 uhd::property_tree::sptr prop_tree;
430 size_t usrp1_str, usrp2_str, b100_str1, b100_str2;
431
432 prop_tree = usrp_dev->get_device()->get_tree();
433 dev_str = prop_tree->access<std::string>("/name").get();
434 mboard_str = usrp_dev->get_mboard_name();
435
436 usrp1_str = dev_str.find("USRP1");
437 b100_str1 = dev_str.find("B-Series");
438 b100_str2 = mboard_str.find("B100");
439
440 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000441 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
442 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400443 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000444 return false;
445 }
446
447 if ((b100_str1 != std::string::npos) || (b100_str2 != std::string::npos)) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400448 tx_window = TX_WINDOW_USRP1;
449 LOG(INFO) << "Using USRP1 type transmit window for "
450 << dev_str << " " << mboard_str;
451 dev_type = B100;
452 return true;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000453 } else {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400454 dev_type = USRP2;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000455 }
456
Thomas Tsou02d88d12013-04-05 15:36:30 -0400457 tx_window = TX_WINDOW_FIXED;
458 LOG(INFO) << "Using fixed transmit window for "
459 << dev_str << " " << mboard_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000460 return true;
461}
462
ttsouf60dafa2012-10-22 00:07:14 +0000463bool uhd_device::open(const std::string &args)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000464{
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000465 // Register msg handler
466 uhd::msg::register_handler(&uhd_msg_handler);
467
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000468 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000469 uhd::device_addr_t addr(args);
470 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000471 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000472 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000473 return false;
474 }
475
476 // Use the first found device
477 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000478 try {
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000479 usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
kurtis.heimerle380af32011-11-26 03:18:55 +0000480 } catch(...) {
ttsou3b5c0c12012-02-14 17:58:11 +0000481 LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000482 return false;
483 }
484
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000485 // Check for a valid device type and set bus type
486 if (!parse_dev_type())
487 return false;
488
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000489#ifdef EXTREF
490 set_ref_clk(true);
491#endif
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400492 // Create TX and RX streamers
493 uhd::stream_args_t stream_args("sc16");
494 tx_stream = usrp_dev->get_tx_stream(stream_args);
495 rx_stream = usrp_dev->get_rx_stream(stream_args);
kurtis.heimerlc6b9b702011-11-26 03:19:19 +0000496
kurtis.heimerl965e7572011-11-26 03:16:54 +0000497 // Number of samples per over-the-wire packet
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400498 tx_spp = tx_stream->get_max_num_samps();
499 rx_spp = rx_stream->get_max_num_samps();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000500
501 // Set rates
Thomas Tsou02d88d12013-04-05 15:36:30 -0400502 if (set_rates(desired_smpl_rt) < 0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000503 return false;
504
505 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400506 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000507 rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
508
509 // Set receive chain sample offset
Thomas Tsoue3e88142013-04-05 20:42:41 -0400510 double offset = get_dev_offset(dev_type, sps);
511 if (offset == 0.0) {
512 LOG(ERR) << "Unsupported configuration, no correction applied";
513 ts_offset = 0;
514 } else {
515 ts_offset = (TIMESTAMP) (offset * actual_smpl_rt);
516 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000517
kurtis.heimerl02d04052011-11-26 03:17:10 +0000518 // Initialize and shadow gain values
519 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000520
kurtis.heimerl965e7572011-11-26 03:16:54 +0000521 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000522 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000523
524 return true;
525}
526
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000527bool uhd_device::flush_recv(size_t num_pkts)
528{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000529 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000530 size_t num_smpls;
531 uint32_t buff[rx_spp];
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000532 float timeout;
533
534 // Use .01 sec instead of the default .1 sec
535 timeout = .01;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000536
537 for (size_t i = 0; i < num_pkts; i++) {
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400538 num_smpls = rx_stream->recv(buff, rx_spp, md,
539 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000540 if (!num_smpls) {
541 switch (md.error_code) {
542 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
543 return true;
544 default:
545 continue;
546 }
547 }
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000548 }
549
550 return true;
551}
552
kurtis.heimerl68292102011-11-26 03:17:28 +0000553void uhd_device::restart(uhd::time_spec_t ts)
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000554{
kurtis.heimerl68292102011-11-26 03:17:28 +0000555 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
556 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000557
kurtis.heimerl68292102011-11-26 03:17:28 +0000558 flush_recv(50);
559
560 usrp_dev->set_time_now(ts);
561 aligned = false;
562
563 cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
564 cmd.stream_now = true;
565 usrp_dev->issue_stream_cmd(cmd);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000566}
567
kurtis.heimerl965e7572011-11-26 03:16:54 +0000568bool uhd_device::start()
569{
570 LOG(INFO) << "Starting USRP...";
571
572 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000573 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000574 return false;
575 }
576
577 setPriority();
578
579 // Start asynchronous event (underrun check) loop
580 async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
581
582 // Start streaming
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000583 restart(uhd::time_spec_t(0.0));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000584
kurtis.heimerl965e7572011-11-26 03:16:54 +0000585 // Display usrp time
586 double time_now = usrp_dev->get_time_now().get_real_secs();
587 LOG(INFO) << "The current time is " << time_now << " seconds";
588
589 started = true;
590 return true;
591}
592
593bool uhd_device::stop()
594{
595 uhd::stream_cmd_t stream_cmd =
596 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
597
598 usrp_dev->issue_stream_cmd(stream_cmd);
599
600 started = false;
601 return true;
602}
603
604void uhd_device::setPriority()
605{
606 uhd::set_thread_priority_safe();
607 return;
608}
609
kurtis.heimerld4be0742011-11-26 03:17:46 +0000610int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000611{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000612 uhd::time_spec_t ts;
613
kurtis.heimerld4be0742011-11-26 03:17:46 +0000614 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000615 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000616
617 switch (md.error_code) {
618 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000619 LOG(ALERT) << "UHD: Receive timed out";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000620 return ERROR_UNRECOVERABLE;
621 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
622 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
623 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
624 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
625 default:
626 return ERROR_UNHANDLED;
627 }
628 }
629
kurtis.heimerl965e7572011-11-26 03:16:54 +0000630 // Missing timestamp
631 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000632 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000633 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000634 }
635
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000636 ts = md.time_spec;
637
kurtis.heimerl965e7572011-11-26 03:16:54 +0000638 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000639 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000640 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +0000641 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
642 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000643 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000644 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +0000645 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000646 }
647
648 return 0;
649}
650
651int uhd_device::readSamples(short *buf, int len, bool *overrun,
652 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
653{
654 ssize_t rc;
655 uhd::time_spec_t ts;
656 uhd::rx_metadata_t metadata;
657 uint32_t pkt_buf[rx_spp];
658
659 if (skip_rx)
660 return 0;
661
662 // Shift read time with respect to transmit clock
663 timestamp += ts_offset;
664
665 ts = convert_time(timestamp, actual_smpl_rt);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000666 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000667
668 // Check that timestamp is valid
669 rc = rx_smpl_buf->avail_smpls(timestamp);
670 if (rc < 0) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000671 LOG(ERR) << rx_smpl_buf->str_code(rc);
672 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000673 return 0;
674 }
675
676 // Receive samples from the usrp until we have enough
677 while (rx_smpl_buf->avail_smpls(timestamp) < len) {
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400678 size_t num_smpls = rx_stream->recv(
kurtis.heimerl965e7572011-11-26 03:16:54 +0000679 (void*)pkt_buf,
680 rx_spp,
681 metadata,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400682 0.1,
683 true);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000684 rx_pkt_cnt++;
685
kurtis.heimerld4be0742011-11-26 03:17:46 +0000686 // Check for errors
687 rc = check_rx_md_err(metadata, num_smpls);
688 switch (rc) {
689 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000690 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
691 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000692 exit(-1);
693 case ERROR_TIMING:
694 restart(prev_ts);
695 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000696 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000697 }
698
kurtis.heimerl965e7572011-11-26 03:16:54 +0000699
700 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000701 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000702
703 rc = rx_smpl_buf->write(pkt_buf,
704 num_smpls,
705 metadata.time_spec);
706
707 // Continue on local overrun, exit on other errors
708 if ((rc < 0)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000709 LOG(ERR) << rx_smpl_buf->str_code(rc);
710 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000711 if (rc != smpl_buf::ERROR_OVERFLOW)
712 return 0;
713 }
714 }
715
716 // We have enough samples
717 rc = rx_smpl_buf->read(buf, len, timestamp);
718 if ((rc < 0) || (rc != len)) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000719 LOG(ERR) << rx_smpl_buf->str_code(rc);
720 LOG(ERR) << rx_smpl_buf->str_status();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000721 return 0;
722 }
723
724 return len;
725}
726
727int uhd_device::writeSamples(short *buf, int len, bool *underrun,
728 unsigned long long timestamp,bool isControl)
729{
730 uhd::tx_metadata_t metadata;
731 metadata.has_time_spec = true;
732 metadata.start_of_burst = false;
733 metadata.end_of_burst = false;
734 metadata.time_spec = convert_time(timestamp, actual_smpl_rt);
735
736 // No control packets
737 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000738 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000739 return 0;
740 }
741
742 // Drop a fixed number of packets (magic value)
743 if (!aligned) {
744 drop_cnt++;
745
746 if (drop_cnt == 1) {
747 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000748 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000749 metadata.end_of_burst = true;
750 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000751 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000752 return len;
753 } else {
754 LOG(DEBUG) << "Aligning transmitter: start burst";
755 metadata.start_of_burst = true;
756 aligned = true;
757 drop_cnt = 0;
758 }
759 }
760
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400761 size_t num_smpls = tx_stream->send(buf, len, metadata);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000762
ttsoub371ed52012-01-09 18:11:34 +0000763 if (num_smpls != (unsigned) len) {
764 LOG(ALERT) << "UHD: Device send timed out";
765 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
766 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
767 exit(-1);
768 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000769
770 return num_smpls;
771}
772
773bool uhd_device::updateAlignment(TIMESTAMP timestamp)
774{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000775 return true;
776}
777
778bool uhd_device::setTxFreq(double wFreq)
779{
780 uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000781 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000782 tx_freq = usrp_dev->get_tx_freq();
783
kurtis.heimerl965e7572011-11-26 03:16:54 +0000784 return true;
785}
786
787bool uhd_device::setRxFreq(double wFreq)
788{
789 uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
kurtis.heimerl3998da72011-11-26 03:19:00 +0000790 LOG(INFO) << "\n" << tr.to_pp_string();
kurtis.heimerl02d04052011-11-26 03:17:10 +0000791 rx_freq = usrp_dev->get_rx_freq();
792
kurtis.heimerl965e7572011-11-26 03:16:54 +0000793 return true;
794}
795
796bool uhd_device::recv_async_msg()
797{
ttsou60dc4c92012-08-08 23:30:23 +0000798 uhd::async_metadata_t md;
799 if (!usrp_dev->get_device()->recv_async_msg(md))
kurtis.heimerl965e7572011-11-26 03:16:54 +0000800 return false;
801
802 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +0000803 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +0000804 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +0000805
806 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
807 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
808 LOG(ERR) << str_code(md);
809 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000810 }
811
812 return true;
813}
814
815std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
816{
817 std::ostringstream ost("UHD: ");
818
819 switch (metadata.error_code) {
820 case uhd::rx_metadata_t::ERROR_CODE_NONE:
821 ost << "No error";
822 break;
823 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
824 ost << "No packet received, implementation timed-out";
825 break;
826 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
827 ost << "A stream command was issued in the past";
828 break;
829 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
830 ost << "Expected another stream command";
831 break;
832 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
833 ost << "An internal receive buffer has filled";
834 break;
835 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
836 ost << "The packet could not be parsed";
837 break;
838 default:
839 ost << "Unknown error " << metadata.error_code;
840 }
841
842 if (metadata.has_time_spec)
843 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
844
845 return ost.str();
846}
847
848std::string uhd_device::str_code(uhd::async_metadata_t metadata)
849{
850 std::ostringstream ost("UHD: ");
851
852 switch (metadata.event_code) {
853 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
854 ost << "A packet was successfully transmitted";
855 break;
856 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
857 ost << "An internal send buffer has emptied";
858 break;
859 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
860 ost << "Packet loss between host and device";
861 break;
862 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
863 ost << "Packet time was too late or too early";
864 break;
865 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
866 ost << "Underflow occurred inside a packet";
867 break;
868 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
869 ost << "Packet loss within a burst";
870 break;
871 default:
872 ost << "Unknown error " << metadata.event_code;
873 }
874
875 if (metadata.has_time_spec)
876 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
877
878 return ost.str();
879}
880
881smpl_buf::smpl_buf(size_t len, double rate)
882 : buf_len(len), clk_rt(rate),
883 time_start(0), time_end(0), data_start(0), data_end(0)
884{
885 data = new uint32_t[len];
886}
887
888smpl_buf::~smpl_buf()
889{
890 delete[] data;
891}
892
893ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
894{
895 if (timestamp < time_start)
896 return ERROR_TIMESTAMP;
897 else if (timestamp >= time_end)
898 return 0;
899 else
900 return time_end - timestamp;
901}
902
903ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
904{
905 return avail_smpls(convert_time(timespec, clk_rt));
906}
907
908ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
909{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000910 int type_sz = 2 * sizeof(short);
911
kurtis.heimerl965e7572011-11-26 03:16:54 +0000912 // Check for valid read
913 if (timestamp < time_start)
914 return ERROR_TIMESTAMP;
915 if (timestamp >= time_end)
916 return 0;
917 if (len >= buf_len)
918 return ERROR_READ;
919
920 // How many samples should be copied
921 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +0000922 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000923 num_smpls = len;
924
925 // Starting index
926 size_t read_start = data_start + (timestamp - time_start);
927
928 // Read it
929 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000930 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000931 memcpy(buf, data + read_start, numBytes);
932 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000933 size_t first_cp = (buf_len - read_start) * type_sz;
934 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000935
936 memcpy(buf, data + read_start, first_cp);
937 memcpy((char*) buf + first_cp, data, second_cp);
938 }
939
940 data_start = (read_start + len) % buf_len;
941 time_start = timestamp + len;
942
943 if (time_start > time_end)
944 return ERROR_READ;
945 else
946 return num_smpls;
947}
948
949ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
950{
951 return read(buf, len, convert_time(ts, clk_rt));
952}
953
954ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
955{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000956 int type_sz = 2 * sizeof(short);
957
kurtis.heimerl965e7572011-11-26 03:16:54 +0000958 // Check for valid write
959 if ((len == 0) || (len >= buf_len))
960 return ERROR_WRITE;
961 if ((timestamp + len) <= time_end)
962 return ERROR_TIMESTAMP;
963
964 // Starting index
965 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
966
967 // Write it
968 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000969 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000970 memcpy(data + write_start, buf, numBytes);
971 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +0000972 size_t first_cp = (buf_len - write_start) * type_sz;
973 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000974
975 memcpy(data + write_start, buf, first_cp);
976 memcpy(data, (char*) buf + first_cp, second_cp);
977 }
978
979 data_end = (write_start + len) % buf_len;
980 time_end = timestamp + len;
981
982 if (((write_start + len) > buf_len) && (data_end > data_start))
983 return ERROR_OVERFLOW;
984 else if (time_end <= time_start)
985 return ERROR_WRITE;
986 else
987 return len;
988}
989
990ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
991{
992 return write(buf, len, convert_time(ts, clk_rt));
993}
994
995std::string smpl_buf::str_status() const
996{
997 std::ostringstream ost("Sample buffer: ");
998
999 ost << "length = " << buf_len;
1000 ost << ", time_start = " << time_start;
1001 ost << ", time_end = " << time_end;
1002 ost << ", data_start = " << data_start;
1003 ost << ", data_end = " << data_end;
1004
1005 return ost.str();
1006}
1007
1008std::string smpl_buf::str_code(ssize_t code)
1009{
1010 switch (code) {
1011 case ERROR_TIMESTAMP:
1012 return "Sample buffer: Requested timestamp is not valid";
1013 case ERROR_READ:
1014 return "Sample buffer: Read error";
1015 case ERROR_WRITE:
1016 return "Sample buffer: Write error";
1017 case ERROR_OVERFLOW:
1018 return "Sample buffer: Overrun";
1019 default:
1020 return "Sample buffer: Unknown error";
1021 }
1022}
1023
Thomas Tsoue3e88142013-04-05 20:42:41 -04001024RadioDevice *RadioDevice::make(double smpl_rt, int sps, bool skip_rx)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001025{
Thomas Tsoue3e88142013-04-05 20:42:41 -04001026 return new uhd_device(smpl_rt, sps, skip_rx);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001027}