blob: ce6d1beb87db38927873084f15f8f039c355bcaf [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>
31
kurtis.heimerlce317332011-11-26 03:18:39 +000032#ifdef HAVE_CONFIG_H
33#include "config.h"
34#endif
35
Tom Tsou72bf7622017-03-07 14:16:46 -080036#ifndef USE_UHD_3_11
37#include <uhd/utils/msg.hpp>
38#endif
39
Thomas Tsou635f1bf2014-02-13 14:34:10 -050040#define B2XX_CLK_RT 26e6
Tom Tsoua93f7892017-03-07 17:54:06 -080041#define B2XX_MCBTS_CLK_RT 51.2e6
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010042#define E1XX_CLK_RT 52e6
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -080043#define LIMESDR_CLK_RT (GSMRATE*32)
Thomas Tsoufe269fe2013-10-14 23:56:51 -040044#define B100_BASE_RT 400000
Thomas Tsou03e6ecf2013-08-20 20:54:54 -040045#define USRP2_BASE_RT 390625
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +030046#define USRP_TX_AMPL 0.3
47#define UMTRX_TX_AMPL 0.7
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -080048#define LIMESDR_TX_AMPL 0.3
Thomas Tsoue3e88142013-04-05 20:42:41 -040049#define SAMPLE_BUF_SZ (1 << 20)
Thomas Tsou02d88d12013-04-05 15:36:30 -040050
Tom Tsoueb54bdd2014-11-25 16:06:32 -080051/*
52 * UHD timeout value on streaming (re)start
53 *
54 * Allow some time for streaming to commence after the start command is issued,
55 * but consider a wait beyond one second to be a definite error condition.
56 */
57#define UHD_RESTART_TIMEOUT 1.0
58
Alexander Chemeris4d029d82014-04-19 17:25:00 +040059/*
60 * UmTRX specific settings
61 */
62#define UMTRX_VGA1_DEF -18
63
Thomas Tsou02d88d12013-04-05 15:36:30 -040064enum uhd_dev_type {
65 USRP1,
66 USRP2,
67 B100,
Thomas Tsoue7882392014-02-13 14:46:23 -050068 B200,
69 B210,
Tom Tsou76764272016-06-24 14:25:39 -070070 B2XX_MCBTS,
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010071 E1XX,
Tom Tsou4ad9ea62014-12-03 18:47:20 -080072 E3XX,
73 X3XX,
Thomas Tsouc88d8d52013-08-21 17:55:54 -040074 UMTRX,
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -080075 LIMESDR,
Thomas Tsou02d88d12013-04-05 15:36:30 -040076 NUM_USRP_TYPES,
77};
kurtis.heimerlac0ee122011-11-28 06:25:58 +000078
Thomas Tsoue3e88142013-04-05 20:42:41 -040079struct uhd_dev_offset {
80 enum uhd_dev_type type;
Tom Tsou3f4a13f2016-05-03 19:02:24 -070081 size_t tx_sps;
82 size_t rx_sps;
Thomas Tsoue3e88142013-04-05 20:42:41 -040083 double offset;
Thomas Tsou2c1f85a2013-11-13 22:53:15 -050084 const std::string desc;
Thomas Tsoue3e88142013-04-05 20:42:41 -040085};
86
kurtis.heimerl965e7572011-11-26 03:16:54 +000087/*
Tom Tsouc3129052015-08-21 18:28:52 -070088 * USRP version dependent device timings
89 */
Tom Tsou72bf7622017-03-07 14:16:46 -080090#if defined(USE_UHD_3_9) || defined(USE_UHD_3_11)
Tom Tsouc3129052015-08-21 18:28:52 -070091#define B2XX_TIMING_1SPS 1.7153e-4
92#define B2XX_TIMING_4SPS 1.1696e-4
Tom Tsoud2b07032016-04-26 19:28:59 -070093#define B2XX_TIMING_4_4SPS 6.18462e-5
Tom Tsoua93f7892017-03-07 17:54:06 -080094#define B2XX_TIMING_MCBTS 7e-5
Tom Tsou80cb0802017-01-19 13:44:02 -080095#else
96#define B2XX_TIMING_1SPS 9.9692e-5
97#define B2XX_TIMING_4SPS 6.9248e-5
98#define B2XX_TIMING_4_4SPS 4.52308e-5
Tom Tsoua93f7892017-03-07 17:54:06 -080099#define B2XX_TIMING_MCBTS 6.42452e-5
Tom Tsou80cb0802017-01-19 13:44:02 -0800100#endif
Tom Tsouc3129052015-08-21 18:28:52 -0700101
102/*
Thomas Tsoue3e88142013-04-05 20:42:41 -0400103 * Tx / Rx sample offset values. In a perfect world, there is no group delay
104 * though analog components, and behaviour through digital filters exactly
105 * matches calculated values. In reality, there are unaccounted factors,
106 * which are captured in these empirically measured (using a loopback test)
107 * timing correction values.
108 *
109 * Notes:
110 * USRP1 with timestamps is not supported by UHD.
111 */
Alexander Chemeris871b8782016-03-20 20:08:57 +0300112static struct uhd_dev_offset uhd_offsets[] = {
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800113 { USRP1, 1, 1, 0.0, "USRP1 not supported" },
114 { USRP1, 4, 1, 0.0, "USRP1 not supported"},
115 { USRP2, 1, 1, 1.2184e-4, "N2XX 1 SPS" },
Tom Tsou8f0ccf62016-07-20 16:35:03 -0700116 { USRP2, 4, 1, 7.6547e-5, "N2XX 4/1 SPS" },
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800117 { B100, 1, 1, 1.2104e-4, "B100 1 SPS" },
118 { B100, 4, 1, 7.9307e-5, "B100 4 SPS" },
119 { B200, 1, 1, B2XX_TIMING_1SPS, "B200 1 SPS" },
Tom Tsou2e4ed102016-06-27 15:39:16 -0700120 { B200, 4, 1, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" },
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800121 { B210, 1, 1, B2XX_TIMING_1SPS, "B210 1 SPS" },
Tom Tsou2e4ed102016-06-27 15:39:16 -0700122 { B210, 4, 1, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" },
Tom Tsoua93f7892017-03-07 17:54:06 -0800123 { B2XX_MCBTS, 4, 4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" },
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800124 { E1XX, 1, 1, 9.5192e-5, "E1XX 1 SPS" },
Tom Tsou2e4ed102016-06-27 15:39:16 -0700125 { E1XX, 4, 1, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" },
Tom Tsoua5e0f1c2016-05-03 15:14:06 -0700126 { E3XX, 1, 1, 1.84616e-4, "E3XX 1 SPS" },
Tom Tsou2e4ed102016-06-27 15:39:16 -0700127 { E3XX, 4, 1, 1.29231e-4, "E3XX 4/1 Tx/Rx SPS" },
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800128 { X3XX, 1, 1, 1.5360e-4, "X3XX 1 SPS"},
Tom Tsou2e4ed102016-06-27 15:39:16 -0700129 { X3XX, 4, 1, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS"},
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800130 { UMTRX, 1, 1, 9.9692e-5, "UmTRX 1 SPS" },
Tom Tsou2e4ed102016-06-27 15:39:16 -0700131 { UMTRX, 4, 1, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS" },
Tom Tsou8f0ccf62016-07-20 16:35:03 -0700132 { USRP2, 4, 4, 4.6080e-5, "N2XX 4 SPS" },
Tom Tsou43242ef2016-10-18 11:23:22 -0700133 { B200, 4, 4, B2XX_TIMING_4_4SPS, "B200 4 SPS" },
134 { B210, 4, 4, B2XX_TIMING_4_4SPS, "B210 4 SPS" },
Tom Tsou44c7f412017-01-11 13:30:42 -0800135 { X3XX, 4, 4, 5.6567e-5, "X3XX 4 SPS"},
Tom Tsou2e4ed102016-06-27 15:39:16 -0700136 { UMTRX, 4, 4, 5.1503e-5, "UmTRX 4 SPS" },
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800137 { LIMESDR, 4, 4, 16.5/GSMRATE, "STREAM/LimeSDR (4 SPS TX/RX)" },
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800138};
Alexander Chemeris871b8782016-03-20 20:08:57 +0300139#define NUM_UHD_OFFSETS (sizeof(uhd_offsets)/sizeof(uhd_offsets[0]))
kurtis.heimerl965e7572011-11-26 03:16:54 +0000140
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500141/*
Thomas Tsoucb69f082013-04-08 14:18:26 -0400142 * Select sample rate based on device type and requested samples-per-symbol.
143 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
144 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800145 */
Tom Tsou05c6feb2016-06-22 16:09:44 -0700146static double select_rate(uhd_dev_type type, int sps,
147 RadioDevice::InterfaceType iface)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400148{
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800149 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400150 return -9999.99;
151
Tom Tsou76764272016-06-24 14:25:39 -0700152 if (iface == RadioDevice::MULTI_ARFCN) {
153 switch (type) {
154 case B2XX_MCBTS:
155 return 4 * MCBTS_SPACING;
156 default:
157 LOG(ALERT) << "Invalid device combination";
158 return -9999.99;
159 }
160 }
161
Thomas Tsoucb69f082013-04-08 14:18:26 -0400162 switch (type) {
163 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800164 case X3XX:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400165 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400166 case B100:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400167 return B100_BASE_RT * sps;
Thomas Tsoue7882392014-02-13 14:46:23 -0500168 case B200:
169 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100170 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800171 case E3XX:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400172 case UMTRX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800173 case LIMESDR:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400174 return GSMRATE * sps;
175 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400176 break;
177 }
178
179 LOG(ALERT) << "Unknown device type " << type;
180 return -9999.99;
181}
182
kurtis.heimerl965e7572011-11-26 03:16:54 +0000183/*
Alexander Chemeris1ba69e72016-06-18 10:50:11 +0300184 Sample Buffer - Allows reading and writing of timed samples using osmo-trx
kurtis.heimerl965e7572011-11-26 03:16:54 +0000185 or UHD style timestamps. Time conversions are handled
186 internally or accessable through the static convert calls.
187*/
188class smpl_buf {
189public:
190 /** Sample buffer constructor
191 @param len number of 32-bit samples the buffer should hold
192 @param rate sample clockrate
193 @param timestamp
194 */
195 smpl_buf(size_t len, double rate);
196 ~smpl_buf();
197
198 /** Query number of samples available for reading
199 @param timestamp time of first sample
200 @return number of available samples or error
201 */
202 ssize_t avail_smpls(TIMESTAMP timestamp) const;
203 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
204
205 /** Read and write
206 @param buf pointer to buffer
207 @param len number of samples desired to read or write
208 @param timestamp time of first stample
209 @return number of actual samples read or written or error
210 */
211 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
212 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
213 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
214 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
215
216 /** Buffer status string
217 @return a formatted string describing internal buffer state
218 */
Tom Tsou1ae25562014-12-05 12:56:34 -0800219 std::string str_status(size_t ts) const;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000220
221 /** Formatted error string
222 @param code an error code
223 @return a formatted error string
224 */
225 static std::string str_code(ssize_t code);
226
227 enum err_code {
228 ERROR_TIMESTAMP = -1,
229 ERROR_READ = -2,
230 ERROR_WRITE = -3,
231 ERROR_OVERFLOW = -4
232 };
233
234private:
235 uint32_t *data;
236 size_t buf_len;
237
238 double clk_rt;
239
240 TIMESTAMP time_start;
241 TIMESTAMP time_end;
242
243 size_t data_start;
244 size_t data_end;
245};
246
247/*
248 uhd_device - UHD implementation of the Device interface. Timestamped samples
249 are sent to and received from the device. An intermediate buffer
250 on the receive side collects and aligns packets of samples.
251 Events and errors such as underruns are reported asynchronously
252 by the device and received in a separate thread.
253*/
254class uhd_device : public RadioDevice {
255public:
Tom Tsou05c6feb2016-06-22 16:09:44 -0700256 uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
257 size_t chans, double offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000258 ~uhd_device();
259
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700260 int open(const std::string &args, int ref, bool swap_channels);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000261 bool start();
262 bool stop();
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800263 bool restart();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500264 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400265 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000266
Thomas Tsou204a9f12013-10-29 18:34:16 -0400267 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000268 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
269
Thomas Tsou204a9f12013-10-29 18:34:16 -0400270 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000271 TIMESTAMP timestamp, bool isControl);
272
273 bool updateAlignment(TIMESTAMP timestamp);
274
Thomas Tsou204a9f12013-10-29 18:34:16 -0400275 bool setTxFreq(double wFreq, size_t chan);
276 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000277
Tom Tsou05c6feb2016-06-22 16:09:44 -0700278 TIMESTAMP initialWriteTimestamp();
279 TIMESTAMP initialReadTimestamp();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000280
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +0300281 double fullScaleInputValue();
282 double fullScaleOutputValue();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000283
Thomas Tsou204a9f12013-10-29 18:34:16 -0400284 double setRxGain(double db, size_t chan);
285 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000286 double maxRxGain(void) { return rx_gain_max; }
287 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000288
Thomas Tsou204a9f12013-10-29 18:34:16 -0400289 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000290 double maxTxGain(void) { return tx_gain_max; }
291 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000292
Thomas Tsou204a9f12013-10-29 18:34:16 -0400293 double getTxFreq(size_t chan);
294 double getRxFreq(size_t chan);
295 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000296
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400297 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000298 inline double numberRead() { return rx_pkt_cnt; }
299 inline double numberWritten() { return 0; }
300
301 /** Receive and process asynchronous message
302 @return true if message received or false on timeout or error
303 */
304 bool recv_async_msg();
305
kurtis.heimerld4be0742011-11-26 03:17:46 +0000306 enum err_code {
307 ERROR_TIMING = -1,
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800308 ERROR_TIMEOUT = -2,
309 ERROR_UNRECOVERABLE = -3,
310 ERROR_UNHANDLED = -4,
kurtis.heimerld4be0742011-11-26 03:17:46 +0000311 };
312
kurtis.heimerl965e7572011-11-26 03:16:54 +0000313private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000314 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400315 uhd::tx_streamer::sptr tx_stream;
316 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400317 enum TxWindowType tx_window;
318 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000319
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800320 size_t tx_sps, rx_sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400321 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000322
Thomas Tsou204a9f12013-10-29 18:34:16 -0400323 double tx_gain_min, tx_gain_max;
324 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500325 double offset;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000326
Thomas Tsou204a9f12013-10-29 18:34:16 -0400327 std::vector<double> tx_gains, rx_gains;
328 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000329 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000330
331 bool started;
332 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000333
334 size_t rx_pkt_cnt;
335 size_t drop_cnt;
336 uhd::time_spec_t prev_ts;
337
Thomas Tsou18d3b832014-02-13 14:55:23 -0500338 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400339 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000340
kurtis.heimerl02d04052011-11-26 03:17:10 +0000341 void init_gains();
Tom Tsou05c6feb2016-06-22 16:09:44 -0700342 double get_dev_offset();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400343 int set_master_clk(double rate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400344 int set_rates(double tx_rate, double rx_rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000345 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000346 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000347 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000348
kurtis.heimerl965e7572011-11-26 03:16:54 +0000349 std::string str_code(uhd::rx_metadata_t metadata);
350 std::string str_code(uhd::async_metadata_t metadata);
351
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500352 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
353 bool set_freq(double freq, size_t chan, bool tx);
354
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800355 Thread *async_event_thrd;
Tom Tsou05c6feb2016-06-22 16:09:44 -0700356 InterfaceType iface;
Tom Tsou93b7f372014-12-15 20:23:33 -0800357 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000358};
359
360void *async_event_loop(uhd_device *dev)
361{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500362 dev->setPriority(0.43);
363
kurtis.heimerl965e7572011-11-26 03:16:54 +0000364 while (1) {
365 dev->recv_async_msg();
366 pthread_testcancel();
367 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500368
369 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000370}
371
Tom Tsou72bf7622017-03-07 14:16:46 -0800372#ifndef USE_UHD_3_11
373/*
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000374 Catch and drop underrun 'U' and overrun 'O' messages from stdout
375 since we already report using the logging facility. Direct
376 everything else appropriately.
377 */
378void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
379{
380 switch (type) {
381 case uhd::msg::status:
382 LOG(INFO) << msg;
383 break;
384 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000385 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000386 break;
387 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000388 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000389 break;
390 case uhd::msg::fastpath:
391 break;
392 }
393}
Tom Tsou72bf7622017-03-07 14:16:46 -0800394#endif
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000395
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800396static void thread_enable_cancel(bool cancel)
397{
398 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
399 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
400}
401
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800402uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
Tom Tsou05c6feb2016-06-22 16:09:44 -0700403 InterfaceType iface, size_t chans, double offset)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400404 : tx_gain_min(0.0), tx_gain_max(0.0),
405 rx_gain_min(0.0), rx_gain_max(0.0),
406 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000407 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou18d3b832014-02-13 14:55:23 -0500408 prev_ts(0,0), ts_initial(0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000409{
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800410 this->tx_sps = tx_sps;
411 this->rx_sps = rx_sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400412 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500413 this->offset = offset;
Tom Tsou05c6feb2016-06-22 16:09:44 -0700414 this->iface = iface;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000415}
416
417uhd_device::~uhd_device()
418{
419 stop();
420
Thomas Tsou204a9f12013-10-29 18:34:16 -0400421 for (size_t i = 0; i < rx_buffers.size(); i++)
422 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000423}
424
kurtis.heimerl24481de2011-11-26 03:17:18 +0000425void uhd_device::init_gains()
426{
427 uhd::gain_range_t range;
428
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400429 if (dev_type == UMTRX) {
430 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
431 if (gain_stages[0] == "VGA") {
432 LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
433 }
434 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
435 range = usrp_dev->get_tx_gain_range();
436 tx_gain_min = range.start();
437 tx_gain_max = range.stop();
438 } else {
439 range = usrp_dev->get_tx_gain_range("VGA2");
440 tx_gain_min = UMTRX_VGA1_DEF + range.start();
441 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
442 }
443 } else {
444 range = usrp_dev->get_tx_gain_range();
445 tx_gain_min = range.start();
446 tx_gain_max = range.stop();
447 }
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400448 LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000449
450 range = usrp_dev->get_rx_gain_range();
451 rx_gain_min = range.start();
452 rx_gain_max = range.stop();
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400453 LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000454
Thomas Tsou204a9f12013-10-29 18:34:16 -0400455 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400456 double gain = (tx_gain_min + tx_gain_max) / 2;
457 LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
458 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400459 tx_gains[i] = usrp_dev->get_tx_gain(i);
460 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000461
Thomas Tsou204a9f12013-10-29 18:34:16 -0400462 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400463 double gain = (rx_gain_min + rx_gain_max) / 2;
464 LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
465 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400466 rx_gains[i] = usrp_dev->get_rx_gain(i);
467 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000468
469 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400470
kurtis.heimerl24481de2011-11-26 03:17:18 +0000471}
472
Tom Tsou05c6feb2016-06-22 16:09:44 -0700473double uhd_device::get_dev_offset()
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700474{
475 struct uhd_dev_offset *offset = NULL;
476
477 /* Reject USRP1 */
478 if (dev_type == USRP1) {
479 LOG(ERR) << "Invalid device type";
480 return 0.0;
481 }
482
Tom Tsoud6ae8642017-03-30 17:22:58 -0700483 /* Search for matching offset value */
484 for (size_t i = 0; i < NUM_UHD_OFFSETS; i++) {
485 if ((dev_type == uhd_offsets[i].type) &&
486 (tx_sps == uhd_offsets[i].tx_sps) &&
487 (rx_sps == uhd_offsets[i].rx_sps)) {
488 offset = &uhd_offsets[i];
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700489 break;
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700490 }
491 }
492
493 if (!offset) {
494 LOG(ERR) << "Invalid device configuration";
495 return 0.0;
496 }
497
498 std::cout << "-- Setting " << offset->desc << std::endl;
499
500 return offset->offset;
501}
502
Thomas Tsou02d88d12013-04-05 15:36:30 -0400503int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000504{
Thomas Tsou092f7572013-04-04 17:04:39 -0400505 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000506
Thomas Tsou7e068062013-04-08 19:39:37 -0400507 try {
508 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400509 } catch (const std::exception &ex) {
510 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
511 LOG(ALERT) << ex.what();
512 return -1;
513 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000514
Thomas Tsou092f7572013-04-04 17:04:39 -0400515 actual = usrp_dev->get_master_clock_rate();
516 offset = fabs(clk_rate - actual);
517
518 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000519 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400520 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400521 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400522 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000523 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400524
525 return 0;
526}
527
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400528int uhd_device::set_rates(double tx_rate, double rx_rate)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400529{
Thomas Tsou092f7572013-04-04 17:04:39 -0400530 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400531 double tx_offset, rx_offset;
532
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100533 /* B2XX and E1xx are the only device where we set FPGA clocking */
Tom Tsou283b22d2015-10-21 17:10:23 -0700534 if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400535 if (set_master_clk(B2XX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400536 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700537 } else if (dev_type == E1XX) {
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100538 if (set_master_clk(E1XX_CLK_RT) < 0)
539 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700540 } else if (dev_type == B2XX_MCBTS) {
541 if (set_master_clk(B2XX_MCBTS_CLK_RT) < 0)
542 return -1;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100543 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800544 else if (dev_type == LIMESDR) {
545 if (set_master_clk(LIMESDR_CLK_RT) < 0)
546 return -1;
547 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000548
Tom Tsou76764272016-06-24 14:25:39 -0700549
kurtis.heimerld3e25902011-11-26 03:18:13 +0000550 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400551 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400552 usrp_dev->set_tx_rate(tx_rate);
553 usrp_dev->set_rx_rate(rx_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400554 } catch (const std::exception &ex) {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400555 LOG(ALERT) << "UHD rate setting failed";
Thomas Tsou7e068062013-04-08 19:39:37 -0400556 LOG(ALERT) << ex.what();
557 return -1;
558 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400559 this->tx_rate = usrp_dev->get_tx_rate();
560 this->rx_rate = usrp_dev->get_rx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000561
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400562 tx_offset = fabs(this->tx_rate - tx_rate);
563 rx_offset = fabs(this->rx_rate - rx_rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400564 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000565 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400566 LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
567 << this->rx_rate << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400568 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000569 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000570
Thomas Tsou02d88d12013-04-05 15:36:30 -0400571 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000572}
573
Thomas Tsou204a9f12013-10-29 18:34:16 -0400574double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000575{
Tom Tsou76764272016-06-24 14:25:39 -0700576 if (iface == MULTI_ARFCN)
577 chan = 0;
578
Thomas Tsou204a9f12013-10-29 18:34:16 -0400579 if (chan >= tx_gains.size()) {
580 LOG(ALERT) << "Requested non-existent channel" << chan;
581 return 0.0f;
582 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000583
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400584 if (dev_type == UMTRX) {
585 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
586 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
587 usrp_dev->set_tx_gain(db, chan);
588 } else {
589 // New UHD versions support split configuration of
590 // Tx gain stages. We utilize this to set the gain
591 // configuration, optimal for the Tx signal quality.
592 // From our measurements, VGA1 must be 18dB plus-minus
593 // one and VGA2 is the best when 23dB or lower.
594 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
595 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
596 }
597 } else {
598 usrp_dev->set_tx_gain(db, chan);
599 }
600
Thomas Tsou204a9f12013-10-29 18:34:16 -0400601 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000602
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400603 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400604
605 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000606}
607
Thomas Tsou204a9f12013-10-29 18:34:16 -0400608double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000609{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400610 if (chan >= rx_gains.size()) {
611 LOG(ALERT) << "Requested non-existent channel " << chan;
612 return 0.0f;
613 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000614
Thomas Tsou204a9f12013-10-29 18:34:16 -0400615 usrp_dev->set_rx_gain(db, chan);
616 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000617
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400618 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400619
620 return rx_gains[chan];
621}
622
623double uhd_device::getRxGain(size_t chan)
624{
Tom Tsou76764272016-06-24 14:25:39 -0700625 if (iface == MULTI_ARFCN)
626 chan = 0;
627
Thomas Tsou204a9f12013-10-29 18:34:16 -0400628 if (chan >= rx_gains.size()) {
629 LOG(ALERT) << "Requested non-existent channel " << chan;
630 return 0.0f;
631 }
632
633 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000634}
635
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000636/*
637 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400638 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000639 deal with the transport latency. Reject the USRP1 because UHD doesn't
640 support timestamped samples with it.
641 */
642bool uhd_device::parse_dev_type()
643{
644 std::string mboard_str, dev_str;
645 uhd::property_tree::sptr prop_tree;
Tom Tsou3b093bb2016-05-03 19:04:15 -0700646 size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str, e3xx_str,
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800647 b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str, limesdr_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000648
649 prop_tree = usrp_dev->get_device()->get_tree();
650 dev_str = prop_tree->access<std::string>("/name").get();
651 mboard_str = usrp_dev->get_mboard_name();
652
653 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400654 usrp2_str = dev_str.find("USRP2");
655 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400656 b200_str = mboard_str.find("B200");
Thomas Tsou69d14c92013-10-11 14:27:35 -0400657 b210_str = mboard_str.find("B210");
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100658 e100_str = mboard_str.find("E100");
659 e110_str = mboard_str.find("E110");
Tom Tsou3b093bb2016-05-03 19:04:15 -0700660 e310_str = mboard_str.find("E310");
661 e3xx_str = mboard_str.find("E3XX");
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800662 x300_str = mboard_str.find("X300");
663 x310_str = mboard_str.find("X310");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400664 umtrx_str = dev_str.find("UmTRX");
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800665 // LimeSDR is based on STREAM board, so it's advertized as such
666 limesdr_str = dev_str.find("STREAM");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000667
668 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000669 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
670 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400671 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000672 return false;
673 }
674
Thomas Tsoucb69f082013-04-08 14:18:26 -0400675 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400676 tx_window = TX_WINDOW_USRP1;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400677 dev_type = B100;
Thomas Tsou092f7572013-04-04 17:04:39 -0400678 } else if (b200_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500679 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500680 dev_type = B200;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400681 } else if (b210_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500682 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500683 dev_type = B210;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100684 } else if (e100_str != std::string::npos) {
685 tx_window = TX_WINDOW_FIXED;
686 dev_type = E1XX;
687 } else if (e110_str != std::string::npos) {
688 tx_window = TX_WINDOW_FIXED;
689 dev_type = E1XX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400690 } else if (usrp2_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500691 tx_window = TX_WINDOW_FIXED;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400692 dev_type = USRP2;
Tom Tsou3b093bb2016-05-03 19:04:15 -0700693 } else if ((e310_str != std::string::npos) ||
694 (e3xx_str != std::string::npos)) {
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800695 tx_window = TX_WINDOW_FIXED;
696 dev_type = E3XX;
697 } else if (x300_str != std::string::npos) {
698 tx_window = TX_WINDOW_FIXED;
699 dev_type = X3XX;
700 } else if (x310_str != std::string::npos) {
701 tx_window = TX_WINDOW_FIXED;
702 dev_type = X3XX;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400703 } else if (umtrx_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500704 tx_window = TX_WINDOW_FIXED;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400705 dev_type = UMTRX;
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800706 } else if (limesdr_str != std::string::npos) {
707 tx_window = TX_WINDOW_USRP1;
708 dev_type = LIMESDR;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400709 } else {
Tom Tsoua5e0f1c2016-05-03 15:14:06 -0700710 LOG(ALERT) << "Unknown UHD device type "
711 << dev_str << " " << mboard_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400712 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000713 }
714
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500715 if (tx_window == TX_WINDOW_USRP1) {
716 LOG(INFO) << "Using USRP1 type transmit window for "
717 << dev_str << " " << mboard_str;
718 } else {
719 LOG(INFO) << "Using fixed transmit window for "
720 << dev_str << " " << mboard_str;
721 }
722
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000723 return true;
724}
725
Tom Tsou3b093bb2016-05-03 19:04:15 -0700726/*
727 * Check for UHD version > 3.9.0 for E3XX support
728 */
729static bool uhd_e3xx_version_chk()
730{
731 std::string ver = uhd::get_version_string();
732 std::string major_str(ver.begin(), ver.begin() + 3);
733 std::string minor_str(ver.begin() + 4, ver.begin() + 7);
734
735 int major_val = atoi(major_str.c_str());
736 int minor_val = atoi(minor_str.c_str());
737
738 if (major_val < 3)
739 return false;
740 if (minor_val < 9)
741 return false;
742
743 return true;
744}
745
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700746int uhd_device::open(const std::string &args, int ref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000747{
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700748 const char *refstr;
749
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000750 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000751 uhd::device_addr_t addr(args);
752 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000753 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000754 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400755 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000756 }
757
758 // Use the first found device
759 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000760 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700761 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000762 } catch(...) {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700763 LOG(ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400764 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000765 }
766
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000767 // Check for a valid device type and set bus type
768 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400769 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000770
Tom Tsou3b093bb2016-05-03 19:04:15 -0700771 if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
772 LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater";
773 return -1;
774 }
775
Thomas Tsou204a9f12013-10-29 18:34:16 -0400776 // Verify and set channels
Tom Tsou76764272016-06-24 14:25:39 -0700777 if (iface == MULTI_ARFCN) {
778 if ((dev_type != B200) && (dev_type != B210)) {
779 LOG(ALERT) << "Unsupported device configuration";
780 return -1;
781 }
782
783 dev_type = B2XX_MCBTS;
784 chans = 1;
785 } else if (chans == 2) {
786 if (dev_type == B210) {
787 } else if (dev_type == UMTRX) {
788 uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
789 usrp_dev->set_tx_subdev_spec(subdev_spec);
790 usrp_dev->set_rx_subdev_spec(subdev_spec);
791 } else {
792 LOG(ALERT) << "Invalid device configuration";
793 return -1;
794 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400795 } else if (chans != 1) {
796 LOG(ALERT) << "Invalid channel combination for device";
797 return -1;
798 }
799
800 tx_freqs.resize(chans);
801 rx_freqs.resize(chans);
802 tx_gains.resize(chans);
803 rx_gains.resize(chans);
804 rx_buffers.resize(chans);
805
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700806 switch (ref) {
807 case REF_INTERNAL:
808 refstr = "internal";
809 break;
810 case REF_EXTERNAL:
811 refstr = "external";
812 break;
813 case REF_GPS:
814 refstr = "gpsdo";
815 break;
816 default:
817 LOG(ALERT) << "Invalid reference type";
818 return -1;
819 }
820
821 usrp_dev->set_clock_source(refstr);
Thomas Tsou010fff72013-10-16 00:31:18 -0400822
kurtis.heimerl965e7572011-11-26 03:16:54 +0000823 // Set rates
Tom Tsou05c6feb2016-06-22 16:09:44 -0700824 double _rx_rate = select_rate(dev_type, rx_sps, iface);
825 double _tx_rate = select_rate(dev_type, tx_sps, iface);
826
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500827 if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
828 return -1;
829 if (set_rates(_tx_rate, _rx_rate) < 0)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400830 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000831
Alexander Chemerise1714252015-04-28 23:09:02 -0400832 // Set RF frontend bandwidth
833 if (dev_type == UMTRX) {
834 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
835 for (size_t i = 0; i < chans; i++) {
836 usrp_dev->set_tx_bandwidth(500*1000*2, i);
Tom Tsoud6ae8642017-03-30 17:22:58 -0700837 usrp_dev->set_rx_bandwidth(500*1000*2, i);
Alexander Chemerise1714252015-04-28 23:09:02 -0400838 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800839 } else if (dev_type == LIMESDR) {
840 for (size_t i = 0; i < chans; i++) {
841 usrp_dev->set_tx_bandwidth(5e6, i);
842 usrp_dev->set_rx_bandwidth(5e6, i);
843 }
Alexander Chemerise1714252015-04-28 23:09:02 -0400844 }
845
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400846 /* Create TX and RX streamers */
847 uhd::stream_args_t stream_args("sc16");
848 for (size_t i = 0; i < chans; i++)
849 stream_args.channels.push_back(i);
850
851 tx_stream = usrp_dev->get_tx_stream(stream_args);
852 rx_stream = usrp_dev->get_rx_stream(stream_args);
853
854 /* Number of samples per over-the-wire packet */
855 tx_spp = tx_stream->get_max_num_samps();
856 rx_spp = rx_stream->get_max_num_samps();
857
kurtis.heimerl965e7572011-11-26 03:16:54 +0000858 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400859 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400860 for (size_t i = 0; i < rx_buffers.size(); i++)
861 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000862
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800863 // Set receive chain sample offset. Trigger the EDGE offset
864 // table by checking for 4 SPS on the receive path. No other
865 // configuration supports using 4 SPS.
Tom Tsou05c6feb2016-06-22 16:09:44 -0700866 double offset = get_dev_offset();
Thomas Tsoue3e88142013-04-05 20:42:41 -0400867 if (offset == 0.0) {
868 LOG(ERR) << "Unsupported configuration, no correction applied";
869 ts_offset = 0;
870 } else {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400871 ts_offset = (TIMESTAMP) (offset * rx_rate);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400872 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000873
kurtis.heimerl02d04052011-11-26 03:17:10 +0000874 // Initialize and shadow gain values
875 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000876
kurtis.heimerl965e7572011-11-26 03:16:54 +0000877 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000878 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000879
Tom Tsou76764272016-06-24 14:25:39 -0700880 if (iface == MULTI_ARFCN)
881 return MULTI_ARFCN;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500882
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400883 switch (dev_type) {
884 case B100:
885 return RESAMP_64M;
886 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800887 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400888 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500889 case B200:
890 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100891 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800892 case E3XX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800893 case LIMESDR:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500894 default:
895 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400896 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400897
898 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000899}
900
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000901bool uhd_device::flush_recv(size_t num_pkts)
902{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000903 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000904 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800905 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000906
Thomas Tsou18d3b832014-02-13 14:55:23 -0500907 std::vector<std::vector<short> >
908 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000909
Thomas Tsou18d3b832014-02-13 14:55:23 -0500910 std::vector<short *> pkt_ptrs;
911 for (size_t i = 0; i < pkt_bufs.size(); i++)
912 pkt_ptrs.push_back(&pkt_bufs[i].front());
913
914 ts_initial = 0;
915 while (!ts_initial || (num_pkts-- > 0)) {
916 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400917 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000918 if (!num_smpls) {
919 switch (md.error_code) {
920 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800921 LOG(ALERT) << "Device timed out";
922 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000923 default:
924 continue;
925 }
926 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500927
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700928 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000929 }
930
Thomas Tsou18d3b832014-02-13 14:55:23 -0500931 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
932
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000933 return true;
934}
935
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800936bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000937{
Thomas Tsouc2839162014-03-27 13:52:58 -0400938 /* Allow 100 ms delay to align multi-channel streams */
939 double delay = 0.1;
940
kurtis.heimerl68292102011-11-26 03:17:28 +0000941 aligned = false;
942
Thomas Tsouc2839162014-03-27 13:52:58 -0400943 uhd::time_spec_t current = usrp_dev->get_time_now();
944
Thomas Tsou204a9f12013-10-29 18:34:16 -0400945 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400946 cmd.stream_now = false;
947 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400948
kurtis.heimerl68292102011-11-26 03:17:28 +0000949 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500950
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800951 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000952}
953
kurtis.heimerl965e7572011-11-26 03:16:54 +0000954bool uhd_device::start()
955{
956 LOG(INFO) << "Starting USRP...";
957
958 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000959 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000960 return false;
961 }
962
Tom Tsou72bf7622017-03-07 14:16:46 -0800963#ifndef USE_UHD_3_11
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400964 // Register msg handler
965 uhd::msg::register_handler(&uhd_msg_handler);
Tom Tsou72bf7622017-03-07 14:16:46 -0800966#endif
kurtis.heimerl965e7572011-11-26 03:16:54 +0000967 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800968 async_event_thrd = new Thread();
969 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000970
971 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800972 if (!restart())
973 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000974
kurtis.heimerl965e7572011-11-26 03:16:54 +0000975 // Display usrp time
976 double time_now = usrp_dev->get_time_now().get_real_secs();
977 LOG(INFO) << "The current time is " << time_now << " seconds";
978
979 started = true;
980 return true;
981}
982
983bool uhd_device::stop()
984{
Thomas Tsou2c791102013-11-15 23:00:28 -0500985 if (!started)
986 return false;
987
988 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000989 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
990
991 usrp_dev->issue_stream_cmd(stream_cmd);
992
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800993 async_event_thrd->cancel();
994 async_event_thrd->join();
995 delete async_event_thrd;
996
kurtis.heimerl965e7572011-11-26 03:16:54 +0000997 started = false;
998 return true;
999}
1000
Thomas Tsou7553aa92013-11-08 12:50:03 -05001001void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001002{
Thomas Tsou7553aa92013-11-08 12:50:03 -05001003 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001004 return;
1005}
1006
kurtis.heimerld4be0742011-11-26 03:17:46 +00001007int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001008{
kurtis.heimerld4be0742011-11-26 03:17:46 +00001009 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001010 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +00001011
1012 switch (md.error_code) {
1013 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +00001014 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001015 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +00001016 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1017 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1018 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1019 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1020 default:
1021 return ERROR_UNHANDLED;
1022 }
1023 }
1024
kurtis.heimerl965e7572011-11-26 03:16:54 +00001025 // Missing timestamp
1026 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +00001027 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +00001028 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001029 }
1030
1031 // Monotonicity check
Tom Tsoua93f7892017-03-07 17:54:06 -08001032 if (md.time_spec < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +00001033 LOG(ALERT) << "UHD: Loss of monotonic time";
Tom Tsoua93f7892017-03-07 17:54:06 -08001034 LOG(ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
ttsou724eb362012-03-13 02:20:01 +00001035 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +00001036 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001037 }
1038
Tom Tsoua93f7892017-03-07 17:54:06 -08001039 // Workaround for UHD tick rounding bug
1040 TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
1041 if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
1042 md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
1043
1044 prev_ts = md.time_spec;
1045
kurtis.heimerl965e7572011-11-26 03:16:54 +00001046 return 0;
1047}
1048
Thomas Tsou204a9f12013-10-29 18:34:16 -04001049int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
1050 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001051{
1052 ssize_t rc;
1053 uhd::time_spec_t ts;
1054 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001055
Thomas Tsou204a9f12013-10-29 18:34:16 -04001056 if (bufs.size() != chans) {
1057 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1058 return -1;
1059 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001060
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001061 *overrun = false;
1062 *underrun = false;
1063
kurtis.heimerl965e7572011-11-26 03:16:54 +00001064 // Shift read time with respect to transmit clock
1065 timestamp += ts_offset;
1066
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001067 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001068 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001069
1070 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -04001071 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001072 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001073 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001074 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001075 return 0;
1076 }
1077
Thomas Tsou204a9f12013-10-29 18:34:16 -04001078 // Create vector buffer
1079 std::vector<std::vector<short> >
1080 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
1081
1082 std::vector<short *> pkt_ptrs;
1083 for (size_t i = 0; i < pkt_bufs.size(); i++)
1084 pkt_ptrs.push_back(&pkt_bufs[i].front());
1085
kurtis.heimerl965e7572011-11-26 03:16:54 +00001086 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -04001087 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001088 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001089 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
1090 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001091 thread_enable_cancel(true);
1092
kurtis.heimerl965e7572011-11-26 03:16:54 +00001093 rx_pkt_cnt++;
1094
kurtis.heimerld4be0742011-11-26 03:17:46 +00001095 // Check for errors
1096 rc = check_rx_md_err(metadata, num_smpls);
1097 switch (rc) {
1098 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +00001099 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
1100 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +00001101 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001102 case ERROR_TIMEOUT:
1103 // Assume stopping condition
1104 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +00001105 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -04001106 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +00001107 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +00001108 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001109 }
1110
kurtis.heimerl965e7572011-11-26 03:16:54 +00001111 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001112 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001113
Thomas Tsou204a9f12013-10-29 18:34:16 -04001114 for (size_t i = 0; i < rx_buffers.size(); i++) {
1115 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
1116 num_smpls,
1117 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001118
Thomas Tsou204a9f12013-10-29 18:34:16 -04001119 // Continue on local overrun, exit on other errors
1120 if ((rc < 0)) {
1121 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001122 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001123 if (rc != smpl_buf::ERROR_OVERFLOW)
1124 return 0;
1125 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001126 }
1127 }
1128
1129 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -04001130 for (size_t i = 0; i < rx_buffers.size(); i++) {
1131 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
1132 if ((rc < 0) || (rc != len)) {
1133 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001134 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001135 return 0;
1136 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001137 }
1138
1139 return len;
1140}
1141
Thomas Tsou204a9f12013-10-29 18:34:16 -04001142int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
1143 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001144{
1145 uhd::tx_metadata_t metadata;
1146 metadata.has_time_spec = true;
1147 metadata.start_of_burst = false;
1148 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001149 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001150
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001151 *underrun = false;
1152
kurtis.heimerl965e7572011-11-26 03:16:54 +00001153 // No control packets
1154 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001155 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001156 return 0;
1157 }
1158
Thomas Tsou204a9f12013-10-29 18:34:16 -04001159 if (bufs.size() != chans) {
1160 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1161 return -1;
1162 }
1163
kurtis.heimerl965e7572011-11-26 03:16:54 +00001164 // Drop a fixed number of packets (magic value)
1165 if (!aligned) {
1166 drop_cnt++;
1167
1168 if (drop_cnt == 1) {
1169 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001170 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001171 metadata.end_of_burst = true;
1172 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001173 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001174 return len;
1175 } else {
1176 LOG(DEBUG) << "Aligning transmitter: start burst";
1177 metadata.start_of_burst = true;
1178 aligned = true;
1179 drop_cnt = 0;
1180 }
1181 }
1182
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001183 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001184 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001185 thread_enable_cancel(true);
1186
ttsoub371ed52012-01-09 18:11:34 +00001187 if (num_smpls != (unsigned) len) {
1188 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001189 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001190
1191 return num_smpls;
1192}
1193
1194bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1195{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001196 return true;
1197}
1198
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001199uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1200{
1201 double rf_spread, rf_freq;
1202 std::vector<double> freqs;
1203 uhd::tune_request_t treq(freq);
1204
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001205 if (dev_type == UMTRX) {
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001206 if (offset != 0.0)
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001207 return uhd::tune_request_t(freq, offset);
1208
1209 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1210 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1211 // only distort the signal (because cordic is not ideal).
1212 treq.target_freq = freq;
1213 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1214 treq.rf_freq = freq;
1215 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1216 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001217 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001218 } else if (chans == 1) {
Thomas Tsou8e17df72014-03-06 14:16:11 -05001219 if (offset == 0.0)
1220 return treq;
1221
1222 return uhd::tune_request_t(freq, offset);
1223 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001224 LOG(ALERT) << chans << " channels unsupported";
1225 return treq;
1226 }
1227
1228 if (tx)
1229 freqs = tx_freqs;
1230 else
1231 freqs = rx_freqs;
1232
1233 /* Tune directly if other channel isn't tuned */
1234 if (freqs[!chan] < 10.0)
1235 return treq;
1236
1237 /* Find center frequency between channels */
1238 rf_spread = fabs(freqs[!chan] - freq);
1239 if (rf_spread > B2XX_CLK_RT) {
1240 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1241 return treq;
1242 }
1243
1244 rf_freq = (freqs[!chan] + freq) / 2.0f;
1245
1246 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1247 treq.target_freq = freq;
1248 treq.rf_freq = rf_freq;
1249
1250 return treq;
1251}
1252
1253bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1254{
1255 std::vector<double> freqs;
1256 uhd::tune_result_t tres;
1257 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1258
1259 if (tx) {
1260 tres = usrp_dev->set_tx_freq(treq, chan);
1261 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1262 } else {
1263 tres = usrp_dev->set_rx_freq(treq, chan);
1264 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1265 }
1266 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1267
Thomas Tsou8e17df72014-03-06 14:16:11 -05001268 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1269 return true;
1270
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001271 /* Manual RF policy means we intentionally tuned with a baseband
1272 * offset for dual-channel purposes. Now retune the other channel
1273 * with the opposite corresponding frequency offset
1274 */
1275 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1276 if (tx) {
1277 treq = select_freq(tx_freqs[!chan], !chan, true);
1278 tres = usrp_dev->set_tx_freq(treq, !chan);
1279 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1280 } else {
1281 treq = select_freq(rx_freqs[!chan], !chan, false);
1282 tres = usrp_dev->set_rx_freq(treq, !chan);
1283 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1284
1285 }
1286 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1287 }
1288
1289 return true;
1290}
1291
Thomas Tsou204a9f12013-10-29 18:34:16 -04001292bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001293{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001294 if (chan >= tx_freqs.size()) {
1295 LOG(ALERT) << "Requested non-existent channel " << chan;
1296 return false;
1297 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001298 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001299
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001300 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001301}
1302
Thomas Tsou204a9f12013-10-29 18:34:16 -04001303bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001304{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001305 if (chan >= rx_freqs.size()) {
1306 LOG(ALERT) << "Requested non-existent channel " << chan;
1307 return false;
1308 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001309 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001310
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001311 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001312}
1313
Thomas Tsou204a9f12013-10-29 18:34:16 -04001314double uhd_device::getTxFreq(size_t chan)
1315{
1316 if (chan >= tx_freqs.size()) {
1317 LOG(ALERT) << "Requested non-existent channel " << chan;
1318 return 0.0;
1319 }
1320
1321 return tx_freqs[chan];
1322}
1323
1324double uhd_device::getRxFreq(size_t chan)
1325{
1326 if (chan >= rx_freqs.size()) {
1327 LOG(ALERT) << "Requested non-existent channel " << chan;
1328 return 0.0;
1329 }
1330
1331 return rx_freqs[chan];
1332}
1333
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001334/*
1335 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1336 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1337 * combination.
1338 */
1339TIMESTAMP uhd_device::initialWriteTimestamp()
1340{
Tom Tsou76764272016-06-24 14:25:39 -07001341 if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001342 return ts_initial;
1343 else
1344 return ts_initial * tx_sps;
1345}
1346
1347TIMESTAMP uhd_device::initialReadTimestamp()
1348{
1349 return ts_initial;
1350}
1351
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001352double uhd_device::fullScaleInputValue()
1353{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001354 if (dev_type == LIMESDR)
1355 return (double) 2047 * LIMESDR_TX_AMPL;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001356 if (dev_type == UMTRX)
1357 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1358 else
1359 return (double) SHRT_MAX * USRP_TX_AMPL;
1360}
1361
1362double uhd_device::fullScaleOutputValue()
1363{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001364 if (dev_type == LIMESDR) return (double) 2047;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001365 return (double) SHRT_MAX;
1366}
1367
kurtis.heimerl965e7572011-11-26 03:16:54 +00001368bool uhd_device::recv_async_msg()
1369{
ttsou60dc4c92012-08-08 23:30:23 +00001370 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001371
1372 thread_enable_cancel(false);
1373 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1374 thread_enable_cancel(true);
1375 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001376 return false;
1377
1378 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001379 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001380 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001381
1382 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1383 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1384 LOG(ERR) << str_code(md);
1385 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001386 }
1387
1388 return true;
1389}
1390
1391std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1392{
1393 std::ostringstream ost("UHD: ");
1394
1395 switch (metadata.error_code) {
1396 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1397 ost << "No error";
1398 break;
1399 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1400 ost << "No packet received, implementation timed-out";
1401 break;
1402 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1403 ost << "A stream command was issued in the past";
1404 break;
1405 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1406 ost << "Expected another stream command";
1407 break;
1408 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1409 ost << "An internal receive buffer has filled";
1410 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001411 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1412 ost << "Multi-channel alignment failed";
1413 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001414 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1415 ost << "The packet could not be parsed";
1416 break;
1417 default:
1418 ost << "Unknown error " << metadata.error_code;
1419 }
1420
1421 if (metadata.has_time_spec)
1422 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1423
1424 return ost.str();
1425}
1426
1427std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1428{
1429 std::ostringstream ost("UHD: ");
1430
1431 switch (metadata.event_code) {
1432 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1433 ost << "A packet was successfully transmitted";
1434 break;
1435 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1436 ost << "An internal send buffer has emptied";
1437 break;
1438 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1439 ost << "Packet loss between host and device";
1440 break;
1441 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1442 ost << "Packet time was too late or too early";
1443 break;
1444 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1445 ost << "Underflow occurred inside a packet";
1446 break;
1447 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1448 ost << "Packet loss within a burst";
1449 break;
1450 default:
1451 ost << "Unknown error " << metadata.event_code;
1452 }
1453
1454 if (metadata.has_time_spec)
1455 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1456
1457 return ost.str();
1458}
1459
1460smpl_buf::smpl_buf(size_t len, double rate)
1461 : buf_len(len), clk_rt(rate),
1462 time_start(0), time_end(0), data_start(0), data_end(0)
1463{
1464 data = new uint32_t[len];
1465}
1466
1467smpl_buf::~smpl_buf()
1468{
1469 delete[] data;
1470}
1471
1472ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1473{
1474 if (timestamp < time_start)
1475 return ERROR_TIMESTAMP;
1476 else if (timestamp >= time_end)
1477 return 0;
1478 else
1479 return time_end - timestamp;
1480}
1481
1482ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1483{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001484 return avail_smpls(timespec.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001485}
1486
1487ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1488{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001489 int type_sz = 2 * sizeof(short);
1490
kurtis.heimerl965e7572011-11-26 03:16:54 +00001491 // Check for valid read
1492 if (timestamp < time_start)
1493 return ERROR_TIMESTAMP;
1494 if (timestamp >= time_end)
1495 return 0;
1496 if (len >= buf_len)
1497 return ERROR_READ;
1498
1499 // How many samples should be copied
1500 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001501 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001502 num_smpls = len;
1503
1504 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001505 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001506
1507 // Read it
1508 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001509 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001510 memcpy(buf, data + read_start, numBytes);
1511 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001512 size_t first_cp = (buf_len - read_start) * type_sz;
1513 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001514
1515 memcpy(buf, data + read_start, first_cp);
1516 memcpy((char*) buf + first_cp, data, second_cp);
1517 }
1518
1519 data_start = (read_start + len) % buf_len;
1520 time_start = timestamp + len;
1521
1522 if (time_start > time_end)
1523 return ERROR_READ;
1524 else
1525 return num_smpls;
1526}
1527
1528ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1529{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001530 return read(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001531}
1532
1533ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1534{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001535 int type_sz = 2 * sizeof(short);
1536
kurtis.heimerl965e7572011-11-26 03:16:54 +00001537 // Check for valid write
1538 if ((len == 0) || (len >= buf_len))
1539 return ERROR_WRITE;
1540 if ((timestamp + len) <= time_end)
1541 return ERROR_TIMESTAMP;
1542
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001543 if (timestamp < time_end) {
1544 LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001545 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1546 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 -04001547 // Do not return error here, because it's a rounding error and is not fatal
1548 }
1549 if (timestamp > time_end && time_end != 0) {
1550 LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001551 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1552 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 -04001553 // Do not return error here, because it's a rounding error and is not fatal
1554 }
1555
kurtis.heimerl965e7572011-11-26 03:16:54 +00001556 // Starting index
1557 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1558
1559 // Write it
1560 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001561 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001562 memcpy(data + write_start, buf, numBytes);
1563 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001564 size_t first_cp = (buf_len - write_start) * type_sz;
1565 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001566
1567 memcpy(data + write_start, buf, first_cp);
1568 memcpy(data, (char*) buf + first_cp, second_cp);
1569 }
1570
1571 data_end = (write_start + len) % buf_len;
1572 time_end = timestamp + len;
1573
Thomas Tsou18d3b832014-02-13 14:55:23 -05001574 if (!data_start)
1575 data_start = write_start;
1576
kurtis.heimerl965e7572011-11-26 03:16:54 +00001577 if (((write_start + len) > buf_len) && (data_end > data_start))
1578 return ERROR_OVERFLOW;
1579 else if (time_end <= time_start)
1580 return ERROR_WRITE;
1581 else
1582 return len;
1583}
1584
1585ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1586{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001587 return write(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001588}
1589
Tom Tsou1ae25562014-12-05 12:56:34 -08001590std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001591{
1592 std::ostringstream ost("Sample buffer: ");
1593
Tom Tsou1ae25562014-12-05 12:56:34 -08001594 ost << "timestamp = " << ts;
1595 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001596 ost << ", time_start = " << time_start;
1597 ost << ", time_end = " << time_end;
1598 ost << ", data_start = " << data_start;
1599 ost << ", data_end = " << data_end;
1600
1601 return ost.str();
1602}
1603
1604std::string smpl_buf::str_code(ssize_t code)
1605{
1606 switch (code) {
1607 case ERROR_TIMESTAMP:
1608 return "Sample buffer: Requested timestamp is not valid";
1609 case ERROR_READ:
1610 return "Sample buffer: Read error";
1611 case ERROR_WRITE:
1612 return "Sample buffer: Write error";
1613 case ERROR_OVERFLOW:
1614 return "Sample buffer: Overrun";
1615 default:
1616 return "Sample buffer: Unknown error";
1617 }
1618}
1619
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001620RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
Tom Tsou05c6feb2016-06-22 16:09:44 -07001621 InterfaceType iface, size_t chans, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001622{
Tom Tsou05c6feb2016-06-22 16:09:44 -07001623 return new uhd_device(tx_sps, rx_sps, iface, chans, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001624}