blob: 89ca2a35bc3cf08b0825fbe84a0581f14de7ec2d [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
kurtis.heimerl119ca9c2011-11-26 03:18:26 +00003 *
4 * Copyright 2010,2011 Free Software Foundation, Inc.
Tom Tsou05c6feb2016-06-22 16:09:44 -07005 * Copyright (C) 2015 Ettus Research LLC
6 *
7 * Author: Tom Tsou <tom.tsou@ettus.com>
kurtis.heimerl119ca9c2011-11-26 03:18:26 +00008 *
9 * This program is free software: you can redistribute it and/or modify
10 * it under the terms of the GNU Affero General Public License as published by
11 * the Free Software Foundation, either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Affero General Public License for more details.
18 *
19 * You should have received a copy of the GNU Affero General Public License
20 * along with this program. If not, see <http://www.gnu.org/licenses/>.
21 * See the COPYING file in the main directory for details.
22 */
kurtis.heimerl965e7572011-11-26 03:16:54 +000023
24#include "radioDevice.h"
25#include "Threads.h"
26#include "Logger.h"
ttsoub371ed52012-01-09 18:11:34 +000027#include <uhd/version.hpp>
kurtis.heimerle380af32011-11-26 03:18:55 +000028#include <uhd/property_tree.hpp>
kurtis.heimerl856bbbe2011-12-15 00:50:16 +000029#include <uhd/usrp/multi_usrp.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000030#include <uhd/utils/thread_priority.hpp>
kurtis.heimerl0803ad92011-11-26 03:18:51 +000031#include <uhd/utils/msg.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000032
kurtis.heimerlce317332011-11-26 03:18:39 +000033#ifdef HAVE_CONFIG_H
34#include "config.h"
35#endif
36
Thomas Tsou635f1bf2014-02-13 14:34:10 -050037#define B2XX_CLK_RT 26e6
Tom Tsou76764272016-06-24 14:25:39 -070038#define B2XX_MCBTS_CLK_RT 3.2e6
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010039#define E1XX_CLK_RT 52e6
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -080040#define LIMESDR_CLK_RT (GSMRATE*32)
Thomas Tsoufe269fe2013-10-14 23:56:51 -040041#define B100_BASE_RT 400000
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040042#define USRP2_BASE_RT 390625
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +030043#define USRP_TX_AMPL 0.3
44#define UMTRX_TX_AMPL 0.7
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -080045#define LIMESDR_TX_AMPL 0.3
Thomas Tsoue3e88142013-04-05 20:42:41 -040046#define SAMPLE_BUF_SZ (1 << 20)
Thomas Tsou02d88d12013-04-05 15:36:30 -040047
Tom Tsoueb54bdd2014-11-25 16:06:32 -080048/*
49 * UHD timeout value on streaming (re)start
50 *
51 * Allow some time for streaming to commence after the start command is issued,
52 * but consider a wait beyond one second to be a definite error condition.
53 */
54#define UHD_RESTART_TIMEOUT 1.0
55
Alexander Chemeris4d029d82014-04-19 17:25:00 +040056/*
57 * UmTRX specific settings
58 */
59#define UMTRX_VGA1_DEF -18
60
Thomas Tsou02d88d12013-04-05 15:36:30 -040061enum uhd_dev_type {
62 USRP1,
63 USRP2,
64 B100,
Thomas Tsoue7882392014-02-13 14:46:23 -050065 B200,
66 B210,
Tom Tsou76764272016-06-24 14:25:39 -070067 B2XX_MCBTS,
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010068 E1XX,
Tom Tsou4ad9ea62014-12-03 18:47:20 -080069 E3XX,
70 X3XX,
Thomas Tsouc88d8d52013-08-21 17:55:54 -040071 UMTRX,
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -080072 LIMESDR,
Thomas Tsou02d88d12013-04-05 15:36:30 -040073 NUM_USRP_TYPES,
74};
kurtis.heimerlac0ee122011-11-28 06:25:58 +000075
Thomas Tsoue3e88142013-04-05 20:42:41 -040076struct uhd_dev_offset {
77 enum uhd_dev_type type;
Tom Tsou3f4a13f2016-05-03 19:02:24 -070078 size_t tx_sps;
79 size_t rx_sps;
Thomas Tsoue3e88142013-04-05 20:42:41 -040080 double offset;
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050081 const std::string desc;
Thomas Tsoue3e88142013-04-05 20:42:41 -040082};
83
kurtis.heimerl965e7572011-11-26 03:16:54 +000084/*
Tom Tsouc3129052015-08-21 18:28:52 -070085 * USRP version dependent device timings
86 */
87#ifdef USE_UHD_3_9
88#define B2XX_TIMING_1SPS 1.7153e-4
89#define B2XX_TIMING_4SPS 1.1696e-4
Tom Tsoud2b07032016-04-26 19:28:59 -070090#define B2XX_TIMING_4_4SPS 6.18462e-5
Tom Tsouc3129052015-08-21 18:28:52 -070091#else
92#define B2XX_TIMING_1SPS 9.9692e-5
93#define B2XX_TIMING_4SPS 6.9248e-5
Tom Tsoud2b07032016-04-26 19:28:59 -070094#define B2XX_TIMING_4_4SPS 4.52308e-5
Tom Tsouc3129052015-08-21 18:28:52 -070095#endif
96
97/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040098 * Tx / Rx sample offset values. In a perfect world, there is no group delay
99 * though analog components, and behaviour through digital filters exactly
100 * matches calculated values. In reality, there are unaccounted factors,
101 * which are captured in these empirically measured (using a loopback test)
102 * timing correction values.
103 *
104 * Notes:
105 * USRP1 with timestamps is not supported by UHD.
106 */
Alexander Chemeris871b8782016-03-20 20:08:57 +0300107static struct uhd_dev_offset uhd_offsets[] = {
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800108 { USRP1, 1, 1, 0.0, "USRP1 not supported" },
109 { USRP1, 4, 1, 0.0, "USRP1 not supported"},
110 { USRP2, 1, 1, 1.2184e-4, "N2XX 1 SPS" },
Tom Tsou8f0ccf62016-07-20 16:35:03 -0700111 { USRP2, 4, 1, 7.6547e-5, "N2XX 4/1 SPS" },
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800112 { B100, 1, 1, 1.2104e-4, "B100 1 SPS" },
113 { B100, 4, 1, 7.9307e-5, "B100 4 SPS" },
114 { B200, 1, 1, B2XX_TIMING_1SPS, "B200 1 SPS" },
Tom Tsou2e4ed102016-06-27 15:39:16 -0700115 { B200, 4, 1, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" },
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800116 { B210, 1, 1, B2XX_TIMING_1SPS, "B210 1 SPS" },
Tom Tsou2e4ed102016-06-27 15:39:16 -0700117 { B210, 4, 1, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" },
Tom Tsou76764272016-06-24 14:25:39 -0700118 { B2XX_MCBTS, 4, 4, 1.07188e-4, "B200/B210 4 SPS Multi-ARFCN" },
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800119 { E1XX, 1, 1, 9.5192e-5, "E1XX 1 SPS" },
Tom Tsou2e4ed102016-06-27 15:39:16 -0700120 { E1XX, 4, 1, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" },
Tom Tsoua5e0f1c2016-05-03 15:14:06 -0700121 { E3XX, 1, 1, 1.84616e-4, "E3XX 1 SPS" },
Tom Tsou2e4ed102016-06-27 15:39:16 -0700122 { E3XX, 4, 1, 1.29231e-4, "E3XX 4/1 Tx/Rx SPS" },
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800123 { X3XX, 1, 1, 1.5360e-4, "X3XX 1 SPS"},
Tom Tsou2e4ed102016-06-27 15:39:16 -0700124 { X3XX, 4, 1, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS"},
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800125 { UMTRX, 1, 1, 9.9692e-5, "UmTRX 1 SPS" },
Tom Tsou2e4ed102016-06-27 15:39:16 -0700126 { UMTRX, 4, 1, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS" },
Tom Tsou8f0ccf62016-07-20 16:35:03 -0700127 { USRP2, 4, 4, 4.6080e-5, "N2XX 4 SPS" },
Tom Tsou2e4ed102016-06-27 15:39:16 -0700128 { B210, 4, 4, B2XX_TIMING_4_4SPS, "B200/B210 4 SPS" },
129 { UMTRX, 4, 4, 5.1503e-5, "UmTRX 4 SPS" },
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800130 { LIMESDR, 4, 4, 16.5/GSMRATE, "STREAM/LimeSDR (4 SPS TX/RX)" },
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800131};
Alexander Chemeris871b8782016-03-20 20:08:57 +0300132#define NUM_UHD_OFFSETS (sizeof(uhd_offsets)/sizeof(uhd_offsets[0]))
kurtis.heimerl965e7572011-11-26 03:16:54 +0000133
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500134/*
135 * Offset handling for special cases. Currently used for UmTRX dual channel
136 * diversity receiver only.
137 */
138static struct uhd_dev_offset special_offsets[] = {
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800139 { UMTRX, 1, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
140 { UMTRX, 4, 1, 5.2103e-5, "UmTRX diversity, 4 SPS" },
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500141};
142
Tom Tsou3b093bb2016-05-03 19:04:15 -0700143
Thomas Tsoucb69f082013-04-08 14:18:26 -0400144/*
145 * Select sample rate based on device type and requested samples-per-symbol.
146 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
147 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800148 */
Tom Tsou05c6feb2016-06-22 16:09:44 -0700149static double select_rate(uhd_dev_type type, int sps,
150 RadioDevice::InterfaceType iface)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400151{
Tom Tsou05c6feb2016-06-22 16:09:44 -0700152 if (iface == RadioDevice::DIVERSITY) {
153 if (type == UMTRX)
154 return GSMRATE * 4;
155
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500156 LOG(ALERT) << "Diversity supported on UmTRX only";
157 return -9999.99;
158 }
159
Tom Tsou76764272016-06-24 14:25:39 -0700160
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800161 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400162 return -9999.99;
163
Tom Tsou76764272016-06-24 14:25:39 -0700164 if (iface == RadioDevice::MULTI_ARFCN) {
165 switch (type) {
166 case B2XX_MCBTS:
167 return 4 * MCBTS_SPACING;
168 default:
169 LOG(ALERT) << "Invalid device combination";
170 return -9999.99;
171 }
172 }
173
Thomas Tsoucb69f082013-04-08 14:18:26 -0400174 switch (type) {
175 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800176 case X3XX:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400177 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400178 case B100:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400179 return B100_BASE_RT * sps;
Thomas Tsoue7882392014-02-13 14:46:23 -0500180 case B200:
181 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100182 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800183 case E3XX:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400184 case UMTRX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800185 case LIMESDR:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400186 return GSMRATE * sps;
187 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400188 break;
189 }
190
191 LOG(ALERT) << "Unknown device type " << type;
192 return -9999.99;
193}
194
kurtis.heimerl965e7572011-11-26 03:16:54 +0000195/*
Alexander Chemeris1ba69e72016-06-18 10:50:11 +0300196 Sample Buffer - Allows reading and writing of timed samples using osmo-trx
kurtis.heimerl965e7572011-11-26 03:16:54 +0000197 or UHD style timestamps. Time conversions are handled
198 internally or accessable through the static convert calls.
199*/
200class smpl_buf {
201public:
202 /** Sample buffer constructor
203 @param len number of 32-bit samples the buffer should hold
204 @param rate sample clockrate
205 @param timestamp
206 */
207 smpl_buf(size_t len, double rate);
208 ~smpl_buf();
209
210 /** Query number of samples available for reading
211 @param timestamp time of first sample
212 @return number of available samples or error
213 */
214 ssize_t avail_smpls(TIMESTAMP timestamp) const;
215 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
216
217 /** Read and write
218 @param buf pointer to buffer
219 @param len number of samples desired to read or write
220 @param timestamp time of first stample
221 @return number of actual samples read or written or error
222 */
223 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
224 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
225 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
226 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
227
228 /** Buffer status string
229 @return a formatted string describing internal buffer state
230 */
Tom Tsou1ae25562014-12-05 12:56:34 -0800231 std::string str_status(size_t ts) const;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000232
233 /** Formatted error string
234 @param code an error code
235 @return a formatted error string
236 */
237 static std::string str_code(ssize_t code);
238
239 enum err_code {
240 ERROR_TIMESTAMP = -1,
241 ERROR_READ = -2,
242 ERROR_WRITE = -3,
243 ERROR_OVERFLOW = -4
244 };
245
246private:
247 uint32_t *data;
248 size_t buf_len;
249
250 double clk_rt;
251
252 TIMESTAMP time_start;
253 TIMESTAMP time_end;
254
255 size_t data_start;
256 size_t data_end;
257};
258
259/*
260 uhd_device - UHD implementation of the Device interface. Timestamped samples
261 are sent to and received from the device. An intermediate buffer
262 on the receive side collects and aligns packets of samples.
263 Events and errors such as underruns are reported asynchronously
264 by the device and received in a separate thread.
265*/
266class uhd_device : public RadioDevice {
267public:
Tom Tsou05c6feb2016-06-22 16:09:44 -0700268 uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
269 size_t chans, double offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000270 ~uhd_device();
271
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700272 int open(const std::string &args, int ref, bool swap_channels);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000273 bool start();
274 bool stop();
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800275 bool restart();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500276 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400277 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000278
Thomas Tsou204a9f12013-10-29 18:34:16 -0400279 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000280 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
281
Thomas Tsou204a9f12013-10-29 18:34:16 -0400282 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000283 TIMESTAMP timestamp, bool isControl);
284
285 bool updateAlignment(TIMESTAMP timestamp);
286
Thomas Tsou204a9f12013-10-29 18:34:16 -0400287 bool setTxFreq(double wFreq, size_t chan);
288 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000289
Tom Tsou05c6feb2016-06-22 16:09:44 -0700290 TIMESTAMP initialWriteTimestamp();
291 TIMESTAMP initialReadTimestamp();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000292
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +0300293 double fullScaleInputValue();
294 double fullScaleOutputValue();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000295
Thomas Tsou204a9f12013-10-29 18:34:16 -0400296 double setRxGain(double db, size_t chan);
297 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000298 double maxRxGain(void) { return rx_gain_max; }
299 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000300
Thomas Tsou204a9f12013-10-29 18:34:16 -0400301 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000302 double maxTxGain(void) { return tx_gain_max; }
303 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000304
Thomas Tsou204a9f12013-10-29 18:34:16 -0400305 double getTxFreq(size_t chan);
306 double getRxFreq(size_t chan);
307 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000308
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400309 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000310 inline double numberRead() { return rx_pkt_cnt; }
311 inline double numberWritten() { return 0; }
312
313 /** Receive and process asynchronous message
314 @return true if message received or false on timeout or error
315 */
316 bool recv_async_msg();
317
kurtis.heimerld4be0742011-11-26 03:17:46 +0000318 enum err_code {
319 ERROR_TIMING = -1,
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800320 ERROR_TIMEOUT = -2,
321 ERROR_UNRECOVERABLE = -3,
322 ERROR_UNHANDLED = -4,
kurtis.heimerld4be0742011-11-26 03:17:46 +0000323 };
324
kurtis.heimerl965e7572011-11-26 03:16:54 +0000325private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000326 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400327 uhd::tx_streamer::sptr tx_stream;
328 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400329 enum TxWindowType tx_window;
330 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000331
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800332 size_t tx_sps, rx_sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400333 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000334
Thomas Tsou204a9f12013-10-29 18:34:16 -0400335 double tx_gain_min, tx_gain_max;
336 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500337 double offset;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000338
Thomas Tsou204a9f12013-10-29 18:34:16 -0400339 std::vector<double> tx_gains, rx_gains;
340 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000341 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000342
343 bool started;
344 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000345
346 size_t rx_pkt_cnt;
347 size_t drop_cnt;
348 uhd::time_spec_t prev_ts;
349
Thomas Tsou18d3b832014-02-13 14:55:23 -0500350 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400351 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000352
kurtis.heimerl02d04052011-11-26 03:17:10 +0000353 void init_gains();
Tom Tsou05c6feb2016-06-22 16:09:44 -0700354 double get_dev_offset();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400355 int set_master_clk(double rate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400356 int set_rates(double tx_rate, double rx_rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000357 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000358 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000359 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000360
kurtis.heimerl965e7572011-11-26 03:16:54 +0000361 std::string str_code(uhd::rx_metadata_t metadata);
362 std::string str_code(uhd::async_metadata_t metadata);
363
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500364 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
365 bool set_freq(double freq, size_t chan, bool tx);
366
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800367 Thread *async_event_thrd;
Tom Tsou05c6feb2016-06-22 16:09:44 -0700368 InterfaceType iface;
Tom Tsou93b7f372014-12-15 20:23:33 -0800369 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000370};
371
372void *async_event_loop(uhd_device *dev)
373{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500374 dev->setPriority(0.43);
375
kurtis.heimerl965e7572011-11-26 03:16:54 +0000376 while (1) {
377 dev->recv_async_msg();
378 pthread_testcancel();
379 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500380
381 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000382}
383
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000384/*
385 Catch and drop underrun 'U' and overrun 'O' messages from stdout
386 since we already report using the logging facility. Direct
387 everything else appropriately.
388 */
389void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
390{
391 switch (type) {
392 case uhd::msg::status:
393 LOG(INFO) << msg;
394 break;
395 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000396 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000397 break;
398 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000399 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000400 break;
401 case uhd::msg::fastpath:
402 break;
403 }
404}
405
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800406static void thread_enable_cancel(bool cancel)
407{
408 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
409 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
410}
411
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800412uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
Tom Tsou05c6feb2016-06-22 16:09:44 -0700413 InterfaceType iface, size_t chans, double offset)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400414 : tx_gain_min(0.0), tx_gain_max(0.0),
415 rx_gain_min(0.0), rx_gain_max(0.0),
416 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000417 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou18d3b832014-02-13 14:55:23 -0500418 prev_ts(0,0), ts_initial(0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000419{
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800420 this->tx_sps = tx_sps;
421 this->rx_sps = rx_sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400422 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500423 this->offset = offset;
Tom Tsou05c6feb2016-06-22 16:09:44 -0700424 this->iface = iface;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000425}
426
427uhd_device::~uhd_device()
428{
429 stop();
430
Thomas Tsou204a9f12013-10-29 18:34:16 -0400431 for (size_t i = 0; i < rx_buffers.size(); i++)
432 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000433}
434
kurtis.heimerl24481de2011-11-26 03:17:18 +0000435void uhd_device::init_gains()
436{
437 uhd::gain_range_t range;
438
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400439 if (dev_type == UMTRX) {
440 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
441 if (gain_stages[0] == "VGA") {
442 LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
443 }
444 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
445 range = usrp_dev->get_tx_gain_range();
446 tx_gain_min = range.start();
447 tx_gain_max = range.stop();
448 } else {
449 range = usrp_dev->get_tx_gain_range("VGA2");
450 tx_gain_min = UMTRX_VGA1_DEF + range.start();
451 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
452 }
453 } else {
454 range = usrp_dev->get_tx_gain_range();
455 tx_gain_min = range.start();
456 tx_gain_max = range.stop();
457 }
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400458 LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000459
460 range = usrp_dev->get_rx_gain_range();
461 rx_gain_min = range.start();
462 rx_gain_max = range.stop();
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400463 LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000464
Thomas Tsou204a9f12013-10-29 18:34:16 -0400465 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400466 double gain = (tx_gain_min + tx_gain_max) / 2;
467 LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
468 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400469 tx_gains[i] = usrp_dev->get_tx_gain(i);
470 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000471
Thomas Tsou204a9f12013-10-29 18:34:16 -0400472 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400473 double gain = (rx_gain_min + rx_gain_max) / 2;
474 LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
475 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400476 rx_gains[i] = usrp_dev->get_rx_gain(i);
477 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000478
479 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400480
kurtis.heimerl24481de2011-11-26 03:17:18 +0000481}
482
Tom Tsou05c6feb2016-06-22 16:09:44 -0700483double uhd_device::get_dev_offset()
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700484{
485 struct uhd_dev_offset *offset = NULL;
486
487 /* Reject USRP1 */
488 if (dev_type == USRP1) {
489 LOG(ERR) << "Invalid device type";
490 return 0.0;
491 }
492
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700493 /* Special cases (e.g. diversity receiver) */
Tom Tsou05c6feb2016-06-22 16:09:44 -0700494 if (iface == DIVERSITY) {
495 if ((dev_type != UMTRX) || (rx_sps != 1)) {
496 LOG(ALERT) << "Unsupported device configuration";
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700497 return 0.0;
498 }
499
500 switch (tx_sps) {
501 case 1:
502 offset = &special_offsets[0];
503 break;
504 case 4:
505 default:
506 offset = &special_offsets[1];
507 }
508 } else {
509 /* Search for matching offset value */
510 for (size_t i = 0; i < NUM_UHD_OFFSETS; i++) {
511 if ((dev_type == uhd_offsets[i].type) &&
512 (tx_sps == uhd_offsets[i].tx_sps) &&
513 (rx_sps == uhd_offsets[i].rx_sps)) {
514 offset = &uhd_offsets[i];
515 break;
516 }
517 }
518 }
519
520 if (!offset) {
521 LOG(ERR) << "Invalid device configuration";
522 return 0.0;
523 }
524
525 std::cout << "-- Setting " << offset->desc << std::endl;
526
527 return offset->offset;
528}
529
Thomas Tsou02d88d12013-04-05 15:36:30 -0400530int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000531{
Thomas Tsou092f7572013-04-04 17:04:39 -0400532 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000533
Thomas Tsou7e068062013-04-08 19:39:37 -0400534 try {
535 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400536 } catch (const std::exception &ex) {
537 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
538 LOG(ALERT) << ex.what();
539 return -1;
540 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000541
Thomas Tsou092f7572013-04-04 17:04:39 -0400542 actual = usrp_dev->get_master_clock_rate();
543 offset = fabs(clk_rate - actual);
544
545 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000546 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400547 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400548 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400549 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000550 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400551
552 return 0;
553}
554
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400555int uhd_device::set_rates(double tx_rate, double rx_rate)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400556{
Thomas Tsou092f7572013-04-04 17:04:39 -0400557 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400558 double tx_offset, rx_offset;
559
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100560 /* B2XX and E1xx are the only device where we set FPGA clocking */
Tom Tsou283b22d2015-10-21 17:10:23 -0700561 if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400562 if (set_master_clk(B2XX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400563 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700564 } else if (dev_type == E1XX) {
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100565 if (set_master_clk(E1XX_CLK_RT) < 0)
566 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700567 } else if (dev_type == B2XX_MCBTS) {
568 if (set_master_clk(B2XX_MCBTS_CLK_RT) < 0)
569 return -1;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100570 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800571 else if (dev_type == LIMESDR) {
572 if (set_master_clk(LIMESDR_CLK_RT) < 0)
573 return -1;
574 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000575
Tom Tsou76764272016-06-24 14:25:39 -0700576
kurtis.heimerld3e25902011-11-26 03:18:13 +0000577 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400578 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400579 usrp_dev->set_tx_rate(tx_rate);
580 usrp_dev->set_rx_rate(rx_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400581 } catch (const std::exception &ex) {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400582 LOG(ALERT) << "UHD rate setting failed";
Thomas Tsou7e068062013-04-08 19:39:37 -0400583 LOG(ALERT) << ex.what();
584 return -1;
585 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400586 this->tx_rate = usrp_dev->get_tx_rate();
587 this->rx_rate = usrp_dev->get_rx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000588
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400589 tx_offset = fabs(this->tx_rate - tx_rate);
590 rx_offset = fabs(this->rx_rate - rx_rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400591 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000592 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400593 LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
594 << this->rx_rate << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400595 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000596 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000597
Thomas Tsou02d88d12013-04-05 15:36:30 -0400598 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000599}
600
Thomas Tsou204a9f12013-10-29 18:34:16 -0400601double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000602{
Tom Tsou76764272016-06-24 14:25:39 -0700603 if (iface == MULTI_ARFCN)
604 chan = 0;
605
Thomas Tsou204a9f12013-10-29 18:34:16 -0400606 if (chan >= tx_gains.size()) {
607 LOG(ALERT) << "Requested non-existent channel" << chan;
608 return 0.0f;
609 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000610
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400611 if (dev_type == UMTRX) {
612 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
613 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
614 usrp_dev->set_tx_gain(db, chan);
615 } else {
616 // New UHD versions support split configuration of
617 // Tx gain stages. We utilize this to set the gain
618 // configuration, optimal for the Tx signal quality.
619 // From our measurements, VGA1 must be 18dB plus-minus
620 // one and VGA2 is the best when 23dB or lower.
621 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
622 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
623 }
624 } else {
625 usrp_dev->set_tx_gain(db, chan);
626 }
627
Thomas Tsou204a9f12013-10-29 18:34:16 -0400628 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000629
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400630 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400631
632 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000633}
634
Thomas Tsou204a9f12013-10-29 18:34:16 -0400635double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000636{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400637 if (chan >= rx_gains.size()) {
638 LOG(ALERT) << "Requested non-existent channel " << chan;
639 return 0.0f;
640 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000641
Thomas Tsou204a9f12013-10-29 18:34:16 -0400642 usrp_dev->set_rx_gain(db, chan);
643 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000644
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400645 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400646
647 return rx_gains[chan];
648}
649
650double uhd_device::getRxGain(size_t chan)
651{
Tom Tsou76764272016-06-24 14:25:39 -0700652 if (iface == MULTI_ARFCN)
653 chan = 0;
654
Thomas Tsou204a9f12013-10-29 18:34:16 -0400655 if (chan >= rx_gains.size()) {
656 LOG(ALERT) << "Requested non-existent channel " << chan;
657 return 0.0f;
658 }
659
660 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000661}
662
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000663/*
664 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400665 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000666 deal with the transport latency. Reject the USRP1 because UHD doesn't
667 support timestamped samples with it.
668 */
669bool uhd_device::parse_dev_type()
670{
671 std::string mboard_str, dev_str;
672 uhd::property_tree::sptr prop_tree;
Tom Tsou3b093bb2016-05-03 19:04:15 -0700673 size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str, e3xx_str,
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800674 b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str, limesdr_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000675
676 prop_tree = usrp_dev->get_device()->get_tree();
677 dev_str = prop_tree->access<std::string>("/name").get();
678 mboard_str = usrp_dev->get_mboard_name();
679
680 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400681 usrp2_str = dev_str.find("USRP2");
682 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400683 b200_str = mboard_str.find("B200");
Thomas Tsou69d14c92013-10-11 14:27:35 -0400684 b210_str = mboard_str.find("B210");
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100685 e100_str = mboard_str.find("E100");
686 e110_str = mboard_str.find("E110");
Tom Tsou3b093bb2016-05-03 19:04:15 -0700687 e310_str = mboard_str.find("E310");
688 e3xx_str = mboard_str.find("E3XX");
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800689 x300_str = mboard_str.find("X300");
690 x310_str = mboard_str.find("X310");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400691 umtrx_str = dev_str.find("UmTRX");
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800692 // LimeSDR is based on STREAM board, so it's advertized as such
693 limesdr_str = dev_str.find("STREAM");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000694
695 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000696 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
697 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400698 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000699 return false;
700 }
701
Thomas Tsoucb69f082013-04-08 14:18:26 -0400702 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400703 tx_window = TX_WINDOW_USRP1;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400704 dev_type = B100;
Thomas Tsou092f7572013-04-04 17:04:39 -0400705 } else if (b200_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500706 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500707 dev_type = B200;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400708 } else if (b210_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500709 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500710 dev_type = B210;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100711 } else if (e100_str != std::string::npos) {
712 tx_window = TX_WINDOW_FIXED;
713 dev_type = E1XX;
714 } else if (e110_str != std::string::npos) {
715 tx_window = TX_WINDOW_FIXED;
716 dev_type = E1XX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400717 } else if (usrp2_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500718 tx_window = TX_WINDOW_FIXED;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400719 dev_type = USRP2;
Tom Tsou3b093bb2016-05-03 19:04:15 -0700720 } else if ((e310_str != std::string::npos) ||
721 (e3xx_str != std::string::npos)) {
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800722 tx_window = TX_WINDOW_FIXED;
723 dev_type = E3XX;
724 } else if (x300_str != std::string::npos) {
725 tx_window = TX_WINDOW_FIXED;
726 dev_type = X3XX;
727 } else if (x310_str != std::string::npos) {
728 tx_window = TX_WINDOW_FIXED;
729 dev_type = X3XX;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400730 } else if (umtrx_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500731 tx_window = TX_WINDOW_FIXED;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400732 dev_type = UMTRX;
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800733 } else if (limesdr_str != std::string::npos) {
734 tx_window = TX_WINDOW_USRP1;
735 dev_type = LIMESDR;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400736 } else {
Tom Tsoua5e0f1c2016-05-03 15:14:06 -0700737 LOG(ALERT) << "Unknown UHD device type "
738 << dev_str << " " << mboard_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400739 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000740 }
741
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500742 if (tx_window == TX_WINDOW_USRP1) {
743 LOG(INFO) << "Using USRP1 type transmit window for "
744 << dev_str << " " << mboard_str;
745 } else {
746 LOG(INFO) << "Using fixed transmit window for "
747 << dev_str << " " << mboard_str;
748 }
749
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000750 return true;
751}
752
Tom Tsou3b093bb2016-05-03 19:04:15 -0700753/*
754 * Check for UHD version > 3.9.0 for E3XX support
755 */
756static bool uhd_e3xx_version_chk()
757{
758 std::string ver = uhd::get_version_string();
759 std::string major_str(ver.begin(), ver.begin() + 3);
760 std::string minor_str(ver.begin() + 4, ver.begin() + 7);
761
762 int major_val = atoi(major_str.c_str());
763 int minor_val = atoi(minor_str.c_str());
764
765 if (major_val < 3)
766 return false;
767 if (minor_val < 9)
768 return false;
769
770 return true;
771}
772
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700773int uhd_device::open(const std::string &args, int ref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000774{
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700775 const char *refstr;
776
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000777 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000778 uhd::device_addr_t addr(args);
779 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000780 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000781 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400782 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000783 }
784
785 // Use the first found device
786 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000787 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700788 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000789 } catch(...) {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700790 LOG(ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400791 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000792 }
793
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000794 // Check for a valid device type and set bus type
795 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400796 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000797
Tom Tsou3b093bb2016-05-03 19:04:15 -0700798 if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
799 LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater";
800 return -1;
801 }
802
Thomas Tsou204a9f12013-10-29 18:34:16 -0400803 // Verify and set channels
Tom Tsou76764272016-06-24 14:25:39 -0700804 if (iface == MULTI_ARFCN) {
805 if ((dev_type != B200) && (dev_type != B210)) {
806 LOG(ALERT) << "Unsupported device configuration";
807 return -1;
808 }
809
810 dev_type = B2XX_MCBTS;
811 chans = 1;
812 } else if (chans == 2) {
813 if (dev_type == B210) {
814 } else if (dev_type == UMTRX) {
815 uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
816 usrp_dev->set_tx_subdev_spec(subdev_spec);
817 usrp_dev->set_rx_subdev_spec(subdev_spec);
818 } else {
819 LOG(ALERT) << "Invalid device configuration";
820 return -1;
821 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400822 } else if (chans != 1) {
823 LOG(ALERT) << "Invalid channel combination for device";
824 return -1;
825 }
826
827 tx_freqs.resize(chans);
828 rx_freqs.resize(chans);
829 tx_gains.resize(chans);
830 rx_gains.resize(chans);
831 rx_buffers.resize(chans);
832
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700833 switch (ref) {
834 case REF_INTERNAL:
835 refstr = "internal";
836 break;
837 case REF_EXTERNAL:
838 refstr = "external";
839 break;
840 case REF_GPS:
841 refstr = "gpsdo";
842 break;
843 default:
844 LOG(ALERT) << "Invalid reference type";
845 return -1;
846 }
847
848 usrp_dev->set_clock_source(refstr);
Thomas Tsou010fff72013-10-16 00:31:18 -0400849
kurtis.heimerl965e7572011-11-26 03:16:54 +0000850 // Set rates
Tom Tsou05c6feb2016-06-22 16:09:44 -0700851 double _rx_rate = select_rate(dev_type, rx_sps, iface);
852 double _tx_rate = select_rate(dev_type, tx_sps, iface);
853
854 if (iface == DIVERSITY)
855 _rx_rate = select_rate(dev_type, 1, iface);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500856
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500857 if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
858 return -1;
859 if (set_rates(_tx_rate, _rx_rate) < 0)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400860 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000861
Alexander Chemerise1714252015-04-28 23:09:02 -0400862 // Set RF frontend bandwidth
863 if (dev_type == UMTRX) {
864 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
865 for (size_t i = 0; i < chans; i++) {
866 usrp_dev->set_tx_bandwidth(500*1000*2, i);
Tom Tsou05c6feb2016-06-22 16:09:44 -0700867 if (iface != DIVERSITY)
Alexander Chemerise1714252015-04-28 23:09:02 -0400868 usrp_dev->set_rx_bandwidth(500*1000*2, i);
869 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800870 } else if (dev_type == LIMESDR) {
871 for (size_t i = 0; i < chans; i++) {
872 usrp_dev->set_tx_bandwidth(5e6, i);
873 usrp_dev->set_rx_bandwidth(5e6, i);
874 }
Alexander Chemerise1714252015-04-28 23:09:02 -0400875 }
876
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400877 /* Create TX and RX streamers */
878 uhd::stream_args_t stream_args("sc16");
879 for (size_t i = 0; i < chans; i++)
880 stream_args.channels.push_back(i);
881
882 tx_stream = usrp_dev->get_tx_stream(stream_args);
883 rx_stream = usrp_dev->get_rx_stream(stream_args);
884
885 /* Number of samples per over-the-wire packet */
886 tx_spp = tx_stream->get_max_num_samps();
887 rx_spp = rx_stream->get_max_num_samps();
888
kurtis.heimerl965e7572011-11-26 03:16:54 +0000889 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400890 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400891 for (size_t i = 0; i < rx_buffers.size(); i++)
892 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000893
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800894 // Set receive chain sample offset. Trigger the EDGE offset
895 // table by checking for 4 SPS on the receive path. No other
896 // configuration supports using 4 SPS.
Tom Tsou05c6feb2016-06-22 16:09:44 -0700897 double offset = get_dev_offset();
Thomas Tsoue3e88142013-04-05 20:42:41 -0400898 if (offset == 0.0) {
899 LOG(ERR) << "Unsupported configuration, no correction applied";
900 ts_offset = 0;
901 } else {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400902 ts_offset = (TIMESTAMP) (offset * rx_rate);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400903 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000904
kurtis.heimerl02d04052011-11-26 03:17:10 +0000905 // Initialize and shadow gain values
906 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000907
kurtis.heimerl965e7572011-11-26 03:16:54 +0000908 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000909 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000910
Tom Tsou05c6feb2016-06-22 16:09:44 -0700911 if (iface == DIVERSITY)
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500912 return DIVERSITY;
Tom Tsou76764272016-06-24 14:25:39 -0700913 if (iface == MULTI_ARFCN)
914 return MULTI_ARFCN;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500915
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400916 switch (dev_type) {
917 case B100:
918 return RESAMP_64M;
919 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800920 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400921 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500922 case B200:
923 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100924 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800925 case E3XX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800926 case LIMESDR:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500927 default:
928 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400929 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400930
931 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000932}
933
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000934bool uhd_device::flush_recv(size_t num_pkts)
935{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000936 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000937 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800938 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000939
Thomas Tsou18d3b832014-02-13 14:55:23 -0500940 std::vector<std::vector<short> >
941 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000942
Thomas Tsou18d3b832014-02-13 14:55:23 -0500943 std::vector<short *> pkt_ptrs;
944 for (size_t i = 0; i < pkt_bufs.size(); i++)
945 pkt_ptrs.push_back(&pkt_bufs[i].front());
946
947 ts_initial = 0;
948 while (!ts_initial || (num_pkts-- > 0)) {
949 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400950 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000951 if (!num_smpls) {
952 switch (md.error_code) {
953 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800954 LOG(ALERT) << "Device timed out";
955 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000956 default:
957 continue;
958 }
959 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500960
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700961 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000962 }
963
Thomas Tsou18d3b832014-02-13 14:55:23 -0500964 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
965
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000966 return true;
967}
968
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800969bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000970{
Thomas Tsouc2839162014-03-27 13:52:58 -0400971 /* Allow 100 ms delay to align multi-channel streams */
972 double delay = 0.1;
973
kurtis.heimerl68292102011-11-26 03:17:28 +0000974 aligned = false;
975
Thomas Tsouc2839162014-03-27 13:52:58 -0400976 uhd::time_spec_t current = usrp_dev->get_time_now();
977
Thomas Tsou204a9f12013-10-29 18:34:16 -0400978 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400979 cmd.stream_now = false;
980 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400981
kurtis.heimerl68292102011-11-26 03:17:28 +0000982 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500983
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800984 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000985}
986
kurtis.heimerl965e7572011-11-26 03:16:54 +0000987bool uhd_device::start()
988{
989 LOG(INFO) << "Starting USRP...";
990
991 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000992 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000993 return false;
994 }
995
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400996 // Register msg handler
997 uhd::msg::register_handler(&uhd_msg_handler);
998
kurtis.heimerl965e7572011-11-26 03:16:54 +0000999 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001000 async_event_thrd = new Thread();
1001 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001002
1003 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001004 if (!restart())
1005 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +00001006
kurtis.heimerl965e7572011-11-26 03:16:54 +00001007 // Display usrp time
1008 double time_now = usrp_dev->get_time_now().get_real_secs();
1009 LOG(INFO) << "The current time is " << time_now << " seconds";
1010
1011 started = true;
1012 return true;
1013}
1014
1015bool uhd_device::stop()
1016{
Thomas Tsou2c791102013-11-15 23:00:28 -05001017 if (!started)
1018 return false;
1019
1020 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +00001021 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
1022
1023 usrp_dev->issue_stream_cmd(stream_cmd);
1024
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001025 async_event_thrd->cancel();
1026 async_event_thrd->join();
1027 delete async_event_thrd;
1028
kurtis.heimerl965e7572011-11-26 03:16:54 +00001029 started = false;
1030 return true;
1031}
1032
Thomas Tsou7553aa92013-11-08 12:50:03 -05001033void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001034{
Thomas Tsou7553aa92013-11-08 12:50:03 -05001035 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001036 return;
1037}
1038
kurtis.heimerld4be0742011-11-26 03:17:46 +00001039int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001040{
kurtis.heimerl3cc1da92011-11-26 03:17:02 +00001041 uhd::time_spec_t ts;
1042
kurtis.heimerld4be0742011-11-26 03:17:46 +00001043 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001044 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +00001045
1046 switch (md.error_code) {
1047 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +00001048 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001049 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +00001050 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1051 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1052 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1053 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1054 default:
1055 return ERROR_UNHANDLED;
1056 }
1057 }
1058
kurtis.heimerl965e7572011-11-26 03:16:54 +00001059 // Missing timestamp
1060 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +00001061 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +00001062 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001063 }
1064
kurtis.heimerl3cc1da92011-11-26 03:17:02 +00001065 ts = md.time_spec;
1066
kurtis.heimerl965e7572011-11-26 03:16:54 +00001067 // Monotonicity check
kurtis.heimerl3cc1da92011-11-26 03:17:02 +00001068 if (ts < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +00001069 LOG(ALERT) << "UHD: Loss of monotonic time";
ttsou724eb362012-03-13 02:20:01 +00001070 LOG(ALERT) << "Current time: " << ts.get_real_secs() << ", "
1071 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +00001072 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001073 } else {
kurtis.heimerl3cc1da92011-11-26 03:17:02 +00001074 prev_ts = ts;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001075 }
1076
1077 return 0;
1078}
1079
Thomas Tsou204a9f12013-10-29 18:34:16 -04001080int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
1081 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001082{
1083 ssize_t rc;
1084 uhd::time_spec_t ts;
1085 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001086
Thomas Tsou204a9f12013-10-29 18:34:16 -04001087 if (bufs.size() != chans) {
1088 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1089 return -1;
1090 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001091
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001092 *overrun = false;
1093 *underrun = false;
1094
kurtis.heimerl965e7572011-11-26 03:16:54 +00001095 // Shift read time with respect to transmit clock
1096 timestamp += ts_offset;
1097
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001098 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001099 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001100
1101 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -04001102 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001103 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001104 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001105 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001106 return 0;
1107 }
1108
Thomas Tsou204a9f12013-10-29 18:34:16 -04001109 // Create vector buffer
1110 std::vector<std::vector<short> >
1111 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
1112
1113 std::vector<short *> pkt_ptrs;
1114 for (size_t i = 0; i < pkt_bufs.size(); i++)
1115 pkt_ptrs.push_back(&pkt_bufs[i].front());
1116
kurtis.heimerl965e7572011-11-26 03:16:54 +00001117 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -04001118 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001119 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001120 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
1121 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001122 thread_enable_cancel(true);
1123
kurtis.heimerl965e7572011-11-26 03:16:54 +00001124 rx_pkt_cnt++;
1125
kurtis.heimerld4be0742011-11-26 03:17:46 +00001126 // Check for errors
1127 rc = check_rx_md_err(metadata, num_smpls);
1128 switch (rc) {
1129 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +00001130 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
1131 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +00001132 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001133 case ERROR_TIMEOUT:
1134 // Assume stopping condition
1135 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +00001136 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -04001137 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +00001138 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +00001139 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001140 }
1141
kurtis.heimerl965e7572011-11-26 03:16:54 +00001142 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001143 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001144
Thomas Tsou204a9f12013-10-29 18:34:16 -04001145 for (size_t i = 0; i < rx_buffers.size(); i++) {
1146 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
1147 num_smpls,
1148 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001149
Thomas Tsou204a9f12013-10-29 18:34:16 -04001150 // Continue on local overrun, exit on other errors
1151 if ((rc < 0)) {
1152 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001153 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001154 if (rc != smpl_buf::ERROR_OVERFLOW)
1155 return 0;
1156 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001157 }
1158 }
1159
1160 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -04001161 for (size_t i = 0; i < rx_buffers.size(); i++) {
1162 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
1163 if ((rc < 0) || (rc != len)) {
1164 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001165 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001166 return 0;
1167 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001168 }
1169
1170 return len;
1171}
1172
Thomas Tsou204a9f12013-10-29 18:34:16 -04001173int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
1174 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001175{
1176 uhd::tx_metadata_t metadata;
1177 metadata.has_time_spec = true;
1178 metadata.start_of_burst = false;
1179 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001180 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001181
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001182 *underrun = false;
1183
kurtis.heimerl965e7572011-11-26 03:16:54 +00001184 // No control packets
1185 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001186 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001187 return 0;
1188 }
1189
Thomas Tsou204a9f12013-10-29 18:34:16 -04001190 if (bufs.size() != chans) {
1191 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1192 return -1;
1193 }
1194
kurtis.heimerl965e7572011-11-26 03:16:54 +00001195 // Drop a fixed number of packets (magic value)
1196 if (!aligned) {
1197 drop_cnt++;
1198
1199 if (drop_cnt == 1) {
1200 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001201 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001202 metadata.end_of_burst = true;
1203 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001204 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001205 return len;
1206 } else {
1207 LOG(DEBUG) << "Aligning transmitter: start burst";
1208 metadata.start_of_burst = true;
1209 aligned = true;
1210 drop_cnt = 0;
1211 }
1212 }
1213
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001214 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001215 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001216 thread_enable_cancel(true);
1217
ttsoub371ed52012-01-09 18:11:34 +00001218 if (num_smpls != (unsigned) len) {
1219 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001220 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001221
1222 return num_smpls;
1223}
1224
1225bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1226{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001227 return true;
1228}
1229
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001230uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1231{
1232 double rf_spread, rf_freq;
1233 std::vector<double> freqs;
1234 uhd::tune_request_t treq(freq);
1235
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001236 if (dev_type == UMTRX) {
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001237 if (offset != 0.0)
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001238 return uhd::tune_request_t(freq, offset);
1239
1240 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1241 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1242 // only distort the signal (because cordic is not ideal).
1243 treq.target_freq = freq;
1244 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1245 treq.rf_freq = freq;
1246 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1247 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001248 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001249 } else if (chans == 1) {
Thomas Tsou8e17df72014-03-06 14:16:11 -05001250 if (offset == 0.0)
1251 return treq;
1252
1253 return uhd::tune_request_t(freq, offset);
1254 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001255 LOG(ALERT) << chans << " channels unsupported";
1256 return treq;
1257 }
1258
1259 if (tx)
1260 freqs = tx_freqs;
1261 else
1262 freqs = rx_freqs;
1263
1264 /* Tune directly if other channel isn't tuned */
1265 if (freqs[!chan] < 10.0)
1266 return treq;
1267
1268 /* Find center frequency between channels */
1269 rf_spread = fabs(freqs[!chan] - freq);
1270 if (rf_spread > B2XX_CLK_RT) {
1271 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1272 return treq;
1273 }
1274
1275 rf_freq = (freqs[!chan] + freq) / 2.0f;
1276
1277 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1278 treq.target_freq = freq;
1279 treq.rf_freq = rf_freq;
1280
1281 return treq;
1282}
1283
1284bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1285{
1286 std::vector<double> freqs;
1287 uhd::tune_result_t tres;
1288 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1289
1290 if (tx) {
1291 tres = usrp_dev->set_tx_freq(treq, chan);
1292 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1293 } else {
1294 tres = usrp_dev->set_rx_freq(treq, chan);
1295 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1296 }
1297 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1298
Thomas Tsou8e17df72014-03-06 14:16:11 -05001299 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1300 return true;
1301
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001302 /* Manual RF policy means we intentionally tuned with a baseband
1303 * offset for dual-channel purposes. Now retune the other channel
1304 * with the opposite corresponding frequency offset
1305 */
1306 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1307 if (tx) {
1308 treq = select_freq(tx_freqs[!chan], !chan, true);
1309 tres = usrp_dev->set_tx_freq(treq, !chan);
1310 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1311 } else {
1312 treq = select_freq(rx_freqs[!chan], !chan, false);
1313 tres = usrp_dev->set_rx_freq(treq, !chan);
1314 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1315
1316 }
1317 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1318 }
1319
1320 return true;
1321}
1322
Thomas Tsou204a9f12013-10-29 18:34:16 -04001323bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001324{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001325 if (chan >= tx_freqs.size()) {
1326 LOG(ALERT) << "Requested non-existent channel " << chan;
1327 return false;
1328 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001329 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001330
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001331 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001332}
1333
Thomas Tsou204a9f12013-10-29 18:34:16 -04001334bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001335{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001336 if (chan >= rx_freqs.size()) {
1337 LOG(ALERT) << "Requested non-existent channel " << chan;
1338 return false;
1339 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001340 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001341
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001342 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001343}
1344
Thomas Tsou204a9f12013-10-29 18:34:16 -04001345double uhd_device::getTxFreq(size_t chan)
1346{
1347 if (chan >= tx_freqs.size()) {
1348 LOG(ALERT) << "Requested non-existent channel " << chan;
1349 return 0.0;
1350 }
1351
1352 return tx_freqs[chan];
1353}
1354
1355double uhd_device::getRxFreq(size_t chan)
1356{
1357 if (chan >= rx_freqs.size()) {
1358 LOG(ALERT) << "Requested non-existent channel " << chan;
1359 return 0.0;
1360 }
1361
1362 return rx_freqs[chan];
1363}
1364
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001365/*
1366 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1367 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1368 * combination.
1369 */
1370TIMESTAMP uhd_device::initialWriteTimestamp()
1371{
Tom Tsou76764272016-06-24 14:25:39 -07001372 if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001373 return ts_initial;
1374 else
1375 return ts_initial * tx_sps;
1376}
1377
1378TIMESTAMP uhd_device::initialReadTimestamp()
1379{
1380 return ts_initial;
1381}
1382
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001383double uhd_device::fullScaleInputValue()
1384{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001385 if (dev_type == LIMESDR)
1386 return (double) 2047 * LIMESDR_TX_AMPL;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001387 if (dev_type == UMTRX)
1388 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1389 else
1390 return (double) SHRT_MAX * USRP_TX_AMPL;
1391}
1392
1393double uhd_device::fullScaleOutputValue()
1394{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001395 if (dev_type == LIMESDR) return (double) 2047;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001396 return (double) SHRT_MAX;
1397}
1398
kurtis.heimerl965e7572011-11-26 03:16:54 +00001399bool uhd_device::recv_async_msg()
1400{
ttsou60dc4c92012-08-08 23:30:23 +00001401 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001402
1403 thread_enable_cancel(false);
1404 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1405 thread_enable_cancel(true);
1406 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001407 return false;
1408
1409 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001410 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001411 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001412
1413 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1414 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1415 LOG(ERR) << str_code(md);
1416 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001417 }
1418
1419 return true;
1420}
1421
1422std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1423{
1424 std::ostringstream ost("UHD: ");
1425
1426 switch (metadata.error_code) {
1427 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1428 ost << "No error";
1429 break;
1430 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1431 ost << "No packet received, implementation timed-out";
1432 break;
1433 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1434 ost << "A stream command was issued in the past";
1435 break;
1436 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1437 ost << "Expected another stream command";
1438 break;
1439 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1440 ost << "An internal receive buffer has filled";
1441 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001442 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1443 ost << "Multi-channel alignment failed";
1444 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001445 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1446 ost << "The packet could not be parsed";
1447 break;
1448 default:
1449 ost << "Unknown error " << metadata.error_code;
1450 }
1451
1452 if (metadata.has_time_spec)
1453 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1454
1455 return ost.str();
1456}
1457
1458std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1459{
1460 std::ostringstream ost("UHD: ");
1461
1462 switch (metadata.event_code) {
1463 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1464 ost << "A packet was successfully transmitted";
1465 break;
1466 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1467 ost << "An internal send buffer has emptied";
1468 break;
1469 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1470 ost << "Packet loss between host and device";
1471 break;
1472 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1473 ost << "Packet time was too late or too early";
1474 break;
1475 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1476 ost << "Underflow occurred inside a packet";
1477 break;
1478 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1479 ost << "Packet loss within a burst";
1480 break;
1481 default:
1482 ost << "Unknown error " << metadata.event_code;
1483 }
1484
1485 if (metadata.has_time_spec)
1486 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1487
1488 return ost.str();
1489}
1490
1491smpl_buf::smpl_buf(size_t len, double rate)
1492 : buf_len(len), clk_rt(rate),
1493 time_start(0), time_end(0), data_start(0), data_end(0)
1494{
1495 data = new uint32_t[len];
1496}
1497
1498smpl_buf::~smpl_buf()
1499{
1500 delete[] data;
1501}
1502
1503ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1504{
1505 if (timestamp < time_start)
1506 return ERROR_TIMESTAMP;
1507 else if (timestamp >= time_end)
1508 return 0;
1509 else
1510 return time_end - timestamp;
1511}
1512
1513ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1514{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001515 return avail_smpls(timespec.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001516}
1517
1518ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1519{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001520 int type_sz = 2 * sizeof(short);
1521
kurtis.heimerl965e7572011-11-26 03:16:54 +00001522 // Check for valid read
1523 if (timestamp < time_start)
1524 return ERROR_TIMESTAMP;
1525 if (timestamp >= time_end)
1526 return 0;
1527 if (len >= buf_len)
1528 return ERROR_READ;
1529
1530 // How many samples should be copied
1531 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001532 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001533 num_smpls = len;
1534
1535 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001536 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001537
1538 // Read it
1539 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001540 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001541 memcpy(buf, data + read_start, numBytes);
1542 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001543 size_t first_cp = (buf_len - read_start) * type_sz;
1544 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001545
1546 memcpy(buf, data + read_start, first_cp);
1547 memcpy((char*) buf + first_cp, data, second_cp);
1548 }
1549
1550 data_start = (read_start + len) % buf_len;
1551 time_start = timestamp + len;
1552
1553 if (time_start > time_end)
1554 return ERROR_READ;
1555 else
1556 return num_smpls;
1557}
1558
1559ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1560{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001561 return read(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001562}
1563
1564ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1565{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001566 int type_sz = 2 * sizeof(short);
1567
kurtis.heimerl965e7572011-11-26 03:16:54 +00001568 // Check for valid write
1569 if ((len == 0) || (len >= buf_len))
1570 return ERROR_WRITE;
1571 if ((timestamp + len) <= time_end)
1572 return ERROR_TIMESTAMP;
1573
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001574 if (timestamp < time_end) {
1575 LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001576 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1577 LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001578 // Do not return error here, because it's a rounding error and is not fatal
1579 }
1580 if (timestamp > time_end && time_end != 0) {
1581 LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001582 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1583 LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001584 // Do not return error here, because it's a rounding error and is not fatal
1585 }
1586
kurtis.heimerl965e7572011-11-26 03:16:54 +00001587 // Starting index
1588 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1589
1590 // Write it
1591 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001592 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001593 memcpy(data + write_start, buf, numBytes);
1594 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001595 size_t first_cp = (buf_len - write_start) * type_sz;
1596 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001597
1598 memcpy(data + write_start, buf, first_cp);
1599 memcpy(data, (char*) buf + first_cp, second_cp);
1600 }
1601
1602 data_end = (write_start + len) % buf_len;
1603 time_end = timestamp + len;
1604
Thomas Tsou18d3b832014-02-13 14:55:23 -05001605 if (!data_start)
1606 data_start = write_start;
1607
kurtis.heimerl965e7572011-11-26 03:16:54 +00001608 if (((write_start + len) > buf_len) && (data_end > data_start))
1609 return ERROR_OVERFLOW;
1610 else if (time_end <= time_start)
1611 return ERROR_WRITE;
1612 else
1613 return len;
1614}
1615
1616ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1617{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001618 return write(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001619}
1620
Tom Tsou1ae25562014-12-05 12:56:34 -08001621std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001622{
1623 std::ostringstream ost("Sample buffer: ");
1624
Tom Tsou1ae25562014-12-05 12:56:34 -08001625 ost << "timestamp = " << ts;
1626 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001627 ost << ", time_start = " << time_start;
1628 ost << ", time_end = " << time_end;
1629 ost << ", data_start = " << data_start;
1630 ost << ", data_end = " << data_end;
1631
1632 return ost.str();
1633}
1634
1635std::string smpl_buf::str_code(ssize_t code)
1636{
1637 switch (code) {
1638 case ERROR_TIMESTAMP:
1639 return "Sample buffer: Requested timestamp is not valid";
1640 case ERROR_READ:
1641 return "Sample buffer: Read error";
1642 case ERROR_WRITE:
1643 return "Sample buffer: Write error";
1644 case ERROR_OVERFLOW:
1645 return "Sample buffer: Overrun";
1646 default:
1647 return "Sample buffer: Unknown error";
1648 }
1649}
1650
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001651RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
Tom Tsou05c6feb2016-06-22 16:09:44 -07001652 InterfaceType iface, size_t chans, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001653{
Tom Tsou05c6feb2016-06-22 16:09:44 -07001654 return new uhd_device(tx_sps, rx_sps, iface, chans, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001655}