blob: c5ea4c175b0e6243d5f7cdb08e4fa1dc94d3d900 [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/*
142 * Offset handling for special cases. Currently used for UmTRX dual channel
143 * diversity receiver only.
144 */
145static struct uhd_dev_offset special_offsets[] = {
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800146 { UMTRX, 1, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
147 { UMTRX, 4, 1, 5.2103e-5, "UmTRX diversity, 4 SPS" },
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500148};
149
Tom Tsou3b093bb2016-05-03 19:04:15 -0700150
Thomas Tsoucb69f082013-04-08 14:18:26 -0400151/*
152 * Select sample rate based on device type and requested samples-per-symbol.
153 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
154 * usable channel spacing of 400 kHz.
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800155 */
Tom Tsou05c6feb2016-06-22 16:09:44 -0700156static double select_rate(uhd_dev_type type, int sps,
157 RadioDevice::InterfaceType iface)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400158{
Tom Tsou05c6feb2016-06-22 16:09:44 -0700159 if (iface == RadioDevice::DIVERSITY) {
160 if (type == UMTRX)
161 return GSMRATE * 4;
162
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500163 LOG(ALERT) << "Diversity supported on UmTRX only";
164 return -9999.99;
165 }
166
Tom Tsou76764272016-06-24 14:25:39 -0700167
Thomas Tsoua57bc8a2013-09-05 08:16:47 +0800168 if ((sps != 4) && (sps != 1))
Thomas Tsoucb69f082013-04-08 14:18:26 -0400169 return -9999.99;
170
Tom Tsou76764272016-06-24 14:25:39 -0700171 if (iface == RadioDevice::MULTI_ARFCN) {
172 switch (type) {
173 case B2XX_MCBTS:
174 return 4 * MCBTS_SPACING;
175 default:
176 LOG(ALERT) << "Invalid device combination";
177 return -9999.99;
178 }
179 }
180
Thomas Tsoucb69f082013-04-08 14:18:26 -0400181 switch (type) {
182 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800183 case X3XX:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400184 return USRP2_BASE_RT * sps;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400185 case B100:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400186 return B100_BASE_RT * sps;
Thomas Tsoue7882392014-02-13 14:46:23 -0500187 case B200:
188 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100189 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800190 case E3XX:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400191 case UMTRX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800192 case LIMESDR:
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400193 return GSMRATE * sps;
194 default:
Thomas Tsoucb69f082013-04-08 14:18:26 -0400195 break;
196 }
197
198 LOG(ALERT) << "Unknown device type " << type;
199 return -9999.99;
200}
201
kurtis.heimerl965e7572011-11-26 03:16:54 +0000202/*
Alexander Chemeris1ba69e72016-06-18 10:50:11 +0300203 Sample Buffer - Allows reading and writing of timed samples using osmo-trx
kurtis.heimerl965e7572011-11-26 03:16:54 +0000204 or UHD style timestamps. Time conversions are handled
205 internally or accessable through the static convert calls.
206*/
207class smpl_buf {
208public:
209 /** Sample buffer constructor
210 @param len number of 32-bit samples the buffer should hold
211 @param rate sample clockrate
212 @param timestamp
213 */
214 smpl_buf(size_t len, double rate);
215 ~smpl_buf();
216
217 /** Query number of samples available for reading
218 @param timestamp time of first sample
219 @return number of available samples or error
220 */
221 ssize_t avail_smpls(TIMESTAMP timestamp) const;
222 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
223
224 /** Read and write
225 @param buf pointer to buffer
226 @param len number of samples desired to read or write
227 @param timestamp time of first stample
228 @return number of actual samples read or written or error
229 */
230 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
231 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
232 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
233 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
234
235 /** Buffer status string
236 @return a formatted string describing internal buffer state
237 */
Tom Tsou1ae25562014-12-05 12:56:34 -0800238 std::string str_status(size_t ts) const;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000239
240 /** Formatted error string
241 @param code an error code
242 @return a formatted error string
243 */
244 static std::string str_code(ssize_t code);
245
246 enum err_code {
247 ERROR_TIMESTAMP = -1,
248 ERROR_READ = -2,
249 ERROR_WRITE = -3,
250 ERROR_OVERFLOW = -4
251 };
252
253private:
254 uint32_t *data;
255 size_t buf_len;
256
257 double clk_rt;
258
259 TIMESTAMP time_start;
260 TIMESTAMP time_end;
261
262 size_t data_start;
263 size_t data_end;
264};
265
266/*
267 uhd_device - UHD implementation of the Device interface. Timestamped samples
268 are sent to and received from the device. An intermediate buffer
269 on the receive side collects and aligns packets of samples.
270 Events and errors such as underruns are reported asynchronously
271 by the device and received in a separate thread.
272*/
273class uhd_device : public RadioDevice {
274public:
Tom Tsou05c6feb2016-06-22 16:09:44 -0700275 uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
276 size_t chans, double offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000277 ~uhd_device();
278
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700279 int open(const std::string &args, int ref, bool swap_channels);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000280 bool start();
281 bool stop();
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800282 bool restart();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500283 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400284 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000285
Thomas Tsou204a9f12013-10-29 18:34:16 -0400286 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000287 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
288
Thomas Tsou204a9f12013-10-29 18:34:16 -0400289 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000290 TIMESTAMP timestamp, bool isControl);
291
292 bool updateAlignment(TIMESTAMP timestamp);
293
Thomas Tsou204a9f12013-10-29 18:34:16 -0400294 bool setTxFreq(double wFreq, size_t chan);
295 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000296
Tom Tsou05c6feb2016-06-22 16:09:44 -0700297 TIMESTAMP initialWriteTimestamp();
298 TIMESTAMP initialReadTimestamp();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000299
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +0300300 double fullScaleInputValue();
301 double fullScaleOutputValue();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000302
Thomas Tsou204a9f12013-10-29 18:34:16 -0400303 double setRxGain(double db, size_t chan);
304 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000305 double maxRxGain(void) { return rx_gain_max; }
306 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000307
Thomas Tsou204a9f12013-10-29 18:34:16 -0400308 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000309 double maxTxGain(void) { return tx_gain_max; }
310 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000311
Thomas Tsou204a9f12013-10-29 18:34:16 -0400312 double getTxFreq(size_t chan);
313 double getRxFreq(size_t chan);
314 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000315
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400316 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000317 inline double numberRead() { return rx_pkt_cnt; }
318 inline double numberWritten() { return 0; }
319
320 /** Receive and process asynchronous message
321 @return true if message received or false on timeout or error
322 */
323 bool recv_async_msg();
324
kurtis.heimerld4be0742011-11-26 03:17:46 +0000325 enum err_code {
326 ERROR_TIMING = -1,
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800327 ERROR_TIMEOUT = -2,
328 ERROR_UNRECOVERABLE = -3,
329 ERROR_UNHANDLED = -4,
kurtis.heimerld4be0742011-11-26 03:17:46 +0000330 };
331
kurtis.heimerl965e7572011-11-26 03:16:54 +0000332private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000333 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400334 uhd::tx_streamer::sptr tx_stream;
335 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400336 enum TxWindowType tx_window;
337 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000338
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800339 size_t tx_sps, rx_sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400340 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000341
Thomas Tsou204a9f12013-10-29 18:34:16 -0400342 double tx_gain_min, tx_gain_max;
343 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500344 double offset;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000345
Thomas Tsou204a9f12013-10-29 18:34:16 -0400346 std::vector<double> tx_gains, rx_gains;
347 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000348 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000349
350 bool started;
351 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000352
353 size_t rx_pkt_cnt;
354 size_t drop_cnt;
355 uhd::time_spec_t prev_ts;
356
Thomas Tsou18d3b832014-02-13 14:55:23 -0500357 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400358 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000359
kurtis.heimerl02d04052011-11-26 03:17:10 +0000360 void init_gains();
Tom Tsou05c6feb2016-06-22 16:09:44 -0700361 double get_dev_offset();
Thomas Tsou02d88d12013-04-05 15:36:30 -0400362 int set_master_clk(double rate);
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400363 int set_rates(double tx_rate, double rx_rate);
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000364 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000365 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000366 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000367
kurtis.heimerl965e7572011-11-26 03:16:54 +0000368 std::string str_code(uhd::rx_metadata_t metadata);
369 std::string str_code(uhd::async_metadata_t metadata);
370
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500371 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
372 bool set_freq(double freq, size_t chan, bool tx);
373
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800374 Thread *async_event_thrd;
Tom Tsou05c6feb2016-06-22 16:09:44 -0700375 InterfaceType iface;
Tom Tsou93b7f372014-12-15 20:23:33 -0800376 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000377};
378
379void *async_event_loop(uhd_device *dev)
380{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500381 dev->setPriority(0.43);
382
kurtis.heimerl965e7572011-11-26 03:16:54 +0000383 while (1) {
384 dev->recv_async_msg();
385 pthread_testcancel();
386 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500387
388 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000389}
390
Tom Tsou72bf7622017-03-07 14:16:46 -0800391#ifndef USE_UHD_3_11
392/*
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000393 Catch and drop underrun 'U' and overrun 'O' messages from stdout
394 since we already report using the logging facility. Direct
395 everything else appropriately.
396 */
397void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
398{
399 switch (type) {
400 case uhd::msg::status:
401 LOG(INFO) << msg;
402 break;
403 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000404 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000405 break;
406 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000407 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000408 break;
409 case uhd::msg::fastpath:
410 break;
411 }
412}
Tom Tsou72bf7622017-03-07 14:16:46 -0800413#endif
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000414
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800415static void thread_enable_cancel(bool cancel)
416{
417 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
418 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
419}
420
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800421uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
Tom Tsou05c6feb2016-06-22 16:09:44 -0700422 InterfaceType iface, size_t chans, double offset)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400423 : tx_gain_min(0.0), tx_gain_max(0.0),
424 rx_gain_min(0.0), rx_gain_max(0.0),
425 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000426 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou18d3b832014-02-13 14:55:23 -0500427 prev_ts(0,0), ts_initial(0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000428{
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800429 this->tx_sps = tx_sps;
430 this->rx_sps = rx_sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400431 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500432 this->offset = offset;
Tom Tsou05c6feb2016-06-22 16:09:44 -0700433 this->iface = iface;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000434}
435
436uhd_device::~uhd_device()
437{
438 stop();
439
Thomas Tsou204a9f12013-10-29 18:34:16 -0400440 for (size_t i = 0; i < rx_buffers.size(); i++)
441 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000442}
443
kurtis.heimerl24481de2011-11-26 03:17:18 +0000444void uhd_device::init_gains()
445{
446 uhd::gain_range_t range;
447
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400448 if (dev_type == UMTRX) {
449 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
450 if (gain_stages[0] == "VGA") {
451 LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
452 }
453 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
454 range = usrp_dev->get_tx_gain_range();
455 tx_gain_min = range.start();
456 tx_gain_max = range.stop();
457 } else {
458 range = usrp_dev->get_tx_gain_range("VGA2");
459 tx_gain_min = UMTRX_VGA1_DEF + range.start();
460 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
461 }
462 } else {
463 range = usrp_dev->get_tx_gain_range();
464 tx_gain_min = range.start();
465 tx_gain_max = range.stop();
466 }
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400467 LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000468
469 range = usrp_dev->get_rx_gain_range();
470 rx_gain_min = range.start();
471 rx_gain_max = range.stop();
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400472 LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000473
Thomas Tsou204a9f12013-10-29 18:34:16 -0400474 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400475 double gain = (tx_gain_min + tx_gain_max) / 2;
476 LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
477 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400478 tx_gains[i] = usrp_dev->get_tx_gain(i);
479 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000480
Thomas Tsou204a9f12013-10-29 18:34:16 -0400481 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400482 double gain = (rx_gain_min + rx_gain_max) / 2;
483 LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
484 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400485 rx_gains[i] = usrp_dev->get_rx_gain(i);
486 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000487
488 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400489
kurtis.heimerl24481de2011-11-26 03:17:18 +0000490}
491
Tom Tsou05c6feb2016-06-22 16:09:44 -0700492double uhd_device::get_dev_offset()
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700493{
494 struct uhd_dev_offset *offset = NULL;
495
496 /* Reject USRP1 */
497 if (dev_type == USRP1) {
498 LOG(ERR) << "Invalid device type";
499 return 0.0;
500 }
501
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700502 /* Special cases (e.g. diversity receiver) */
Tom Tsou05c6feb2016-06-22 16:09:44 -0700503 if (iface == DIVERSITY) {
504 if ((dev_type != UMTRX) || (rx_sps != 1)) {
505 LOG(ALERT) << "Unsupported device configuration";
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700506 return 0.0;
507 }
508
509 switch (tx_sps) {
510 case 1:
511 offset = &special_offsets[0];
512 break;
513 case 4:
514 default:
515 offset = &special_offsets[1];
516 }
517 } else {
518 /* Search for matching offset value */
519 for (size_t i = 0; i < NUM_UHD_OFFSETS; i++) {
520 if ((dev_type == uhd_offsets[i].type) &&
521 (tx_sps == uhd_offsets[i].tx_sps) &&
522 (rx_sps == uhd_offsets[i].rx_sps)) {
523 offset = &uhd_offsets[i];
524 break;
525 }
526 }
527 }
528
529 if (!offset) {
530 LOG(ERR) << "Invalid device configuration";
531 return 0.0;
532 }
533
534 std::cout << "-- Setting " << offset->desc << std::endl;
535
536 return offset->offset;
537}
538
Thomas Tsou02d88d12013-04-05 15:36:30 -0400539int uhd_device::set_master_clk(double clk_rate)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000540{
Thomas Tsou092f7572013-04-04 17:04:39 -0400541 double actual, offset, limit = 1.0;
kurtis.heimerlac0ee122011-11-28 06:25:58 +0000542
Thomas Tsou7e068062013-04-08 19:39:37 -0400543 try {
544 usrp_dev->set_master_clock_rate(clk_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400545 } catch (const std::exception &ex) {
546 LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
547 LOG(ALERT) << ex.what();
548 return -1;
549 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000550
Thomas Tsou092f7572013-04-04 17:04:39 -0400551 actual = usrp_dev->get_master_clock_rate();
552 offset = fabs(clk_rate - actual);
553
554 if (offset > limit) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000555 LOG(ALERT) << "Failed to set master clock rate";
Thomas Tsou7e068062013-04-08 19:39:37 -0400556 LOG(ALERT) << "Requested clock rate " << clk_rate;
Thomas Tsou092f7572013-04-04 17:04:39 -0400557 LOG(ALERT) << "Actual clock rate " << actual;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400558 return -1;
kurtis.heimerld3e25902011-11-26 03:18:13 +0000559 }
Thomas Tsou02d88d12013-04-05 15:36:30 -0400560
561 return 0;
562}
563
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400564int uhd_device::set_rates(double tx_rate, double rx_rate)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400565{
Thomas Tsou092f7572013-04-04 17:04:39 -0400566 double offset_limit = 1.0;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400567 double tx_offset, rx_offset;
568
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100569 /* B2XX and E1xx are the only device where we set FPGA clocking */
Tom Tsou283b22d2015-10-21 17:10:23 -0700570 if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400571 if (set_master_clk(B2XX_CLK_RT) < 0)
Thomas Tsou02d88d12013-04-05 15:36:30 -0400572 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700573 } else if (dev_type == E1XX) {
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100574 if (set_master_clk(E1XX_CLK_RT) < 0)
575 return -1;
Tom Tsou76764272016-06-24 14:25:39 -0700576 } else if (dev_type == B2XX_MCBTS) {
577 if (set_master_clk(B2XX_MCBTS_CLK_RT) < 0)
578 return -1;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100579 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800580 else if (dev_type == LIMESDR) {
581 if (set_master_clk(LIMESDR_CLK_RT) < 0)
582 return -1;
583 }
kurtis.heimerld3e25902011-11-26 03:18:13 +0000584
Tom Tsou76764272016-06-24 14:25:39 -0700585
kurtis.heimerld3e25902011-11-26 03:18:13 +0000586 // Set sample rates
Thomas Tsou7e068062013-04-08 19:39:37 -0400587 try {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400588 usrp_dev->set_tx_rate(tx_rate);
589 usrp_dev->set_rx_rate(rx_rate);
Thomas Tsou7e068062013-04-08 19:39:37 -0400590 } catch (const std::exception &ex) {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400591 LOG(ALERT) << "UHD rate setting failed";
Thomas Tsou7e068062013-04-08 19:39:37 -0400592 LOG(ALERT) << ex.what();
593 return -1;
594 }
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400595 this->tx_rate = usrp_dev->get_tx_rate();
596 this->rx_rate = usrp_dev->get_rx_rate();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000597
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400598 tx_offset = fabs(this->tx_rate - tx_rate);
599 rx_offset = fabs(this->rx_rate - rx_rate);
Thomas Tsoucb69f082013-04-08 14:18:26 -0400600 if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
kurtis.heimerle3320322011-11-28 06:26:08 +0000601 LOG(ALERT) << "Actual sample rate differs from desired rate";
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400602 LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
603 << this->rx_rate << ")";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400604 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000605 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000606
Thomas Tsou02d88d12013-04-05 15:36:30 -0400607 return 0;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000608}
609
Thomas Tsou204a9f12013-10-29 18:34:16 -0400610double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000611{
Tom Tsou76764272016-06-24 14:25:39 -0700612 if (iface == MULTI_ARFCN)
613 chan = 0;
614
Thomas Tsou204a9f12013-10-29 18:34:16 -0400615 if (chan >= tx_gains.size()) {
616 LOG(ALERT) << "Requested non-existent channel" << chan;
617 return 0.0f;
618 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000619
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400620 if (dev_type == UMTRX) {
621 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
622 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
623 usrp_dev->set_tx_gain(db, chan);
624 } else {
625 // New UHD versions support split configuration of
626 // Tx gain stages. We utilize this to set the gain
627 // configuration, optimal for the Tx signal quality.
628 // From our measurements, VGA1 must be 18dB plus-minus
629 // one and VGA2 is the best when 23dB or lower.
630 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
631 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
632 }
633 } else {
634 usrp_dev->set_tx_gain(db, chan);
635 }
636
Thomas Tsou204a9f12013-10-29 18:34:16 -0400637 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000638
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400639 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400640
641 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000642}
643
Thomas Tsou204a9f12013-10-29 18:34:16 -0400644double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000645{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400646 if (chan >= rx_gains.size()) {
647 LOG(ALERT) << "Requested non-existent channel " << chan;
648 return 0.0f;
649 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000650
Thomas Tsou204a9f12013-10-29 18:34:16 -0400651 usrp_dev->set_rx_gain(db, chan);
652 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000653
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400654 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400655
656 return rx_gains[chan];
657}
658
659double uhd_device::getRxGain(size_t chan)
660{
Tom Tsou76764272016-06-24 14:25:39 -0700661 if (iface == MULTI_ARFCN)
662 chan = 0;
663
Thomas Tsou204a9f12013-10-29 18:34:16 -0400664 if (chan >= rx_gains.size()) {
665 LOG(ALERT) << "Requested non-existent channel " << chan;
666 return 0.0f;
667 }
668
669 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000670}
671
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000672/*
673 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400674 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000675 deal with the transport latency. Reject the USRP1 because UHD doesn't
676 support timestamped samples with it.
677 */
678bool uhd_device::parse_dev_type()
679{
680 std::string mboard_str, dev_str;
681 uhd::property_tree::sptr prop_tree;
Tom Tsou3b093bb2016-05-03 19:04:15 -0700682 size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str, e3xx_str,
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800683 b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str, limesdr_str;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000684
685 prop_tree = usrp_dev->get_device()->get_tree();
686 dev_str = prop_tree->access<std::string>("/name").get();
687 mboard_str = usrp_dev->get_mboard_name();
688
689 usrp1_str = dev_str.find("USRP1");
Thomas Tsoucb69f082013-04-08 14:18:26 -0400690 usrp2_str = dev_str.find("USRP2");
691 b100_str = mboard_str.find("B100");
Thomas Tsou092f7572013-04-04 17:04:39 -0400692 b200_str = mboard_str.find("B200");
Thomas Tsou69d14c92013-10-11 14:27:35 -0400693 b210_str = mboard_str.find("B210");
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100694 e100_str = mboard_str.find("E100");
695 e110_str = mboard_str.find("E110");
Tom Tsou3b093bb2016-05-03 19:04:15 -0700696 e310_str = mboard_str.find("E310");
697 e3xx_str = mboard_str.find("E3XX");
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800698 x300_str = mboard_str.find("X300");
699 x310_str = mboard_str.find("X310");
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400700 umtrx_str = dev_str.find("UmTRX");
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800701 // LimeSDR is based on STREAM board, so it's advertized as such
702 limesdr_str = dev_str.find("STREAM");
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000703
704 if (usrp1_str != std::string::npos) {
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000705 LOG(ALERT) << "USRP1 is not supported using the UHD driver";
706 LOG(ALERT) << "Please compile with GNU Radio libusrp support";
Thomas Tsou02d88d12013-04-05 15:36:30 -0400707 dev_type = USRP1;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000708 return false;
709 }
710
Thomas Tsoucb69f082013-04-08 14:18:26 -0400711 if (b100_str != std::string::npos) {
Thomas Tsou02d88d12013-04-05 15:36:30 -0400712 tx_window = TX_WINDOW_USRP1;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400713 dev_type = B100;
Thomas Tsou092f7572013-04-04 17:04:39 -0400714 } else if (b200_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500715 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500716 dev_type = B200;
Thomas Tsou69d14c92013-10-11 14:27:35 -0400717 } else if (b210_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500718 tx_window = TX_WINDOW_USRP1;
Thomas Tsoue7882392014-02-13 14:46:23 -0500719 dev_type = B210;
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100720 } else if (e100_str != std::string::npos) {
721 tx_window = TX_WINDOW_FIXED;
722 dev_type = E1XX;
723 } else if (e110_str != std::string::npos) {
724 tx_window = TX_WINDOW_FIXED;
725 dev_type = E1XX;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400726 } else if (usrp2_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500727 tx_window = TX_WINDOW_FIXED;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400728 dev_type = USRP2;
Tom Tsou3b093bb2016-05-03 19:04:15 -0700729 } else if ((e310_str != std::string::npos) ||
730 (e3xx_str != std::string::npos)) {
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800731 tx_window = TX_WINDOW_FIXED;
732 dev_type = E3XX;
733 } else if (x300_str != std::string::npos) {
734 tx_window = TX_WINDOW_FIXED;
735 dev_type = X3XX;
736 } else if (x310_str != std::string::npos) {
737 tx_window = TX_WINDOW_FIXED;
738 dev_type = X3XX;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400739 } else if (umtrx_str != std::string::npos) {
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500740 tx_window = TX_WINDOW_FIXED;
Thomas Tsouc88d8d52013-08-21 17:55:54 -0400741 dev_type = UMTRX;
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800742 } else if (limesdr_str != std::string::npos) {
743 tx_window = TX_WINDOW_USRP1;
744 dev_type = LIMESDR;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400745 } else {
Tom Tsoua5e0f1c2016-05-03 15:14:06 -0700746 LOG(ALERT) << "Unknown UHD device type "
747 << dev_str << " " << mboard_str;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400748 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000749 }
750
Thomas Tsouacc22fa2013-11-09 02:25:41 -0500751 if (tx_window == TX_WINDOW_USRP1) {
752 LOG(INFO) << "Using USRP1 type transmit window for "
753 << dev_str << " " << mboard_str;
754 } else {
755 LOG(INFO) << "Using fixed transmit window for "
756 << dev_str << " " << mboard_str;
757 }
758
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000759 return true;
760}
761
Tom Tsou3b093bb2016-05-03 19:04:15 -0700762/*
763 * Check for UHD version > 3.9.0 for E3XX support
764 */
765static bool uhd_e3xx_version_chk()
766{
767 std::string ver = uhd::get_version_string();
768 std::string major_str(ver.begin(), ver.begin() + 3);
769 std::string minor_str(ver.begin() + 4, ver.begin() + 7);
770
771 int major_val = atoi(major_str.c_str());
772 int minor_val = atoi(minor_str.c_str());
773
774 if (major_val < 3)
775 return false;
776 if (minor_val < 9)
777 return false;
778
779 return true;
780}
781
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700782int uhd_device::open(const std::string &args, int ref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000783{
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700784 const char *refstr;
785
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000786 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000787 uhd::device_addr_t addr(args);
788 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000789 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000790 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400791 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000792 }
793
794 // Use the first found device
795 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000796 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700797 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000798 } catch(...) {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700799 LOG(ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400800 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000801 }
802
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000803 // Check for a valid device type and set bus type
804 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400805 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000806
Tom Tsou3b093bb2016-05-03 19:04:15 -0700807 if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
808 LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater";
809 return -1;
810 }
811
Thomas Tsou204a9f12013-10-29 18:34:16 -0400812 // Verify and set channels
Tom Tsou76764272016-06-24 14:25:39 -0700813 if (iface == MULTI_ARFCN) {
814 if ((dev_type != B200) && (dev_type != B210)) {
815 LOG(ALERT) << "Unsupported device configuration";
816 return -1;
817 }
818
819 dev_type = B2XX_MCBTS;
820 chans = 1;
821 } else if (chans == 2) {
822 if (dev_type == B210) {
823 } else if (dev_type == UMTRX) {
824 uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
825 usrp_dev->set_tx_subdev_spec(subdev_spec);
826 usrp_dev->set_rx_subdev_spec(subdev_spec);
827 } else {
828 LOG(ALERT) << "Invalid device configuration";
829 return -1;
830 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400831 } else if (chans != 1) {
832 LOG(ALERT) << "Invalid channel combination for device";
833 return -1;
834 }
835
836 tx_freqs.resize(chans);
837 rx_freqs.resize(chans);
838 tx_gains.resize(chans);
839 rx_gains.resize(chans);
840 rx_buffers.resize(chans);
841
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700842 switch (ref) {
843 case REF_INTERNAL:
844 refstr = "internal";
845 break;
846 case REF_EXTERNAL:
847 refstr = "external";
848 break;
849 case REF_GPS:
850 refstr = "gpsdo";
851 break;
852 default:
853 LOG(ALERT) << "Invalid reference type";
854 return -1;
855 }
856
857 usrp_dev->set_clock_source(refstr);
Thomas Tsou010fff72013-10-16 00:31:18 -0400858
kurtis.heimerl965e7572011-11-26 03:16:54 +0000859 // Set rates
Tom Tsou05c6feb2016-06-22 16:09:44 -0700860 double _rx_rate = select_rate(dev_type, rx_sps, iface);
861 double _tx_rate = select_rate(dev_type, tx_sps, iface);
862
863 if (iface == DIVERSITY)
864 _rx_rate = select_rate(dev_type, 1, iface);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500865
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500866 if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
867 return -1;
868 if (set_rates(_tx_rate, _rx_rate) < 0)
Thomas Tsoucb69f082013-04-08 14:18:26 -0400869 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000870
Alexander Chemerise1714252015-04-28 23:09:02 -0400871 // Set RF frontend bandwidth
872 if (dev_type == UMTRX) {
873 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
874 for (size_t i = 0; i < chans; i++) {
875 usrp_dev->set_tx_bandwidth(500*1000*2, i);
Tom Tsou05c6feb2016-06-22 16:09:44 -0700876 if (iface != DIVERSITY)
Alexander Chemerise1714252015-04-28 23:09:02 -0400877 usrp_dev->set_rx_bandwidth(500*1000*2, i);
878 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800879 } else if (dev_type == LIMESDR) {
880 for (size_t i = 0; i < chans; i++) {
881 usrp_dev->set_tx_bandwidth(5e6, i);
882 usrp_dev->set_rx_bandwidth(5e6, i);
883 }
Alexander Chemerise1714252015-04-28 23:09:02 -0400884 }
885
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400886 /* Create TX and RX streamers */
887 uhd::stream_args_t stream_args("sc16");
888 for (size_t i = 0; i < chans; i++)
889 stream_args.channels.push_back(i);
890
891 tx_stream = usrp_dev->get_tx_stream(stream_args);
892 rx_stream = usrp_dev->get_rx_stream(stream_args);
893
894 /* Number of samples per over-the-wire packet */
895 tx_spp = tx_stream->get_max_num_samps();
896 rx_spp = rx_stream->get_max_num_samps();
897
kurtis.heimerl965e7572011-11-26 03:16:54 +0000898 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400899 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400900 for (size_t i = 0; i < rx_buffers.size(); i++)
901 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000902
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800903 // Set receive chain sample offset. Trigger the EDGE offset
904 // table by checking for 4 SPS on the receive path. No other
905 // configuration supports using 4 SPS.
Tom Tsou05c6feb2016-06-22 16:09:44 -0700906 double offset = get_dev_offset();
Thomas Tsoue3e88142013-04-05 20:42:41 -0400907 if (offset == 0.0) {
908 LOG(ERR) << "Unsupported configuration, no correction applied";
909 ts_offset = 0;
910 } else {
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400911 ts_offset = (TIMESTAMP) (offset * rx_rate);
Thomas Tsoue3e88142013-04-05 20:42:41 -0400912 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000913
kurtis.heimerl02d04052011-11-26 03:17:10 +0000914 // Initialize and shadow gain values
915 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000916
kurtis.heimerl965e7572011-11-26 03:16:54 +0000917 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000918 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000919
Tom Tsou05c6feb2016-06-22 16:09:44 -0700920 if (iface == DIVERSITY)
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500921 return DIVERSITY;
Tom Tsou76764272016-06-24 14:25:39 -0700922 if (iface == MULTI_ARFCN)
923 return MULTI_ARFCN;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500924
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400925 switch (dev_type) {
926 case B100:
927 return RESAMP_64M;
928 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800929 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400930 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500931 case B200:
932 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100933 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800934 case E3XX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800935 case LIMESDR:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500936 default:
937 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400938 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400939
940 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000941}
942
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000943bool uhd_device::flush_recv(size_t num_pkts)
944{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000945 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000946 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800947 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000948
Thomas Tsou18d3b832014-02-13 14:55:23 -0500949 std::vector<std::vector<short> >
950 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000951
Thomas Tsou18d3b832014-02-13 14:55:23 -0500952 std::vector<short *> pkt_ptrs;
953 for (size_t i = 0; i < pkt_bufs.size(); i++)
954 pkt_ptrs.push_back(&pkt_bufs[i].front());
955
956 ts_initial = 0;
957 while (!ts_initial || (num_pkts-- > 0)) {
958 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400959 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000960 if (!num_smpls) {
961 switch (md.error_code) {
962 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800963 LOG(ALERT) << "Device timed out";
964 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000965 default:
966 continue;
967 }
968 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500969
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700970 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000971 }
972
Thomas Tsou18d3b832014-02-13 14:55:23 -0500973 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
974
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000975 return true;
976}
977
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800978bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000979{
Thomas Tsouc2839162014-03-27 13:52:58 -0400980 /* Allow 100 ms delay to align multi-channel streams */
981 double delay = 0.1;
982
kurtis.heimerl68292102011-11-26 03:17:28 +0000983 aligned = false;
984
Thomas Tsouc2839162014-03-27 13:52:58 -0400985 uhd::time_spec_t current = usrp_dev->get_time_now();
986
Thomas Tsou204a9f12013-10-29 18:34:16 -0400987 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400988 cmd.stream_now = false;
989 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400990
kurtis.heimerl68292102011-11-26 03:17:28 +0000991 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500992
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800993 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000994}
995
kurtis.heimerl965e7572011-11-26 03:16:54 +0000996bool uhd_device::start()
997{
998 LOG(INFO) << "Starting USRP...";
999
1000 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001001 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001002 return false;
1003 }
1004
Tom Tsou72bf7622017-03-07 14:16:46 -08001005#ifndef USE_UHD_3_11
Thomas Tsou61b4a6a2013-10-15 20:31:44 -04001006 // Register msg handler
1007 uhd::msg::register_handler(&uhd_msg_handler);
Tom Tsou72bf7622017-03-07 14:16:46 -08001008#endif
kurtis.heimerl965e7572011-11-26 03:16:54 +00001009 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001010 async_event_thrd = new Thread();
1011 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001012
1013 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001014 if (!restart())
1015 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +00001016
kurtis.heimerl965e7572011-11-26 03:16:54 +00001017 // Display usrp time
1018 double time_now = usrp_dev->get_time_now().get_real_secs();
1019 LOG(INFO) << "The current time is " << time_now << " seconds";
1020
1021 started = true;
1022 return true;
1023}
1024
1025bool uhd_device::stop()
1026{
Thomas Tsou2c791102013-11-15 23:00:28 -05001027 if (!started)
1028 return false;
1029
1030 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +00001031 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
1032
1033 usrp_dev->issue_stream_cmd(stream_cmd);
1034
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001035 async_event_thrd->cancel();
1036 async_event_thrd->join();
1037 delete async_event_thrd;
1038
kurtis.heimerl965e7572011-11-26 03:16:54 +00001039 started = false;
1040 return true;
1041}
1042
Thomas Tsou7553aa92013-11-08 12:50:03 -05001043void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001044{
Thomas Tsou7553aa92013-11-08 12:50:03 -05001045 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001046 return;
1047}
1048
kurtis.heimerld4be0742011-11-26 03:17:46 +00001049int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001050{
kurtis.heimerld4be0742011-11-26 03:17:46 +00001051 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001052 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +00001053
1054 switch (md.error_code) {
1055 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +00001056 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001057 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +00001058 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1059 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1060 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1061 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1062 default:
1063 return ERROR_UNHANDLED;
1064 }
1065 }
1066
kurtis.heimerl965e7572011-11-26 03:16:54 +00001067 // Missing timestamp
1068 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +00001069 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +00001070 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001071 }
1072
1073 // Monotonicity check
Tom Tsoua93f7892017-03-07 17:54:06 -08001074 if (md.time_spec < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +00001075 LOG(ALERT) << "UHD: Loss of monotonic time";
Tom Tsoua93f7892017-03-07 17:54:06 -08001076 LOG(ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
ttsou724eb362012-03-13 02:20:01 +00001077 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +00001078 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001079 }
1080
Tom Tsoua93f7892017-03-07 17:54:06 -08001081 // Workaround for UHD tick rounding bug
1082 TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
1083 if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
1084 md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
1085
1086 prev_ts = md.time_spec;
1087
kurtis.heimerl965e7572011-11-26 03:16:54 +00001088 return 0;
1089}
1090
Thomas Tsou204a9f12013-10-29 18:34:16 -04001091int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
1092 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001093{
1094 ssize_t rc;
1095 uhd::time_spec_t ts;
1096 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001097
Thomas Tsou204a9f12013-10-29 18:34:16 -04001098 if (bufs.size() != chans) {
1099 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1100 return -1;
1101 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001102
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001103 *overrun = false;
1104 *underrun = false;
1105
kurtis.heimerl965e7572011-11-26 03:16:54 +00001106 // Shift read time with respect to transmit clock
1107 timestamp += ts_offset;
1108
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001109 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001110 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001111
1112 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -04001113 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001114 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001115 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001116 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001117 return 0;
1118 }
1119
Thomas Tsou204a9f12013-10-29 18:34:16 -04001120 // Create vector buffer
1121 std::vector<std::vector<short> >
1122 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
1123
1124 std::vector<short *> pkt_ptrs;
1125 for (size_t i = 0; i < pkt_bufs.size(); i++)
1126 pkt_ptrs.push_back(&pkt_bufs[i].front());
1127
kurtis.heimerl965e7572011-11-26 03:16:54 +00001128 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -04001129 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001130 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001131 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
1132 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001133 thread_enable_cancel(true);
1134
kurtis.heimerl965e7572011-11-26 03:16:54 +00001135 rx_pkt_cnt++;
1136
kurtis.heimerld4be0742011-11-26 03:17:46 +00001137 // Check for errors
1138 rc = check_rx_md_err(metadata, num_smpls);
1139 switch (rc) {
1140 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +00001141 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
1142 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +00001143 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001144 case ERROR_TIMEOUT:
1145 // Assume stopping condition
1146 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +00001147 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -04001148 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +00001149 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +00001150 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001151 }
1152
kurtis.heimerl965e7572011-11-26 03:16:54 +00001153 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001154 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +00001155
Thomas Tsou204a9f12013-10-29 18:34:16 -04001156 for (size_t i = 0; i < rx_buffers.size(); i++) {
1157 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
1158 num_smpls,
1159 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001160
Thomas Tsou204a9f12013-10-29 18:34:16 -04001161 // Continue on local overrun, exit on other errors
1162 if ((rc < 0)) {
1163 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001164 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001165 if (rc != smpl_buf::ERROR_OVERFLOW)
1166 return 0;
1167 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001168 }
1169 }
1170
1171 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -04001172 for (size_t i = 0; i < rx_buffers.size(); i++) {
1173 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
1174 if ((rc < 0) || (rc != len)) {
1175 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001176 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001177 return 0;
1178 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001179 }
1180
1181 return len;
1182}
1183
Thomas Tsou204a9f12013-10-29 18:34:16 -04001184int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
1185 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001186{
1187 uhd::tx_metadata_t metadata;
1188 metadata.has_time_spec = true;
1189 metadata.start_of_burst = false;
1190 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001191 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001192
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001193 *underrun = false;
1194
kurtis.heimerl965e7572011-11-26 03:16:54 +00001195 // No control packets
1196 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001197 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001198 return 0;
1199 }
1200
Thomas Tsou204a9f12013-10-29 18:34:16 -04001201 if (bufs.size() != chans) {
1202 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1203 return -1;
1204 }
1205
kurtis.heimerl965e7572011-11-26 03:16:54 +00001206 // Drop a fixed number of packets (magic value)
1207 if (!aligned) {
1208 drop_cnt++;
1209
1210 if (drop_cnt == 1) {
1211 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001212 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001213 metadata.end_of_burst = true;
1214 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001215 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001216 return len;
1217 } else {
1218 LOG(DEBUG) << "Aligning transmitter: start burst";
1219 metadata.start_of_burst = true;
1220 aligned = true;
1221 drop_cnt = 0;
1222 }
1223 }
1224
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001225 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001226 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001227 thread_enable_cancel(true);
1228
ttsoub371ed52012-01-09 18:11:34 +00001229 if (num_smpls != (unsigned) len) {
1230 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001231 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001232
1233 return num_smpls;
1234}
1235
1236bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1237{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001238 return true;
1239}
1240
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001241uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1242{
1243 double rf_spread, rf_freq;
1244 std::vector<double> freqs;
1245 uhd::tune_request_t treq(freq);
1246
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001247 if (dev_type == UMTRX) {
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001248 if (offset != 0.0)
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001249 return uhd::tune_request_t(freq, offset);
1250
1251 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1252 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1253 // only distort the signal (because cordic is not ideal).
1254 treq.target_freq = freq;
1255 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1256 treq.rf_freq = freq;
1257 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1258 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001259 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001260 } else if (chans == 1) {
Thomas Tsou8e17df72014-03-06 14:16:11 -05001261 if (offset == 0.0)
1262 return treq;
1263
1264 return uhd::tune_request_t(freq, offset);
1265 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001266 LOG(ALERT) << chans << " channels unsupported";
1267 return treq;
1268 }
1269
1270 if (tx)
1271 freqs = tx_freqs;
1272 else
1273 freqs = rx_freqs;
1274
1275 /* Tune directly if other channel isn't tuned */
1276 if (freqs[!chan] < 10.0)
1277 return treq;
1278
1279 /* Find center frequency between channels */
1280 rf_spread = fabs(freqs[!chan] - freq);
1281 if (rf_spread > B2XX_CLK_RT) {
1282 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1283 return treq;
1284 }
1285
1286 rf_freq = (freqs[!chan] + freq) / 2.0f;
1287
1288 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1289 treq.target_freq = freq;
1290 treq.rf_freq = rf_freq;
1291
1292 return treq;
1293}
1294
1295bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1296{
1297 std::vector<double> freqs;
1298 uhd::tune_result_t tres;
1299 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1300
1301 if (tx) {
1302 tres = usrp_dev->set_tx_freq(treq, chan);
1303 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1304 } else {
1305 tres = usrp_dev->set_rx_freq(treq, chan);
1306 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1307 }
1308 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1309
Thomas Tsou8e17df72014-03-06 14:16:11 -05001310 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1311 return true;
1312
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001313 /* Manual RF policy means we intentionally tuned with a baseband
1314 * offset for dual-channel purposes. Now retune the other channel
1315 * with the opposite corresponding frequency offset
1316 */
1317 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1318 if (tx) {
1319 treq = select_freq(tx_freqs[!chan], !chan, true);
1320 tres = usrp_dev->set_tx_freq(treq, !chan);
1321 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1322 } else {
1323 treq = select_freq(rx_freqs[!chan], !chan, false);
1324 tres = usrp_dev->set_rx_freq(treq, !chan);
1325 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1326
1327 }
1328 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1329 }
1330
1331 return true;
1332}
1333
Thomas Tsou204a9f12013-10-29 18:34:16 -04001334bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001335{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001336 if (chan >= tx_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, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001343}
1344
Thomas Tsou204a9f12013-10-29 18:34:16 -04001345bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001346{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001347 if (chan >= rx_freqs.size()) {
1348 LOG(ALERT) << "Requested non-existent channel " << chan;
1349 return false;
1350 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001351 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001352
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001353 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001354}
1355
Thomas Tsou204a9f12013-10-29 18:34:16 -04001356double uhd_device::getTxFreq(size_t chan)
1357{
1358 if (chan >= tx_freqs.size()) {
1359 LOG(ALERT) << "Requested non-existent channel " << chan;
1360 return 0.0;
1361 }
1362
1363 return tx_freqs[chan];
1364}
1365
1366double uhd_device::getRxFreq(size_t chan)
1367{
1368 if (chan >= rx_freqs.size()) {
1369 LOG(ALERT) << "Requested non-existent channel " << chan;
1370 return 0.0;
1371 }
1372
1373 return rx_freqs[chan];
1374}
1375
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001376/*
1377 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1378 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1379 * combination.
1380 */
1381TIMESTAMP uhd_device::initialWriteTimestamp()
1382{
Tom Tsou76764272016-06-24 14:25:39 -07001383 if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001384 return ts_initial;
1385 else
1386 return ts_initial * tx_sps;
1387}
1388
1389TIMESTAMP uhd_device::initialReadTimestamp()
1390{
1391 return ts_initial;
1392}
1393
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001394double uhd_device::fullScaleInputValue()
1395{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001396 if (dev_type == LIMESDR)
1397 return (double) 2047 * LIMESDR_TX_AMPL;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001398 if (dev_type == UMTRX)
1399 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1400 else
1401 return (double) SHRT_MAX * USRP_TX_AMPL;
1402}
1403
1404double uhd_device::fullScaleOutputValue()
1405{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001406 if (dev_type == LIMESDR) return (double) 2047;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001407 return (double) SHRT_MAX;
1408}
1409
kurtis.heimerl965e7572011-11-26 03:16:54 +00001410bool uhd_device::recv_async_msg()
1411{
ttsou60dc4c92012-08-08 23:30:23 +00001412 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001413
1414 thread_enable_cancel(false);
1415 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1416 thread_enable_cancel(true);
1417 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001418 return false;
1419
1420 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001421 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001422 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001423
1424 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1425 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1426 LOG(ERR) << str_code(md);
1427 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001428 }
1429
1430 return true;
1431}
1432
1433std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1434{
1435 std::ostringstream ost("UHD: ");
1436
1437 switch (metadata.error_code) {
1438 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1439 ost << "No error";
1440 break;
1441 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1442 ost << "No packet received, implementation timed-out";
1443 break;
1444 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1445 ost << "A stream command was issued in the past";
1446 break;
1447 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1448 ost << "Expected another stream command";
1449 break;
1450 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1451 ost << "An internal receive buffer has filled";
1452 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001453 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1454 ost << "Multi-channel alignment failed";
1455 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001456 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1457 ost << "The packet could not be parsed";
1458 break;
1459 default:
1460 ost << "Unknown error " << metadata.error_code;
1461 }
1462
1463 if (metadata.has_time_spec)
1464 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1465
1466 return ost.str();
1467}
1468
1469std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1470{
1471 std::ostringstream ost("UHD: ");
1472
1473 switch (metadata.event_code) {
1474 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1475 ost << "A packet was successfully transmitted";
1476 break;
1477 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1478 ost << "An internal send buffer has emptied";
1479 break;
1480 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1481 ost << "Packet loss between host and device";
1482 break;
1483 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1484 ost << "Packet time was too late or too early";
1485 break;
1486 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1487 ost << "Underflow occurred inside a packet";
1488 break;
1489 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1490 ost << "Packet loss within a burst";
1491 break;
1492 default:
1493 ost << "Unknown error " << metadata.event_code;
1494 }
1495
1496 if (metadata.has_time_spec)
1497 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1498
1499 return ost.str();
1500}
1501
1502smpl_buf::smpl_buf(size_t len, double rate)
1503 : buf_len(len), clk_rt(rate),
1504 time_start(0), time_end(0), data_start(0), data_end(0)
1505{
1506 data = new uint32_t[len];
1507}
1508
1509smpl_buf::~smpl_buf()
1510{
1511 delete[] data;
1512}
1513
1514ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1515{
1516 if (timestamp < time_start)
1517 return ERROR_TIMESTAMP;
1518 else if (timestamp >= time_end)
1519 return 0;
1520 else
1521 return time_end - timestamp;
1522}
1523
1524ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1525{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001526 return avail_smpls(timespec.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001527}
1528
1529ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1530{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001531 int type_sz = 2 * sizeof(short);
1532
kurtis.heimerl965e7572011-11-26 03:16:54 +00001533 // Check for valid read
1534 if (timestamp < time_start)
1535 return ERROR_TIMESTAMP;
1536 if (timestamp >= time_end)
1537 return 0;
1538 if (len >= buf_len)
1539 return ERROR_READ;
1540
1541 // How many samples should be copied
1542 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001543 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001544 num_smpls = len;
1545
1546 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001547 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001548
1549 // Read it
1550 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001551 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001552 memcpy(buf, data + read_start, numBytes);
1553 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001554 size_t first_cp = (buf_len - read_start) * type_sz;
1555 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001556
1557 memcpy(buf, data + read_start, first_cp);
1558 memcpy((char*) buf + first_cp, data, second_cp);
1559 }
1560
1561 data_start = (read_start + len) % buf_len;
1562 time_start = timestamp + len;
1563
1564 if (time_start > time_end)
1565 return ERROR_READ;
1566 else
1567 return num_smpls;
1568}
1569
1570ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1571{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001572 return read(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001573}
1574
1575ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1576{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001577 int type_sz = 2 * sizeof(short);
1578
kurtis.heimerl965e7572011-11-26 03:16:54 +00001579 // Check for valid write
1580 if ((len == 0) || (len >= buf_len))
1581 return ERROR_WRITE;
1582 if ((timestamp + len) <= time_end)
1583 return ERROR_TIMESTAMP;
1584
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001585 if (timestamp < time_end) {
1586 LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001587 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1588 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 -04001589 // Do not return error here, because it's a rounding error and is not fatal
1590 }
1591 if (timestamp > time_end && time_end != 0) {
1592 LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001593 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1594 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 -04001595 // Do not return error here, because it's a rounding error and is not fatal
1596 }
1597
kurtis.heimerl965e7572011-11-26 03:16:54 +00001598 // Starting index
1599 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1600
1601 // Write it
1602 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001603 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001604 memcpy(data + write_start, buf, numBytes);
1605 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001606 size_t first_cp = (buf_len - write_start) * type_sz;
1607 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001608
1609 memcpy(data + write_start, buf, first_cp);
1610 memcpy(data, (char*) buf + first_cp, second_cp);
1611 }
1612
1613 data_end = (write_start + len) % buf_len;
1614 time_end = timestamp + len;
1615
Thomas Tsou18d3b832014-02-13 14:55:23 -05001616 if (!data_start)
1617 data_start = write_start;
1618
kurtis.heimerl965e7572011-11-26 03:16:54 +00001619 if (((write_start + len) > buf_len) && (data_end > data_start))
1620 return ERROR_OVERFLOW;
1621 else if (time_end <= time_start)
1622 return ERROR_WRITE;
1623 else
1624 return len;
1625}
1626
1627ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1628{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001629 return write(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001630}
1631
Tom Tsou1ae25562014-12-05 12:56:34 -08001632std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001633{
1634 std::ostringstream ost("Sample buffer: ");
1635
Tom Tsou1ae25562014-12-05 12:56:34 -08001636 ost << "timestamp = " << ts;
1637 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001638 ost << ", time_start = " << time_start;
1639 ost << ", time_end = " << time_end;
1640 ost << ", data_start = " << data_start;
1641 ost << ", data_end = " << data_end;
1642
1643 return ost.str();
1644}
1645
1646std::string smpl_buf::str_code(ssize_t code)
1647{
1648 switch (code) {
1649 case ERROR_TIMESTAMP:
1650 return "Sample buffer: Requested timestamp is not valid";
1651 case ERROR_READ:
1652 return "Sample buffer: Read error";
1653 case ERROR_WRITE:
1654 return "Sample buffer: Write error";
1655 case ERROR_OVERFLOW:
1656 return "Sample buffer: Overrun";
1657 default:
1658 return "Sample buffer: Unknown error";
1659 }
1660}
1661
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001662RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
Tom Tsou05c6feb2016-06-22 16:09:44 -07001663 InterfaceType iface, size_t chans, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001664{
Tom Tsou05c6feb2016-06-22 16:09:44 -07001665 return new uhd_device(tx_sps, rx_sps, iface, chans, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001666}