blob: 09317a9d3447e3f13b92bbe3b3ac41c8513c5de8 [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
Tom Tsou1fb0ce62017-06-08 19:41:48 -070024#include <map>
kurtis.heimerl965e7572011-11-26 03:16:54 +000025#include "radioDevice.h"
26#include "Threads.h"
27#include "Logger.h"
ttsoub371ed52012-01-09 18:11:34 +000028#include <uhd/version.hpp>
kurtis.heimerle380af32011-11-26 03:18:55 +000029#include <uhd/property_tree.hpp>
kurtis.heimerl856bbbe2011-12-15 00:50:16 +000030#include <uhd/usrp/multi_usrp.hpp>
kurtis.heimerl965e7572011-11-26 03:16:54 +000031#include <uhd/utils/thread_priority.hpp>
32
kurtis.heimerlce317332011-11-26 03:18:39 +000033#ifdef HAVE_CONFIG_H
34#include "config.h"
35#endif
36
Tom Tsou72bf7622017-03-07 14:16:46 -080037#ifndef USE_UHD_3_11
38#include <uhd/utils/msg.hpp>
39#endif
40
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +030041#define USRP_TX_AMPL 0.3
42#define UMTRX_TX_AMPL 0.7
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -080043#define LIMESDR_TX_AMPL 0.3
Thomas Tsoue3e88142013-04-05 20:42:41 -040044#define SAMPLE_BUF_SZ (1 << 20)
Thomas Tsou02d88d12013-04-05 15:36:30 -040045
Tom Tsoueb54bdd2014-11-25 16:06:32 -080046/*
47 * UHD timeout value on streaming (re)start
48 *
49 * Allow some time for streaming to commence after the start command is issued,
50 * but consider a wait beyond one second to be a definite error condition.
51 */
52#define UHD_RESTART_TIMEOUT 1.0
53
Alexander Chemeris4d029d82014-04-19 17:25:00 +040054/*
55 * UmTRX specific settings
56 */
57#define UMTRX_VGA1_DEF -18
58
Thomas Tsou02d88d12013-04-05 15:36:30 -040059enum uhd_dev_type {
60 USRP1,
61 USRP2,
62 B100,
Thomas Tsoue7882392014-02-13 14:46:23 -050063 B200,
64 B210,
Tom Tsou76764272016-06-24 14:25:39 -070065 B2XX_MCBTS,
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010066 E1XX,
Tom Tsou4ad9ea62014-12-03 18:47:20 -080067 E3XX,
68 X3XX,
Thomas Tsouc88d8d52013-08-21 17:55:54 -040069 UMTRX,
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -080070 LIMESDR,
Thomas Tsoue3e88142013-04-05 20:42:41 -040071};
72
kurtis.heimerl965e7572011-11-26 03:16:54 +000073/*
Tom Tsouc3129052015-08-21 18:28:52 -070074 * USRP version dependent device timings
75 */
Tom Tsou72bf7622017-03-07 14:16:46 -080076#if defined(USE_UHD_3_9) || defined(USE_UHD_3_11)
Tom Tsouc3129052015-08-21 18:28:52 -070077#define B2XX_TIMING_1SPS 1.7153e-4
78#define B2XX_TIMING_4SPS 1.1696e-4
Tom Tsoud2b07032016-04-26 19:28:59 -070079#define B2XX_TIMING_4_4SPS 6.18462e-5
Tom Tsoua93f7892017-03-07 17:54:06 -080080#define B2XX_TIMING_MCBTS 7e-5
Tom Tsou80cb0802017-01-19 13:44:02 -080081#else
82#define B2XX_TIMING_1SPS 9.9692e-5
83#define B2XX_TIMING_4SPS 6.9248e-5
84#define B2XX_TIMING_4_4SPS 4.52308e-5
Tom Tsoua93f7892017-03-07 17:54:06 -080085#define B2XX_TIMING_MCBTS 6.42452e-5
Tom Tsou80cb0802017-01-19 13:44:02 -080086#endif
Tom Tsouc3129052015-08-21 18:28:52 -070087
88/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040089 * Tx / Rx sample offset values. In a perfect world, there is no group delay
90 * though analog components, and behaviour through digital filters exactly
91 * matches calculated values. In reality, there are unaccounted factors,
92 * which are captured in these empirically measured (using a loopback test)
93 * timing correction values.
94 *
95 * Notes:
96 * USRP1 with timestamps is not supported by UHD.
97 */
Tom Tsou1fb0ce62017-06-08 19:41:48 -070098
99/* Device Type, Tx-SPS, Rx-SPS */
100typedef std::tuple<uhd_dev_type, int, int> dev_key;
101
102/* Device parameter descriptor */
103struct dev_desc {
104 unsigned channels;
105 double mcr;
106 double rate;
107 double offset;
108 std::string str;
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800109};
kurtis.heimerl965e7572011-11-26 03:16:54 +0000110
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700111static const std::map<dev_key, dev_desc> dev_param_map {
112 { std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } },
113 { std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } },
114 { std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } },
115 { std::make_tuple(B100, 1, 1), { 1, 0.0, 400000, 1.2104e-4, "B100 1 SPS" } },
116 { std::make_tuple(B100, 4, 1), { 1, 0.0, 400000, 7.9307e-5, "B100 4/1 Tx/Rx SPS" } },
117 { std::make_tuple(B200, 1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS" } },
118 { std::make_tuple(B200, 4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } },
119 { std::make_tuple(B200, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
120 { std::make_tuple(B210, 1, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B210 1 SPS" } },
121 { std::make_tuple(B210, 4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" } },
122 { std::make_tuple(B210, 4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B210 4 SPS" } },
123 { std::make_tuple(E1XX, 1, 1), { 1, 52e6, GSMRATE, 9.5192e-5, "E1XX 1 SPS" } },
124 { std::make_tuple(E1XX, 4, 1), { 1, 52e6, GSMRATE, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" } },
125 { std::make_tuple(E3XX, 1, 1), { 2, 26e6, GSMRATE, 1.8462e-4, "E3XX 1 SPS" } },
126 { std::make_tuple(E3XX, 4, 1), { 2, 26e6, GSMRATE, 1.2923e-4, "E3XX 4/1 Tx/Rx SPS" } },
127 { std::make_tuple(X3XX, 1, 1), { 2, 0.0, 390625, 1.5360e-4, "X3XX 1 SPS" } },
128 { std::make_tuple(X3XX, 4, 1), { 2, 0.0, 390625, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS" } },
129 { std::make_tuple(X3XX, 4, 4), { 2, 0.0, 390625, 5.6567e-5, "X3XX 4 SPS" } },
130 { std::make_tuple(UMTRX, 1, 1), { 2, 0.0, GSMRATE, 9.9692e-5, "UmTRX 1 SPS" } },
131 { std::make_tuple(UMTRX, 4, 1), { 2, 0.0, GSMRATE, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS"} },
132 { std::make_tuple(UMTRX, 4, 4), { 2, 0.0, GSMRATE, 5.1503e-5, "UmTRX 4 SPS" } },
Tom Tsou4cafb0f2017-06-26 10:13:25 -0700133 { std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS" } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700134 { std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
135};
Thomas Tsoucb69f082013-04-08 14:18:26 -0400136
kurtis.heimerl965e7572011-11-26 03:16:54 +0000137/*
Alexander Chemeris1ba69e72016-06-18 10:50:11 +0300138 Sample Buffer - Allows reading and writing of timed samples using osmo-trx
kurtis.heimerl965e7572011-11-26 03:16:54 +0000139 or UHD style timestamps. Time conversions are handled
140 internally or accessable through the static convert calls.
141*/
142class smpl_buf {
143public:
144 /** Sample buffer constructor
145 @param len number of 32-bit samples the buffer should hold
146 @param rate sample clockrate
147 @param timestamp
148 */
149 smpl_buf(size_t len, double rate);
150 ~smpl_buf();
151
152 /** Query number of samples available for reading
153 @param timestamp time of first sample
154 @return number of available samples or error
155 */
156 ssize_t avail_smpls(TIMESTAMP timestamp) const;
157 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
158
159 /** Read and write
160 @param buf pointer to buffer
161 @param len number of samples desired to read or write
162 @param timestamp time of first stample
163 @return number of actual samples read or written or error
164 */
165 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
166 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
167 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
168 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
169
170 /** Buffer status string
171 @return a formatted string describing internal buffer state
172 */
Tom Tsou1ae25562014-12-05 12:56:34 -0800173 std::string str_status(size_t ts) const;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000174
175 /** Formatted error string
176 @param code an error code
177 @return a formatted error string
178 */
179 static std::string str_code(ssize_t code);
180
181 enum err_code {
182 ERROR_TIMESTAMP = -1,
183 ERROR_READ = -2,
184 ERROR_WRITE = -3,
185 ERROR_OVERFLOW = -4
186 };
187
188private:
189 uint32_t *data;
190 size_t buf_len;
191
192 double clk_rt;
193
194 TIMESTAMP time_start;
195 TIMESTAMP time_end;
196
197 size_t data_start;
198 size_t data_end;
199};
200
201/*
202 uhd_device - UHD implementation of the Device interface. Timestamped samples
203 are sent to and received from the device. An intermediate buffer
204 on the receive side collects and aligns packets of samples.
205 Events and errors such as underruns are reported asynchronously
206 by the device and received in a separate thread.
207*/
208class uhd_device : public RadioDevice {
209public:
Tom Tsou05c6feb2016-06-22 16:09:44 -0700210 uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
211 size_t chans, double offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000212 ~uhd_device();
213
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700214 int open(const std::string &args, int ref, bool swap_channels);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000215 bool start();
216 bool stop();
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800217 bool restart();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500218 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400219 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000220
Thomas Tsou204a9f12013-10-29 18:34:16 -0400221 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000222 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
223
Thomas Tsou204a9f12013-10-29 18:34:16 -0400224 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000225 TIMESTAMP timestamp, bool isControl);
226
227 bool updateAlignment(TIMESTAMP timestamp);
228
Thomas Tsou204a9f12013-10-29 18:34:16 -0400229 bool setTxFreq(double wFreq, size_t chan);
230 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000231
Tom Tsou05c6feb2016-06-22 16:09:44 -0700232 TIMESTAMP initialWriteTimestamp();
233 TIMESTAMP initialReadTimestamp();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000234
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +0300235 double fullScaleInputValue();
236 double fullScaleOutputValue();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000237
Thomas Tsou204a9f12013-10-29 18:34:16 -0400238 double setRxGain(double db, size_t chan);
239 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000240 double maxRxGain(void) { return rx_gain_max; }
241 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000242
Thomas Tsou204a9f12013-10-29 18:34:16 -0400243 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000244 double maxTxGain(void) { return tx_gain_max; }
245 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000246
Thomas Tsou204a9f12013-10-29 18:34:16 -0400247 double getTxFreq(size_t chan);
248 double getRxFreq(size_t chan);
249 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000250
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400251 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000252 inline double numberRead() { return rx_pkt_cnt; }
253 inline double numberWritten() { return 0; }
254
255 /** Receive and process asynchronous message
256 @return true if message received or false on timeout or error
257 */
258 bool recv_async_msg();
259
kurtis.heimerld4be0742011-11-26 03:17:46 +0000260 enum err_code {
261 ERROR_TIMING = -1,
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800262 ERROR_TIMEOUT = -2,
263 ERROR_UNRECOVERABLE = -3,
264 ERROR_UNHANDLED = -4,
kurtis.heimerld4be0742011-11-26 03:17:46 +0000265 };
266
kurtis.heimerl965e7572011-11-26 03:16:54 +0000267private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000268 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400269 uhd::tx_streamer::sptr tx_stream;
270 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400271 enum TxWindowType tx_window;
272 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000273
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800274 size_t tx_sps, rx_sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400275 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000276
Thomas Tsou204a9f12013-10-29 18:34:16 -0400277 double tx_gain_min, tx_gain_max;
278 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500279 double offset;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000280
Thomas Tsou204a9f12013-10-29 18:34:16 -0400281 std::vector<double> tx_gains, rx_gains;
282 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000283 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000284
285 bool started;
286 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000287
288 size_t rx_pkt_cnt;
289 size_t drop_cnt;
290 uhd::time_spec_t prev_ts;
291
Thomas Tsou18d3b832014-02-13 14:55:23 -0500292 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400293 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000294
kurtis.heimerl02d04052011-11-26 03:17:10 +0000295 void init_gains();
Tom Tsou980525c2017-06-09 15:37:19 -0700296 void set_channels(bool swap);
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700297 void set_rates();
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000298 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000299 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000300 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000301
kurtis.heimerl965e7572011-11-26 03:16:54 +0000302 std::string str_code(uhd::rx_metadata_t metadata);
303 std::string str_code(uhd::async_metadata_t metadata);
304
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500305 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
306 bool set_freq(double freq, size_t chan, bool tx);
307
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800308 Thread *async_event_thrd;
Tom Tsou05c6feb2016-06-22 16:09:44 -0700309 InterfaceType iface;
Tom Tsou93b7f372014-12-15 20:23:33 -0800310 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000311};
312
313void *async_event_loop(uhd_device *dev)
314{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500315 dev->setPriority(0.43);
316
kurtis.heimerl965e7572011-11-26 03:16:54 +0000317 while (1) {
318 dev->recv_async_msg();
319 pthread_testcancel();
320 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500321
322 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000323}
324
Tom Tsou72bf7622017-03-07 14:16:46 -0800325#ifndef USE_UHD_3_11
326/*
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000327 Catch and drop underrun 'U' and overrun 'O' messages from stdout
328 since we already report using the logging facility. Direct
329 everything else appropriately.
330 */
331void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
332{
333 switch (type) {
334 case uhd::msg::status:
335 LOG(INFO) << msg;
336 break;
337 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000338 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000339 break;
340 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000341 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000342 break;
343 case uhd::msg::fastpath:
344 break;
345 }
346}
Tom Tsou72bf7622017-03-07 14:16:46 -0800347#endif
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000348
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800349static void thread_enable_cancel(bool cancel)
350{
351 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
352 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
353}
354
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800355uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
Tom Tsou05c6feb2016-06-22 16:09:44 -0700356 InterfaceType iface, size_t chans, double offset)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400357 : tx_gain_min(0.0), tx_gain_max(0.0),
358 rx_gain_min(0.0), rx_gain_max(0.0),
359 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000360 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou18d3b832014-02-13 14:55:23 -0500361 prev_ts(0,0), ts_initial(0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000362{
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800363 this->tx_sps = tx_sps;
364 this->rx_sps = rx_sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400365 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500366 this->offset = offset;
Tom Tsou05c6feb2016-06-22 16:09:44 -0700367 this->iface = iface;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000368}
369
370uhd_device::~uhd_device()
371{
372 stop();
373
Thomas Tsou204a9f12013-10-29 18:34:16 -0400374 for (size_t i = 0; i < rx_buffers.size(); i++)
375 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000376}
377
kurtis.heimerl24481de2011-11-26 03:17:18 +0000378void uhd_device::init_gains()
379{
380 uhd::gain_range_t range;
381
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400382 if (dev_type == UMTRX) {
383 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
384 if (gain_stages[0] == "VGA") {
385 LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
386 }
387 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
388 range = usrp_dev->get_tx_gain_range();
389 tx_gain_min = range.start();
390 tx_gain_max = range.stop();
391 } else {
392 range = usrp_dev->get_tx_gain_range("VGA2");
393 tx_gain_min = UMTRX_VGA1_DEF + range.start();
394 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
395 }
396 } else {
397 range = usrp_dev->get_tx_gain_range();
398 tx_gain_min = range.start();
399 tx_gain_max = range.stop();
400 }
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400401 LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000402
403 range = usrp_dev->get_rx_gain_range();
404 rx_gain_min = range.start();
405 rx_gain_max = range.stop();
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400406 LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000407
Thomas Tsou204a9f12013-10-29 18:34:16 -0400408 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400409 double gain = (tx_gain_min + tx_gain_max) / 2;
410 LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
411 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400412 tx_gains[i] = usrp_dev->get_tx_gain(i);
413 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000414
Thomas Tsou204a9f12013-10-29 18:34:16 -0400415 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400416 double gain = (rx_gain_min + rx_gain_max) / 2;
417 LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
418 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400419 rx_gains[i] = usrp_dev->get_rx_gain(i);
420 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000421
422 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400423
kurtis.heimerl24481de2011-11-26 03:17:18 +0000424}
425
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700426void uhd_device::set_rates()
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700427{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700428 dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
429 if (desc.mcr != 0.0)
430 usrp_dev->set_master_clock_rate(desc.mcr);
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700431
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700432 tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate;
433 rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate;
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700434
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700435 usrp_dev->set_tx_rate(tx_rate);
436 usrp_dev->set_rx_rate(rx_rate);
437 tx_rate = usrp_dev->get_tx_rate();
438 rx_rate = usrp_dev->get_rx_rate();
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700439
Tom Tsou1b6ab7d2017-06-15 15:31:08 -0700440 ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700441 LOG(INFO) << "Rates configured for " << desc.str;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000442}
443
Thomas Tsou204a9f12013-10-29 18:34:16 -0400444double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000445{
Tom Tsou76764272016-06-24 14:25:39 -0700446 if (iface == MULTI_ARFCN)
447 chan = 0;
448
Thomas Tsou204a9f12013-10-29 18:34:16 -0400449 if (chan >= tx_gains.size()) {
450 LOG(ALERT) << "Requested non-existent channel" << chan;
451 return 0.0f;
452 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000453
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400454 if (dev_type == UMTRX) {
455 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
456 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
457 usrp_dev->set_tx_gain(db, chan);
458 } else {
459 // New UHD versions support split configuration of
460 // Tx gain stages. We utilize this to set the gain
461 // configuration, optimal for the Tx signal quality.
462 // From our measurements, VGA1 must be 18dB plus-minus
463 // one and VGA2 is the best when 23dB or lower.
464 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
465 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
466 }
467 } else {
468 usrp_dev->set_tx_gain(db, chan);
469 }
470
Thomas Tsou204a9f12013-10-29 18:34:16 -0400471 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000472
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400473 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400474
475 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000476}
477
Thomas Tsou204a9f12013-10-29 18:34:16 -0400478double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000479{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400480 if (chan >= rx_gains.size()) {
481 LOG(ALERT) << "Requested non-existent channel " << chan;
482 return 0.0f;
483 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000484
Thomas Tsou204a9f12013-10-29 18:34:16 -0400485 usrp_dev->set_rx_gain(db, chan);
486 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000487
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400488 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400489
490 return rx_gains[chan];
491}
492
493double uhd_device::getRxGain(size_t chan)
494{
Tom Tsou76764272016-06-24 14:25:39 -0700495 if (iface == MULTI_ARFCN)
496 chan = 0;
497
Thomas Tsou204a9f12013-10-29 18:34:16 -0400498 if (chan >= rx_gains.size()) {
499 LOG(ALERT) << "Requested non-existent channel " << chan;
500 return 0.0f;
501 }
502
503 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000504}
505
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000506/*
507 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400508 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000509 deal with the transport latency. Reject the USRP1 because UHD doesn't
510 support timestamped samples with it.
511 */
512bool uhd_device::parse_dev_type()
513{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700514 uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree();
515 std::string devString = prop_tree->access<std::string>("/name").get();
516 std::string mboardString = usrp_dev->get_mboard_name();
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000517
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700518 const std::map<std::string, std::pair<uhd_dev_type, TxWindowType>> devStringMap {
519 { "B100", { B100, TX_WINDOW_USRP1 } },
520 { "B200", { B200, TX_WINDOW_USRP1 } },
521 { "B200mini", { B200, TX_WINDOW_USRP1 } },
Piotr Krysikaa60dda2017-12-04 00:29:16 +0700522 { "B205mini", { B200, TX_WINDOW_USRP1 } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700523 { "B210", { B210, TX_WINDOW_USRP1 } },
524 { "E100", { E1XX, TX_WINDOW_FIXED } },
525 { "E110", { E1XX, TX_WINDOW_FIXED } },
526 { "E310", { E3XX, TX_WINDOW_FIXED } },
527 { "E3XX", { E3XX, TX_WINDOW_FIXED } },
528 { "X300", { X3XX, TX_WINDOW_FIXED } },
529 { "X310", { X3XX, TX_WINDOW_FIXED } },
Tom Tsou988a4642017-06-15 09:16:53 -0700530 { "USRP2", { USRP2, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700531 { "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
ignasj87ed77b2017-06-13 23:37:46 +0300532 { "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700533 };
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000534
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700535 // Compare UHD motherboard and device strings */
Tom Tsou988a4642017-06-15 09:16:53 -0700536 auto mapIter = devStringMap.begin();
537 while (mapIter != devStringMap.end()) {
538 if (devString.find(mapIter->first) != std::string::npos ||
539 mboardString.find(mapIter->first) != std::string::npos) {
540 dev_type = std::get<0>(mapIter->second);
541 tx_window = std::get<1>(mapIter->second);
542 return true;
543 }
544 mapIter++;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000545 }
546
Tom Tsou988a4642017-06-15 09:16:53 -0700547 LOG(ALERT) << "Unsupported device " << devString;
548 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000549}
550
Tom Tsou3b093bb2016-05-03 19:04:15 -0700551/*
552 * Check for UHD version > 3.9.0 for E3XX support
553 */
554static bool uhd_e3xx_version_chk()
555{
556 std::string ver = uhd::get_version_string();
557 std::string major_str(ver.begin(), ver.begin() + 3);
558 std::string minor_str(ver.begin() + 4, ver.begin() + 7);
559
560 int major_val = atoi(major_str.c_str());
561 int minor_val = atoi(minor_str.c_str());
562
563 if (major_val < 3)
564 return false;
565 if (minor_val < 9)
566 return false;
567
568 return true;
569}
570
Tom Tsou980525c2017-06-09 15:37:19 -0700571void uhd_device::set_channels(bool swap)
572{
573 if (iface == MULTI_ARFCN) {
574 if (dev_type != B200 && dev_type != B210)
575 throw std::invalid_argument("Device does not support MCBTS");
576 dev_type = B2XX_MCBTS;
577 chans = 1;
578 }
579
Tom Tsouf6115692017-06-26 10:13:25 -0700580 if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
Tom Tsou980525c2017-06-09 15:37:19 -0700581 throw std::invalid_argument("Device does not support number of requested channels");
582
583 std::string subdev_string;
584 switch (dev_type) {
585 case B210:
586 case E3XX:
587 if (chans == 1)
588 subdev_string = swap ? "A:B" : "A:A";
589 else if (chans == 2)
590 subdev_string = swap ? "A:B A:A" : "A:A A:B";
591 break;
592 case X3XX:
593 case UMTRX:
594 if (chans == 1)
595 subdev_string = swap ? "B:0" : "A:0";
596 else if (chans == 2)
597 subdev_string = swap ? "B:0 A:0" : "A:0 B:0";
598 break;
599 default:
600 break;
601 }
602
603 if (!subdev_string.empty()) {
604 uhd::usrp::subdev_spec_t spec(subdev_string);
605 usrp_dev->set_tx_subdev_spec(spec);
606 usrp_dev->set_rx_subdev_spec(spec);
607 }
608}
609
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700610int uhd_device::open(const std::string &args, int ref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000611{
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700612 const char *refstr;
613
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000614 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000615 uhd::device_addr_t addr(args);
616 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000617 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000618 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400619 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000620 }
621
622 // Use the first found device
623 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000624 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700625 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000626 } catch(...) {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700627 LOG(ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400628 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000629 }
630
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000631 // Check for a valid device type and set bus type
632 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400633 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000634
Tom Tsou3b093bb2016-05-03 19:04:15 -0700635 if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
636 LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater";
637 return -1;
638 }
639
Tom Tsou980525c2017-06-09 15:37:19 -0700640 try {
641 set_channels(swap_channels);
642 } catch (const std::exception &e) {
643 LOG(ALERT) << "Channel setting failed - " << e.what();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400644 return -1;
645 }
646
647 tx_freqs.resize(chans);
648 rx_freqs.resize(chans);
649 tx_gains.resize(chans);
650 rx_gains.resize(chans);
651 rx_buffers.resize(chans);
652
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700653 switch (ref) {
654 case REF_INTERNAL:
655 refstr = "internal";
656 break;
657 case REF_EXTERNAL:
658 refstr = "external";
659 break;
660 case REF_GPS:
661 refstr = "gpsdo";
662 break;
663 default:
664 LOG(ALERT) << "Invalid reference type";
665 return -1;
666 }
667
668 usrp_dev->set_clock_source(refstr);
Thomas Tsou010fff72013-10-16 00:31:18 -0400669
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700670 try {
671 set_rates();
672 } catch (const std::exception &e) {
673 LOG(ALERT) << "UHD rate setting failed - " << e.what();
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500674 return -1;
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700675 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000676
Alexander Chemerise1714252015-04-28 23:09:02 -0400677 // Set RF frontend bandwidth
678 if (dev_type == UMTRX) {
679 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
680 for (size_t i = 0; i < chans; i++) {
681 usrp_dev->set_tx_bandwidth(500*1000*2, i);
Tom Tsoud6ae8642017-03-30 17:22:58 -0700682 usrp_dev->set_rx_bandwidth(500*1000*2, i);
Alexander Chemerise1714252015-04-28 23:09:02 -0400683 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800684 } else if (dev_type == LIMESDR) {
685 for (size_t i = 0; i < chans; i++) {
686 usrp_dev->set_tx_bandwidth(5e6, i);
687 usrp_dev->set_rx_bandwidth(5e6, i);
688 }
Alexander Chemerise1714252015-04-28 23:09:02 -0400689 }
690
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400691 /* Create TX and RX streamers */
692 uhd::stream_args_t stream_args("sc16");
693 for (size_t i = 0; i < chans; i++)
694 stream_args.channels.push_back(i);
695
696 tx_stream = usrp_dev->get_tx_stream(stream_args);
697 rx_stream = usrp_dev->get_rx_stream(stream_args);
698
699 /* Number of samples per over-the-wire packet */
700 tx_spp = tx_stream->get_max_num_samps();
701 rx_spp = rx_stream->get_max_num_samps();
702
kurtis.heimerl965e7572011-11-26 03:16:54 +0000703 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400704 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400705 for (size_t i = 0; i < rx_buffers.size(); i++)
706 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000707
kurtis.heimerl02d04052011-11-26 03:17:10 +0000708 // Initialize and shadow gain values
709 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000710
kurtis.heimerl965e7572011-11-26 03:16:54 +0000711 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000712 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000713
Tom Tsou76764272016-06-24 14:25:39 -0700714 if (iface == MULTI_ARFCN)
715 return MULTI_ARFCN;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500716
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400717 switch (dev_type) {
718 case B100:
719 return RESAMP_64M;
720 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800721 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400722 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500723 case B200:
724 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100725 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800726 case E3XX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800727 case LIMESDR:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500728 default:
729 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400730 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400731
732 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000733}
734
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000735bool uhd_device::flush_recv(size_t num_pkts)
736{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000737 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000738 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800739 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000740
Thomas Tsou18d3b832014-02-13 14:55:23 -0500741 std::vector<std::vector<short> >
742 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000743
Thomas Tsou18d3b832014-02-13 14:55:23 -0500744 std::vector<short *> pkt_ptrs;
745 for (size_t i = 0; i < pkt_bufs.size(); i++)
746 pkt_ptrs.push_back(&pkt_bufs[i].front());
747
748 ts_initial = 0;
749 while (!ts_initial || (num_pkts-- > 0)) {
750 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400751 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000752 if (!num_smpls) {
753 switch (md.error_code) {
754 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800755 LOG(ALERT) << "Device timed out";
756 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000757 default:
758 continue;
759 }
760 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500761
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700762 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000763 }
764
Thomas Tsou18d3b832014-02-13 14:55:23 -0500765 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
766
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000767 return true;
768}
769
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800770bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000771{
Thomas Tsouc2839162014-03-27 13:52:58 -0400772 /* Allow 100 ms delay to align multi-channel streams */
773 double delay = 0.1;
774
kurtis.heimerl68292102011-11-26 03:17:28 +0000775 aligned = false;
776
Thomas Tsouc2839162014-03-27 13:52:58 -0400777 uhd::time_spec_t current = usrp_dev->get_time_now();
778
Thomas Tsou204a9f12013-10-29 18:34:16 -0400779 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400780 cmd.stream_now = false;
781 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400782
kurtis.heimerl68292102011-11-26 03:17:28 +0000783 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500784
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800785 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000786}
787
kurtis.heimerl965e7572011-11-26 03:16:54 +0000788bool uhd_device::start()
789{
790 LOG(INFO) << "Starting USRP...";
791
792 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000793 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000794 return false;
795 }
796
Tom Tsou72bf7622017-03-07 14:16:46 -0800797#ifndef USE_UHD_3_11
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400798 // Register msg handler
799 uhd::msg::register_handler(&uhd_msg_handler);
Tom Tsou72bf7622017-03-07 14:16:46 -0800800#endif
kurtis.heimerl965e7572011-11-26 03:16:54 +0000801 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800802 async_event_thrd = new Thread();
803 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000804
805 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800806 if (!restart())
807 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000808
kurtis.heimerl965e7572011-11-26 03:16:54 +0000809 // Display usrp time
810 double time_now = usrp_dev->get_time_now().get_real_secs();
811 LOG(INFO) << "The current time is " << time_now << " seconds";
812
813 started = true;
814 return true;
815}
816
817bool uhd_device::stop()
818{
Thomas Tsou2c791102013-11-15 23:00:28 -0500819 if (!started)
820 return false;
821
822 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000823 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
824
825 usrp_dev->issue_stream_cmd(stream_cmd);
826
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800827 async_event_thrd->cancel();
828 async_event_thrd->join();
829 delete async_event_thrd;
830
kurtis.heimerl965e7572011-11-26 03:16:54 +0000831 started = false;
832 return true;
833}
834
Thomas Tsou7553aa92013-11-08 12:50:03 -0500835void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000836{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500837 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000838 return;
839}
840
kurtis.heimerld4be0742011-11-26 03:17:46 +0000841int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000842{
kurtis.heimerld4be0742011-11-26 03:17:46 +0000843 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000844 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000845
846 switch (md.error_code) {
847 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000848 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800849 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000850 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
851 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
852 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
853 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
854 default:
855 return ERROR_UNHANDLED;
856 }
857 }
858
kurtis.heimerl965e7572011-11-26 03:16:54 +0000859 // Missing timestamp
860 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000861 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000862 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000863 }
864
865 // Monotonicity check
Tom Tsoua93f7892017-03-07 17:54:06 -0800866 if (md.time_spec < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000867 LOG(ALERT) << "UHD: Loss of monotonic time";
Tom Tsoua93f7892017-03-07 17:54:06 -0800868 LOG(ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
ttsou724eb362012-03-13 02:20:01 +0000869 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000870 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000871 }
872
Tom Tsoua93f7892017-03-07 17:54:06 -0800873 // Workaround for UHD tick rounding bug
874 TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
875 if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
876 md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
877
878 prev_ts = md.time_spec;
879
kurtis.heimerl965e7572011-11-26 03:16:54 +0000880 return 0;
881}
882
Thomas Tsou204a9f12013-10-29 18:34:16 -0400883int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
884 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000885{
886 ssize_t rc;
887 uhd::time_spec_t ts;
888 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000889
Thomas Tsou204a9f12013-10-29 18:34:16 -0400890 if (bufs.size() != chans) {
891 LOG(ALERT) << "Invalid channel combination " << bufs.size();
892 return -1;
893 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000894
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000895 *overrun = false;
896 *underrun = false;
897
kurtis.heimerl965e7572011-11-26 03:16:54 +0000898 // Shift read time with respect to transmit clock
899 timestamp += ts_offset;
900
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700901 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000902 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000903
904 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400905 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000906 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400907 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800908 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000909 return 0;
910 }
911
Thomas Tsou204a9f12013-10-29 18:34:16 -0400912 // Create vector buffer
913 std::vector<std::vector<short> >
914 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
915
916 std::vector<short *> pkt_ptrs;
917 for (size_t i = 0; i < pkt_bufs.size(); i++)
918 pkt_ptrs.push_back(&pkt_bufs[i].front());
919
kurtis.heimerl965e7572011-11-26 03:16:54 +0000920 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400921 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800922 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400923 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
924 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800925 thread_enable_cancel(true);
926
kurtis.heimerl965e7572011-11-26 03:16:54 +0000927 rx_pkt_cnt++;
928
kurtis.heimerld4be0742011-11-26 03:17:46 +0000929 // Check for errors
930 rc = check_rx_md_err(metadata, num_smpls);
931 switch (rc) {
932 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000933 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
934 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000935 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800936 case ERROR_TIMEOUT:
937 // Assume stopping condition
938 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000939 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -0400940 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000941 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000942 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000943 }
944
kurtis.heimerl965e7572011-11-26 03:16:54 +0000945 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000946 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000947
Thomas Tsou204a9f12013-10-29 18:34:16 -0400948 for (size_t i = 0; i < rx_buffers.size(); i++) {
949 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
950 num_smpls,
951 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000952
Thomas Tsou204a9f12013-10-29 18:34:16 -0400953 // Continue on local overrun, exit on other errors
954 if ((rc < 0)) {
955 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800956 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400957 if (rc != smpl_buf::ERROR_OVERFLOW)
958 return 0;
959 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000960 }
961 }
962
963 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -0400964 for (size_t i = 0; i < rx_buffers.size(); i++) {
965 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
966 if ((rc < 0) || (rc != len)) {
967 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800968 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400969 return 0;
970 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000971 }
972
973 return len;
974}
975
Thomas Tsou204a9f12013-10-29 18:34:16 -0400976int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
977 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000978{
979 uhd::tx_metadata_t metadata;
980 metadata.has_time_spec = true;
981 metadata.start_of_burst = false;
982 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700983 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000984
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000985 *underrun = false;
986
kurtis.heimerl965e7572011-11-26 03:16:54 +0000987 // No control packets
988 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000989 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000990 return 0;
991 }
992
Thomas Tsou204a9f12013-10-29 18:34:16 -0400993 if (bufs.size() != chans) {
994 LOG(ALERT) << "Invalid channel combination " << bufs.size();
995 return -1;
996 }
997
kurtis.heimerl965e7572011-11-26 03:16:54 +0000998 // Drop a fixed number of packets (magic value)
999 if (!aligned) {
1000 drop_cnt++;
1001
1002 if (drop_cnt == 1) {
1003 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001004 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001005 metadata.end_of_burst = true;
1006 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001007 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001008 return len;
1009 } else {
1010 LOG(DEBUG) << "Aligning transmitter: start burst";
1011 metadata.start_of_burst = true;
1012 aligned = true;
1013 drop_cnt = 0;
1014 }
1015 }
1016
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001017 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001018 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001019 thread_enable_cancel(true);
1020
ttsoub371ed52012-01-09 18:11:34 +00001021 if (num_smpls != (unsigned) len) {
1022 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001023 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001024
1025 return num_smpls;
1026}
1027
1028bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1029{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001030 return true;
1031}
1032
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001033uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1034{
1035 double rf_spread, rf_freq;
1036 std::vector<double> freqs;
1037 uhd::tune_request_t treq(freq);
1038
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001039 if (dev_type == UMTRX) {
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001040 if (offset != 0.0)
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001041 return uhd::tune_request_t(freq, offset);
1042
1043 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1044 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1045 // only distort the signal (because cordic is not ideal).
1046 treq.target_freq = freq;
1047 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1048 treq.rf_freq = freq;
1049 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1050 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001051 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001052 } else if (chans == 1) {
Thomas Tsou8e17df72014-03-06 14:16:11 -05001053 if (offset == 0.0)
1054 return treq;
1055
1056 return uhd::tune_request_t(freq, offset);
1057 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001058 LOG(ALERT) << chans << " channels unsupported";
1059 return treq;
1060 }
1061
1062 if (tx)
1063 freqs = tx_freqs;
1064 else
1065 freqs = rx_freqs;
1066
1067 /* Tune directly if other channel isn't tuned */
1068 if (freqs[!chan] < 10.0)
1069 return treq;
1070
1071 /* Find center frequency between channels */
1072 rf_spread = fabs(freqs[!chan] - freq);
Tom Tsouf6115692017-06-26 10:13:25 -07001073 if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001074 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1075 return treq;
1076 }
1077
1078 rf_freq = (freqs[!chan] + freq) / 2.0f;
1079
1080 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1081 treq.target_freq = freq;
1082 treq.rf_freq = rf_freq;
1083
1084 return treq;
1085}
1086
1087bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1088{
1089 std::vector<double> freqs;
1090 uhd::tune_result_t tres;
1091 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1092
1093 if (tx) {
1094 tres = usrp_dev->set_tx_freq(treq, chan);
1095 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1096 } else {
1097 tres = usrp_dev->set_rx_freq(treq, chan);
1098 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1099 }
1100 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1101
Thomas Tsou8e17df72014-03-06 14:16:11 -05001102 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1103 return true;
1104
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001105 /* Manual RF policy means we intentionally tuned with a baseband
1106 * offset for dual-channel purposes. Now retune the other channel
1107 * with the opposite corresponding frequency offset
1108 */
1109 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1110 if (tx) {
1111 treq = select_freq(tx_freqs[!chan], !chan, true);
1112 tres = usrp_dev->set_tx_freq(treq, !chan);
1113 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1114 } else {
1115 treq = select_freq(rx_freqs[!chan], !chan, false);
1116 tres = usrp_dev->set_rx_freq(treq, !chan);
1117 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1118
1119 }
1120 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1121 }
1122
1123 return true;
1124}
1125
Thomas Tsou204a9f12013-10-29 18:34:16 -04001126bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001127{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001128 if (chan >= tx_freqs.size()) {
1129 LOG(ALERT) << "Requested non-existent channel " << chan;
1130 return false;
1131 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001132 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001133
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001134 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001135}
1136
Thomas Tsou204a9f12013-10-29 18:34:16 -04001137bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001138{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001139 if (chan >= rx_freqs.size()) {
1140 LOG(ALERT) << "Requested non-existent channel " << chan;
1141 return false;
1142 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001143 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001144
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001145 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001146}
1147
Thomas Tsou204a9f12013-10-29 18:34:16 -04001148double uhd_device::getTxFreq(size_t chan)
1149{
1150 if (chan >= tx_freqs.size()) {
1151 LOG(ALERT) << "Requested non-existent channel " << chan;
1152 return 0.0;
1153 }
1154
1155 return tx_freqs[chan];
1156}
1157
1158double uhd_device::getRxFreq(size_t chan)
1159{
1160 if (chan >= rx_freqs.size()) {
1161 LOG(ALERT) << "Requested non-existent channel " << chan;
1162 return 0.0;
1163 }
1164
1165 return rx_freqs[chan];
1166}
1167
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001168/*
1169 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1170 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1171 * combination.
1172 */
1173TIMESTAMP uhd_device::initialWriteTimestamp()
1174{
Tom Tsou76764272016-06-24 14:25:39 -07001175 if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001176 return ts_initial;
1177 else
1178 return ts_initial * tx_sps;
1179}
1180
1181TIMESTAMP uhd_device::initialReadTimestamp()
1182{
1183 return ts_initial;
1184}
1185
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001186double uhd_device::fullScaleInputValue()
1187{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001188 if (dev_type == LIMESDR)
ignasj28d80812017-06-13 23:37:46 +03001189 return (double) SHRT_MAX * LIMESDR_TX_AMPL;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001190 if (dev_type == UMTRX)
1191 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1192 else
1193 return (double) SHRT_MAX * USRP_TX_AMPL;
1194}
1195
1196double uhd_device::fullScaleOutputValue()
1197{
1198 return (double) SHRT_MAX;
1199}
1200
kurtis.heimerl965e7572011-11-26 03:16:54 +00001201bool uhd_device::recv_async_msg()
1202{
ttsou60dc4c92012-08-08 23:30:23 +00001203 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001204
1205 thread_enable_cancel(false);
1206 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1207 thread_enable_cancel(true);
1208 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001209 return false;
1210
1211 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001212 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001213 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001214
1215 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1216 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1217 LOG(ERR) << str_code(md);
1218 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001219 }
1220
1221 return true;
1222}
1223
1224std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1225{
1226 std::ostringstream ost("UHD: ");
1227
1228 switch (metadata.error_code) {
1229 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1230 ost << "No error";
1231 break;
1232 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1233 ost << "No packet received, implementation timed-out";
1234 break;
1235 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1236 ost << "A stream command was issued in the past";
1237 break;
1238 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1239 ost << "Expected another stream command";
1240 break;
1241 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1242 ost << "An internal receive buffer has filled";
1243 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001244 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1245 ost << "Multi-channel alignment failed";
1246 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001247 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1248 ost << "The packet could not be parsed";
1249 break;
1250 default:
1251 ost << "Unknown error " << metadata.error_code;
1252 }
1253
1254 if (metadata.has_time_spec)
1255 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1256
1257 return ost.str();
1258}
1259
1260std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1261{
1262 std::ostringstream ost("UHD: ");
1263
1264 switch (metadata.event_code) {
1265 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1266 ost << "A packet was successfully transmitted";
1267 break;
1268 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1269 ost << "An internal send buffer has emptied";
1270 break;
1271 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1272 ost << "Packet loss between host and device";
1273 break;
1274 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1275 ost << "Packet time was too late or too early";
1276 break;
1277 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1278 ost << "Underflow occurred inside a packet";
1279 break;
1280 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1281 ost << "Packet loss within a burst";
1282 break;
1283 default:
1284 ost << "Unknown error " << metadata.event_code;
1285 }
1286
1287 if (metadata.has_time_spec)
1288 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1289
1290 return ost.str();
1291}
1292
1293smpl_buf::smpl_buf(size_t len, double rate)
1294 : buf_len(len), clk_rt(rate),
1295 time_start(0), time_end(0), data_start(0), data_end(0)
1296{
1297 data = new uint32_t[len];
1298}
1299
1300smpl_buf::~smpl_buf()
1301{
1302 delete[] data;
1303}
1304
1305ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1306{
1307 if (timestamp < time_start)
1308 return ERROR_TIMESTAMP;
1309 else if (timestamp >= time_end)
1310 return 0;
1311 else
1312 return time_end - timestamp;
1313}
1314
1315ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1316{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001317 return avail_smpls(timespec.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001318}
1319
1320ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1321{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001322 int type_sz = 2 * sizeof(short);
1323
kurtis.heimerl965e7572011-11-26 03:16:54 +00001324 // Check for valid read
1325 if (timestamp < time_start)
1326 return ERROR_TIMESTAMP;
1327 if (timestamp >= time_end)
1328 return 0;
1329 if (len >= buf_len)
1330 return ERROR_READ;
1331
1332 // How many samples should be copied
1333 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001334 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001335 num_smpls = len;
1336
1337 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001338 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001339
1340 // Read it
1341 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001342 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001343 memcpy(buf, data + read_start, numBytes);
1344 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001345 size_t first_cp = (buf_len - read_start) * type_sz;
1346 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001347
1348 memcpy(buf, data + read_start, first_cp);
1349 memcpy((char*) buf + first_cp, data, second_cp);
1350 }
1351
1352 data_start = (read_start + len) % buf_len;
1353 time_start = timestamp + len;
1354
1355 if (time_start > time_end)
1356 return ERROR_READ;
1357 else
1358 return num_smpls;
1359}
1360
1361ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1362{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001363 return read(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001364}
1365
1366ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1367{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001368 int type_sz = 2 * sizeof(short);
1369
kurtis.heimerl965e7572011-11-26 03:16:54 +00001370 // Check for valid write
1371 if ((len == 0) || (len >= buf_len))
1372 return ERROR_WRITE;
1373 if ((timestamp + len) <= time_end)
1374 return ERROR_TIMESTAMP;
1375
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001376 if (timestamp < time_end) {
1377 LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001378 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1379 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 -04001380 // Do not return error here, because it's a rounding error and is not fatal
1381 }
1382 if (timestamp > time_end && time_end != 0) {
1383 LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001384 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1385 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 -04001386 // Do not return error here, because it's a rounding error and is not fatal
1387 }
1388
kurtis.heimerl965e7572011-11-26 03:16:54 +00001389 // Starting index
1390 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1391
1392 // Write it
1393 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001394 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001395 memcpy(data + write_start, buf, numBytes);
1396 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001397 size_t first_cp = (buf_len - write_start) * type_sz;
1398 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001399
1400 memcpy(data + write_start, buf, first_cp);
1401 memcpy(data, (char*) buf + first_cp, second_cp);
1402 }
1403
1404 data_end = (write_start + len) % buf_len;
1405 time_end = timestamp + len;
1406
Thomas Tsou18d3b832014-02-13 14:55:23 -05001407 if (!data_start)
1408 data_start = write_start;
1409
kurtis.heimerl965e7572011-11-26 03:16:54 +00001410 if (((write_start + len) > buf_len) && (data_end > data_start))
1411 return ERROR_OVERFLOW;
1412 else if (time_end <= time_start)
1413 return ERROR_WRITE;
1414 else
1415 return len;
1416}
1417
1418ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1419{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001420 return write(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001421}
1422
Tom Tsou1ae25562014-12-05 12:56:34 -08001423std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001424{
1425 std::ostringstream ost("Sample buffer: ");
1426
Tom Tsou1ae25562014-12-05 12:56:34 -08001427 ost << "timestamp = " << ts;
1428 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001429 ost << ", time_start = " << time_start;
1430 ost << ", time_end = " << time_end;
1431 ost << ", data_start = " << data_start;
1432 ost << ", data_end = " << data_end;
1433
1434 return ost.str();
1435}
1436
1437std::string smpl_buf::str_code(ssize_t code)
1438{
1439 switch (code) {
1440 case ERROR_TIMESTAMP:
1441 return "Sample buffer: Requested timestamp is not valid";
1442 case ERROR_READ:
1443 return "Sample buffer: Read error";
1444 case ERROR_WRITE:
1445 return "Sample buffer: Write error";
1446 case ERROR_OVERFLOW:
1447 return "Sample buffer: Overrun";
1448 default:
1449 return "Sample buffer: Unknown error";
1450 }
1451}
1452
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001453RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
Tom Tsou05c6feb2016-06-22 16:09:44 -07001454 InterfaceType iface, size_t chans, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001455{
Tom Tsou05c6feb2016-06-22 16:09:44 -07001456 return new uhd_device(tx_sps, rx_sps, iface, chans, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001457}