blob: cea68cc1b346968b576aa1f1d00a4622b162978e [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" } },
133 { std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 16.5/GSMRATE, "STREAM/LimeSDR (4 SPS TX/RX)" } },
134 { 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 Tsou1fb0ce62017-06-08 19:41:48 -0700296 void set_rates();
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000297 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000298 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000299 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000300
kurtis.heimerl965e7572011-11-26 03:16:54 +0000301 std::string str_code(uhd::rx_metadata_t metadata);
302 std::string str_code(uhd::async_metadata_t metadata);
303
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500304 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
305 bool set_freq(double freq, size_t chan, bool tx);
306
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800307 Thread *async_event_thrd;
Tom Tsou05c6feb2016-06-22 16:09:44 -0700308 InterfaceType iface;
Tom Tsou93b7f372014-12-15 20:23:33 -0800309 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000310};
311
312void *async_event_loop(uhd_device *dev)
313{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500314 dev->setPriority(0.43);
315
kurtis.heimerl965e7572011-11-26 03:16:54 +0000316 while (1) {
317 dev->recv_async_msg();
318 pthread_testcancel();
319 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500320
321 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000322}
323
Tom Tsou72bf7622017-03-07 14:16:46 -0800324#ifndef USE_UHD_3_11
325/*
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000326 Catch and drop underrun 'U' and overrun 'O' messages from stdout
327 since we already report using the logging facility. Direct
328 everything else appropriately.
329 */
330void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
331{
332 switch (type) {
333 case uhd::msg::status:
334 LOG(INFO) << msg;
335 break;
336 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000337 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000338 break;
339 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000340 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000341 break;
342 case uhd::msg::fastpath:
343 break;
344 }
345}
Tom Tsou72bf7622017-03-07 14:16:46 -0800346#endif
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000347
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800348static void thread_enable_cancel(bool cancel)
349{
350 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
351 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
352}
353
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800354uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
Tom Tsou05c6feb2016-06-22 16:09:44 -0700355 InterfaceType iface, size_t chans, double offset)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400356 : tx_gain_min(0.0), tx_gain_max(0.0),
357 rx_gain_min(0.0), rx_gain_max(0.0),
358 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000359 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Thomas Tsou18d3b832014-02-13 14:55:23 -0500360 prev_ts(0,0), ts_initial(0), ts_offset(0)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000361{
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800362 this->tx_sps = tx_sps;
363 this->rx_sps = rx_sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400364 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500365 this->offset = offset;
Tom Tsou05c6feb2016-06-22 16:09:44 -0700366 this->iface = iface;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000367}
368
369uhd_device::~uhd_device()
370{
371 stop();
372
Thomas Tsou204a9f12013-10-29 18:34:16 -0400373 for (size_t i = 0; i < rx_buffers.size(); i++)
374 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000375}
376
kurtis.heimerl24481de2011-11-26 03:17:18 +0000377void uhd_device::init_gains()
378{
379 uhd::gain_range_t range;
380
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400381 if (dev_type == UMTRX) {
382 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
383 if (gain_stages[0] == "VGA") {
384 LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
385 }
386 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
387 range = usrp_dev->get_tx_gain_range();
388 tx_gain_min = range.start();
389 tx_gain_max = range.stop();
390 } else {
391 range = usrp_dev->get_tx_gain_range("VGA2");
392 tx_gain_min = UMTRX_VGA1_DEF + range.start();
393 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
394 }
395 } else {
396 range = usrp_dev->get_tx_gain_range();
397 tx_gain_min = range.start();
398 tx_gain_max = range.stop();
399 }
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400400 LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000401
402 range = usrp_dev->get_rx_gain_range();
403 rx_gain_min = range.start();
404 rx_gain_max = range.stop();
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400405 LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000406
Thomas Tsou204a9f12013-10-29 18:34:16 -0400407 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400408 double gain = (tx_gain_min + tx_gain_max) / 2;
409 LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
410 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400411 tx_gains[i] = usrp_dev->get_tx_gain(i);
412 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000413
Thomas Tsou204a9f12013-10-29 18:34:16 -0400414 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400415 double gain = (rx_gain_min + rx_gain_max) / 2;
416 LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
417 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400418 rx_gains[i] = usrp_dev->get_rx_gain(i);
419 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000420
421 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400422
kurtis.heimerl24481de2011-11-26 03:17:18 +0000423}
424
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700425void uhd_device::set_rates()
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700426{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700427 dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
428 if (desc.mcr != 0.0)
429 usrp_dev->set_master_clock_rate(desc.mcr);
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700430
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700431 tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate;
432 rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate;
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700433
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700434 usrp_dev->set_tx_rate(tx_rate);
435 usrp_dev->set_rx_rate(rx_rate);
436 tx_rate = usrp_dev->get_tx_rate();
437 rx_rate = usrp_dev->get_rx_rate();
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700438
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700439 ts_offset = (TIMESTAMP) desc.offset * rx_rate;
440 LOG(INFO) << "Rates configured for " << desc.str;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000441}
442
Thomas Tsou204a9f12013-10-29 18:34:16 -0400443double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000444{
Tom Tsou76764272016-06-24 14:25:39 -0700445 if (iface == MULTI_ARFCN)
446 chan = 0;
447
Thomas Tsou204a9f12013-10-29 18:34:16 -0400448 if (chan >= tx_gains.size()) {
449 LOG(ALERT) << "Requested non-existent channel" << chan;
450 return 0.0f;
451 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000452
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400453 if (dev_type == UMTRX) {
454 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
455 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
456 usrp_dev->set_tx_gain(db, chan);
457 } else {
458 // New UHD versions support split configuration of
459 // Tx gain stages. We utilize this to set the gain
460 // configuration, optimal for the Tx signal quality.
461 // From our measurements, VGA1 must be 18dB plus-minus
462 // one and VGA2 is the best when 23dB or lower.
463 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
464 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
465 }
466 } else {
467 usrp_dev->set_tx_gain(db, chan);
468 }
469
Thomas Tsou204a9f12013-10-29 18:34:16 -0400470 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000471
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400472 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400473
474 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000475}
476
Thomas Tsou204a9f12013-10-29 18:34:16 -0400477double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000478{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400479 if (chan >= rx_gains.size()) {
480 LOG(ALERT) << "Requested non-existent channel " << chan;
481 return 0.0f;
482 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000483
Thomas Tsou204a9f12013-10-29 18:34:16 -0400484 usrp_dev->set_rx_gain(db, chan);
485 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000486
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400487 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400488
489 return rx_gains[chan];
490}
491
492double uhd_device::getRxGain(size_t chan)
493{
Tom Tsou76764272016-06-24 14:25:39 -0700494 if (iface == MULTI_ARFCN)
495 chan = 0;
496
Thomas Tsou204a9f12013-10-29 18:34:16 -0400497 if (chan >= rx_gains.size()) {
498 LOG(ALERT) << "Requested non-existent channel " << chan;
499 return 0.0f;
500 }
501
502 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000503}
504
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000505/*
506 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400507 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000508 deal with the transport latency. Reject the USRP1 because UHD doesn't
509 support timestamped samples with it.
510 */
511bool uhd_device::parse_dev_type()
512{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700513 uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree();
514 std::string devString = prop_tree->access<std::string>("/name").get();
515 std::string mboardString = usrp_dev->get_mboard_name();
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000516
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700517 const std::map<std::string, std::pair<uhd_dev_type, TxWindowType>> devStringMap {
518 { "B100", { B100, TX_WINDOW_USRP1 } },
519 { "B200", { B200, TX_WINDOW_USRP1 } },
520 { "B200mini", { B200, TX_WINDOW_USRP1 } },
521 { "B210", { B210, TX_WINDOW_USRP1 } },
522 { "E100", { E1XX, TX_WINDOW_FIXED } },
523 { "E110", { E1XX, TX_WINDOW_FIXED } },
524 { "E310", { E3XX, TX_WINDOW_FIXED } },
525 { "E3XX", { E3XX, TX_WINDOW_FIXED } },
526 { "X300", { X3XX, TX_WINDOW_FIXED } },
527 { "X310", { X3XX, TX_WINDOW_FIXED } },
528 { "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
529 { "STREAM", { LIMESDR, TX_WINDOW_USRP1 } },
530 };
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000531
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700532 // Compare UHD motherboard and device strings */
533 std::string found;
534 if (devStringMap.find(devString) != devStringMap.end())
535 found = devString;
536 else if (devStringMap.find(mboardString) != devStringMap.end())
537 found = mboardString;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000538
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700539 if (found.empty()) {
540 LOG(ALERT) << "Unsupported device " << devString;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000541 return false;
542 }
543
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700544 dev_type = devStringMap.at(found).first;
545 tx_window = devStringMap.at(found).second;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000546 return true;
547}
548
Tom Tsou3b093bb2016-05-03 19:04:15 -0700549/*
550 * Check for UHD version > 3.9.0 for E3XX support
551 */
552static bool uhd_e3xx_version_chk()
553{
554 std::string ver = uhd::get_version_string();
555 std::string major_str(ver.begin(), ver.begin() + 3);
556 std::string minor_str(ver.begin() + 4, ver.begin() + 7);
557
558 int major_val = atoi(major_str.c_str());
559 int minor_val = atoi(minor_str.c_str());
560
561 if (major_val < 3)
562 return false;
563 if (minor_val < 9)
564 return false;
565
566 return true;
567}
568
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700569int uhd_device::open(const std::string &args, int ref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000570{
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700571 const char *refstr;
572
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000573 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000574 uhd::device_addr_t addr(args);
575 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000576 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000577 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400578 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000579 }
580
581 // Use the first found device
582 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000583 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700584 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000585 } catch(...) {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700586 LOG(ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400587 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000588 }
589
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000590 // Check for a valid device type and set bus type
591 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400592 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000593
Tom Tsou3b093bb2016-05-03 19:04:15 -0700594 if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
595 LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater";
596 return -1;
597 }
598
Thomas Tsou204a9f12013-10-29 18:34:16 -0400599 // Verify and set channels
Tom Tsou76764272016-06-24 14:25:39 -0700600 if (iface == MULTI_ARFCN) {
601 if ((dev_type != B200) && (dev_type != B210)) {
602 LOG(ALERT) << "Unsupported device configuration";
603 return -1;
604 }
605
606 dev_type = B2XX_MCBTS;
607 chans = 1;
608 } else if (chans == 2) {
609 if (dev_type == B210) {
610 } else if (dev_type == UMTRX) {
611 uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
612 usrp_dev->set_tx_subdev_spec(subdev_spec);
613 usrp_dev->set_rx_subdev_spec(subdev_spec);
614 } else {
615 LOG(ALERT) << "Invalid device configuration";
616 return -1;
617 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400618 } else if (chans != 1) {
619 LOG(ALERT) << "Invalid channel combination for device";
620 return -1;
621 }
622
623 tx_freqs.resize(chans);
624 rx_freqs.resize(chans);
625 tx_gains.resize(chans);
626 rx_gains.resize(chans);
627 rx_buffers.resize(chans);
628
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700629 switch (ref) {
630 case REF_INTERNAL:
631 refstr = "internal";
632 break;
633 case REF_EXTERNAL:
634 refstr = "external";
635 break;
636 case REF_GPS:
637 refstr = "gpsdo";
638 break;
639 default:
640 LOG(ALERT) << "Invalid reference type";
641 return -1;
642 }
643
644 usrp_dev->set_clock_source(refstr);
Thomas Tsou010fff72013-10-16 00:31:18 -0400645
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700646 try {
647 set_rates();
648 } catch (const std::exception &e) {
649 LOG(ALERT) << "UHD rate setting failed - " << e.what();
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500650 return -1;
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700651 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000652
Alexander Chemerise1714252015-04-28 23:09:02 -0400653 // Set RF frontend bandwidth
654 if (dev_type == UMTRX) {
655 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
656 for (size_t i = 0; i < chans; i++) {
657 usrp_dev->set_tx_bandwidth(500*1000*2, i);
Tom Tsoud6ae8642017-03-30 17:22:58 -0700658 usrp_dev->set_rx_bandwidth(500*1000*2, i);
Alexander Chemerise1714252015-04-28 23:09:02 -0400659 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800660 } else if (dev_type == LIMESDR) {
661 for (size_t i = 0; i < chans; i++) {
662 usrp_dev->set_tx_bandwidth(5e6, i);
663 usrp_dev->set_rx_bandwidth(5e6, i);
664 }
Alexander Chemerise1714252015-04-28 23:09:02 -0400665 }
666
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400667 /* Create TX and RX streamers */
668 uhd::stream_args_t stream_args("sc16");
669 for (size_t i = 0; i < chans; i++)
670 stream_args.channels.push_back(i);
671
672 tx_stream = usrp_dev->get_tx_stream(stream_args);
673 rx_stream = usrp_dev->get_rx_stream(stream_args);
674
675 /* Number of samples per over-the-wire packet */
676 tx_spp = tx_stream->get_max_num_samps();
677 rx_spp = rx_stream->get_max_num_samps();
678
kurtis.heimerl965e7572011-11-26 03:16:54 +0000679 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400680 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400681 for (size_t i = 0; i < rx_buffers.size(); i++)
682 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000683
kurtis.heimerl02d04052011-11-26 03:17:10 +0000684 // Initialize and shadow gain values
685 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000686
kurtis.heimerl965e7572011-11-26 03:16:54 +0000687 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000688 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000689
Tom Tsou76764272016-06-24 14:25:39 -0700690 if (iface == MULTI_ARFCN)
691 return MULTI_ARFCN;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500692
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400693 switch (dev_type) {
694 case B100:
695 return RESAMP_64M;
696 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800697 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400698 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500699 case B200:
700 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100701 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800702 case E3XX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800703 case LIMESDR:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500704 default:
705 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400706 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400707
708 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000709}
710
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000711bool uhd_device::flush_recv(size_t num_pkts)
712{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000713 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000714 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800715 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000716
Thomas Tsou18d3b832014-02-13 14:55:23 -0500717 std::vector<std::vector<short> >
718 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000719
Thomas Tsou18d3b832014-02-13 14:55:23 -0500720 std::vector<short *> pkt_ptrs;
721 for (size_t i = 0; i < pkt_bufs.size(); i++)
722 pkt_ptrs.push_back(&pkt_bufs[i].front());
723
724 ts_initial = 0;
725 while (!ts_initial || (num_pkts-- > 0)) {
726 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400727 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000728 if (!num_smpls) {
729 switch (md.error_code) {
730 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800731 LOG(ALERT) << "Device timed out";
732 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000733 default:
734 continue;
735 }
736 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500737
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700738 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000739 }
740
Thomas Tsou18d3b832014-02-13 14:55:23 -0500741 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
742
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000743 return true;
744}
745
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800746bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000747{
Thomas Tsouc2839162014-03-27 13:52:58 -0400748 /* Allow 100 ms delay to align multi-channel streams */
749 double delay = 0.1;
750
kurtis.heimerl68292102011-11-26 03:17:28 +0000751 aligned = false;
752
Thomas Tsouc2839162014-03-27 13:52:58 -0400753 uhd::time_spec_t current = usrp_dev->get_time_now();
754
Thomas Tsou204a9f12013-10-29 18:34:16 -0400755 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400756 cmd.stream_now = false;
757 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400758
kurtis.heimerl68292102011-11-26 03:17:28 +0000759 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500760
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800761 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000762}
763
kurtis.heimerl965e7572011-11-26 03:16:54 +0000764bool uhd_device::start()
765{
766 LOG(INFO) << "Starting USRP...";
767
768 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000769 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000770 return false;
771 }
772
Tom Tsou72bf7622017-03-07 14:16:46 -0800773#ifndef USE_UHD_3_11
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400774 // Register msg handler
775 uhd::msg::register_handler(&uhd_msg_handler);
Tom Tsou72bf7622017-03-07 14:16:46 -0800776#endif
kurtis.heimerl965e7572011-11-26 03:16:54 +0000777 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800778 async_event_thrd = new Thread();
779 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000780
781 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800782 if (!restart())
783 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000784
kurtis.heimerl965e7572011-11-26 03:16:54 +0000785 // Display usrp time
786 double time_now = usrp_dev->get_time_now().get_real_secs();
787 LOG(INFO) << "The current time is " << time_now << " seconds";
788
789 started = true;
790 return true;
791}
792
793bool uhd_device::stop()
794{
Thomas Tsou2c791102013-11-15 23:00:28 -0500795 if (!started)
796 return false;
797
798 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000799 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
800
801 usrp_dev->issue_stream_cmd(stream_cmd);
802
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800803 async_event_thrd->cancel();
804 async_event_thrd->join();
805 delete async_event_thrd;
806
kurtis.heimerl965e7572011-11-26 03:16:54 +0000807 started = false;
808 return true;
809}
810
Thomas Tsou7553aa92013-11-08 12:50:03 -0500811void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000812{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500813 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000814 return;
815}
816
kurtis.heimerld4be0742011-11-26 03:17:46 +0000817int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000818{
kurtis.heimerld4be0742011-11-26 03:17:46 +0000819 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000820 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000821
822 switch (md.error_code) {
823 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000824 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800825 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000826 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
827 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
828 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
829 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
830 default:
831 return ERROR_UNHANDLED;
832 }
833 }
834
kurtis.heimerl965e7572011-11-26 03:16:54 +0000835 // Missing timestamp
836 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000837 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000838 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000839 }
840
841 // Monotonicity check
Tom Tsoua93f7892017-03-07 17:54:06 -0800842 if (md.time_spec < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000843 LOG(ALERT) << "UHD: Loss of monotonic time";
Tom Tsoua93f7892017-03-07 17:54:06 -0800844 LOG(ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
ttsou724eb362012-03-13 02:20:01 +0000845 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000846 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000847 }
848
Tom Tsoua93f7892017-03-07 17:54:06 -0800849 // Workaround for UHD tick rounding bug
850 TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
851 if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
852 md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
853
854 prev_ts = md.time_spec;
855
kurtis.heimerl965e7572011-11-26 03:16:54 +0000856 return 0;
857}
858
Thomas Tsou204a9f12013-10-29 18:34:16 -0400859int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
860 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000861{
862 ssize_t rc;
863 uhd::time_spec_t ts;
864 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000865
Thomas Tsou204a9f12013-10-29 18:34:16 -0400866 if (bufs.size() != chans) {
867 LOG(ALERT) << "Invalid channel combination " << bufs.size();
868 return -1;
869 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000870
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000871 *overrun = false;
872 *underrun = false;
873
kurtis.heimerl965e7572011-11-26 03:16:54 +0000874 // Shift read time with respect to transmit clock
875 timestamp += ts_offset;
876
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700877 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000878 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000879
880 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400881 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000882 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400883 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800884 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000885 return 0;
886 }
887
Thomas Tsou204a9f12013-10-29 18:34:16 -0400888 // Create vector buffer
889 std::vector<std::vector<short> >
890 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
891
892 std::vector<short *> pkt_ptrs;
893 for (size_t i = 0; i < pkt_bufs.size(); i++)
894 pkt_ptrs.push_back(&pkt_bufs[i].front());
895
kurtis.heimerl965e7572011-11-26 03:16:54 +0000896 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400897 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800898 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400899 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
900 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800901 thread_enable_cancel(true);
902
kurtis.heimerl965e7572011-11-26 03:16:54 +0000903 rx_pkt_cnt++;
904
kurtis.heimerld4be0742011-11-26 03:17:46 +0000905 // Check for errors
906 rc = check_rx_md_err(metadata, num_smpls);
907 switch (rc) {
908 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000909 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
910 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000911 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800912 case ERROR_TIMEOUT:
913 // Assume stopping condition
914 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000915 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -0400916 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000917 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000918 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000919 }
920
kurtis.heimerl965e7572011-11-26 03:16:54 +0000921 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000922 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000923
Thomas Tsou204a9f12013-10-29 18:34:16 -0400924 for (size_t i = 0; i < rx_buffers.size(); i++) {
925 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
926 num_smpls,
927 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000928
Thomas Tsou204a9f12013-10-29 18:34:16 -0400929 // Continue on local overrun, exit on other errors
930 if ((rc < 0)) {
931 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800932 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400933 if (rc != smpl_buf::ERROR_OVERFLOW)
934 return 0;
935 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000936 }
937 }
938
939 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -0400940 for (size_t i = 0; i < rx_buffers.size(); i++) {
941 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
942 if ((rc < 0) || (rc != len)) {
943 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800944 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400945 return 0;
946 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000947 }
948
949 return len;
950}
951
Thomas Tsou204a9f12013-10-29 18:34:16 -0400952int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
953 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000954{
955 uhd::tx_metadata_t metadata;
956 metadata.has_time_spec = true;
957 metadata.start_of_burst = false;
958 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700959 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000960
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000961 *underrun = false;
962
kurtis.heimerl965e7572011-11-26 03:16:54 +0000963 // No control packets
964 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000965 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000966 return 0;
967 }
968
Thomas Tsou204a9f12013-10-29 18:34:16 -0400969 if (bufs.size() != chans) {
970 LOG(ALERT) << "Invalid channel combination " << bufs.size();
971 return -1;
972 }
973
kurtis.heimerl965e7572011-11-26 03:16:54 +0000974 // Drop a fixed number of packets (magic value)
975 if (!aligned) {
976 drop_cnt++;
977
978 if (drop_cnt == 1) {
979 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000980 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000981 metadata.end_of_burst = true;
982 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000983 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000984 return len;
985 } else {
986 LOG(DEBUG) << "Aligning transmitter: start burst";
987 metadata.start_of_burst = true;
988 aligned = true;
989 drop_cnt = 0;
990 }
991 }
992
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800993 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400994 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800995 thread_enable_cancel(true);
996
ttsoub371ed52012-01-09 18:11:34 +0000997 if (num_smpls != (unsigned) len) {
998 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +0000999 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001000
1001 return num_smpls;
1002}
1003
1004bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1005{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001006 return true;
1007}
1008
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001009uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1010{
1011 double rf_spread, rf_freq;
1012 std::vector<double> freqs;
1013 uhd::tune_request_t treq(freq);
1014
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001015 if (dev_type == UMTRX) {
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001016 if (offset != 0.0)
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001017 return uhd::tune_request_t(freq, offset);
1018
1019 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1020 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1021 // only distort the signal (because cordic is not ideal).
1022 treq.target_freq = freq;
1023 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1024 treq.rf_freq = freq;
1025 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1026 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001027 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001028 } else if (chans == 1) {
Thomas Tsou8e17df72014-03-06 14:16:11 -05001029 if (offset == 0.0)
1030 return treq;
1031
1032 return uhd::tune_request_t(freq, offset);
1033 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001034 LOG(ALERT) << chans << " channels unsupported";
1035 return treq;
1036 }
1037
1038 if (tx)
1039 freqs = tx_freqs;
1040 else
1041 freqs = rx_freqs;
1042
1043 /* Tune directly if other channel isn't tuned */
1044 if (freqs[!chan] < 10.0)
1045 return treq;
1046
1047 /* Find center frequency between channels */
1048 rf_spread = fabs(freqs[!chan] - freq);
Tom Tsou1fb0ce62017-06-08 19:41:48 -07001049 if (rf_spread > dev_param_map.at(dev_key(B210, 1, 1)).mcr) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001050 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1051 return treq;
1052 }
1053
1054 rf_freq = (freqs[!chan] + freq) / 2.0f;
1055
1056 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1057 treq.target_freq = freq;
1058 treq.rf_freq = rf_freq;
1059
1060 return treq;
1061}
1062
1063bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1064{
1065 std::vector<double> freqs;
1066 uhd::tune_result_t tres;
1067 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1068
1069 if (tx) {
1070 tres = usrp_dev->set_tx_freq(treq, chan);
1071 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1072 } else {
1073 tres = usrp_dev->set_rx_freq(treq, chan);
1074 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1075 }
1076 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1077
Thomas Tsou8e17df72014-03-06 14:16:11 -05001078 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1079 return true;
1080
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001081 /* Manual RF policy means we intentionally tuned with a baseband
1082 * offset for dual-channel purposes. Now retune the other channel
1083 * with the opposite corresponding frequency offset
1084 */
1085 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1086 if (tx) {
1087 treq = select_freq(tx_freqs[!chan], !chan, true);
1088 tres = usrp_dev->set_tx_freq(treq, !chan);
1089 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1090 } else {
1091 treq = select_freq(rx_freqs[!chan], !chan, false);
1092 tres = usrp_dev->set_rx_freq(treq, !chan);
1093 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1094
1095 }
1096 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1097 }
1098
1099 return true;
1100}
1101
Thomas Tsou204a9f12013-10-29 18:34:16 -04001102bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001103{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001104 if (chan >= tx_freqs.size()) {
1105 LOG(ALERT) << "Requested non-existent channel " << chan;
1106 return false;
1107 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001108 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001109
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001110 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001111}
1112
Thomas Tsou204a9f12013-10-29 18:34:16 -04001113bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001114{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001115 if (chan >= rx_freqs.size()) {
1116 LOG(ALERT) << "Requested non-existent channel " << chan;
1117 return false;
1118 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001119 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001120
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001121 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001122}
1123
Thomas Tsou204a9f12013-10-29 18:34:16 -04001124double uhd_device::getTxFreq(size_t chan)
1125{
1126 if (chan >= tx_freqs.size()) {
1127 LOG(ALERT) << "Requested non-existent channel " << chan;
1128 return 0.0;
1129 }
1130
1131 return tx_freqs[chan];
1132}
1133
1134double uhd_device::getRxFreq(size_t chan)
1135{
1136 if (chan >= rx_freqs.size()) {
1137 LOG(ALERT) << "Requested non-existent channel " << chan;
1138 return 0.0;
1139 }
1140
1141 return rx_freqs[chan];
1142}
1143
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001144/*
1145 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1146 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1147 * combination.
1148 */
1149TIMESTAMP uhd_device::initialWriteTimestamp()
1150{
Tom Tsou76764272016-06-24 14:25:39 -07001151 if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001152 return ts_initial;
1153 else
1154 return ts_initial * tx_sps;
1155}
1156
1157TIMESTAMP uhd_device::initialReadTimestamp()
1158{
1159 return ts_initial;
1160}
1161
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001162double uhd_device::fullScaleInputValue()
1163{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001164 if (dev_type == LIMESDR)
1165 return (double) 2047 * LIMESDR_TX_AMPL;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001166 if (dev_type == UMTRX)
1167 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1168 else
1169 return (double) SHRT_MAX * USRP_TX_AMPL;
1170}
1171
1172double uhd_device::fullScaleOutputValue()
1173{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001174 if (dev_type == LIMESDR) return (double) 2047;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001175 return (double) SHRT_MAX;
1176}
1177
kurtis.heimerl965e7572011-11-26 03:16:54 +00001178bool uhd_device::recv_async_msg()
1179{
ttsou60dc4c92012-08-08 23:30:23 +00001180 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001181
1182 thread_enable_cancel(false);
1183 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1184 thread_enable_cancel(true);
1185 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001186 return false;
1187
1188 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001189 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001190 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001191
1192 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1193 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1194 LOG(ERR) << str_code(md);
1195 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001196 }
1197
1198 return true;
1199}
1200
1201std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1202{
1203 std::ostringstream ost("UHD: ");
1204
1205 switch (metadata.error_code) {
1206 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1207 ost << "No error";
1208 break;
1209 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1210 ost << "No packet received, implementation timed-out";
1211 break;
1212 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1213 ost << "A stream command was issued in the past";
1214 break;
1215 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1216 ost << "Expected another stream command";
1217 break;
1218 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1219 ost << "An internal receive buffer has filled";
1220 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001221 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1222 ost << "Multi-channel alignment failed";
1223 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001224 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1225 ost << "The packet could not be parsed";
1226 break;
1227 default:
1228 ost << "Unknown error " << metadata.error_code;
1229 }
1230
1231 if (metadata.has_time_spec)
1232 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1233
1234 return ost.str();
1235}
1236
1237std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1238{
1239 std::ostringstream ost("UHD: ");
1240
1241 switch (metadata.event_code) {
1242 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1243 ost << "A packet was successfully transmitted";
1244 break;
1245 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1246 ost << "An internal send buffer has emptied";
1247 break;
1248 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1249 ost << "Packet loss between host and device";
1250 break;
1251 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1252 ost << "Packet time was too late or too early";
1253 break;
1254 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1255 ost << "Underflow occurred inside a packet";
1256 break;
1257 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1258 ost << "Packet loss within a burst";
1259 break;
1260 default:
1261 ost << "Unknown error " << metadata.event_code;
1262 }
1263
1264 if (metadata.has_time_spec)
1265 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1266
1267 return ost.str();
1268}
1269
1270smpl_buf::smpl_buf(size_t len, double rate)
1271 : buf_len(len), clk_rt(rate),
1272 time_start(0), time_end(0), data_start(0), data_end(0)
1273{
1274 data = new uint32_t[len];
1275}
1276
1277smpl_buf::~smpl_buf()
1278{
1279 delete[] data;
1280}
1281
1282ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1283{
1284 if (timestamp < time_start)
1285 return ERROR_TIMESTAMP;
1286 else if (timestamp >= time_end)
1287 return 0;
1288 else
1289 return time_end - timestamp;
1290}
1291
1292ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1293{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001294 return avail_smpls(timespec.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001295}
1296
1297ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1298{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001299 int type_sz = 2 * sizeof(short);
1300
kurtis.heimerl965e7572011-11-26 03:16:54 +00001301 // Check for valid read
1302 if (timestamp < time_start)
1303 return ERROR_TIMESTAMP;
1304 if (timestamp >= time_end)
1305 return 0;
1306 if (len >= buf_len)
1307 return ERROR_READ;
1308
1309 // How many samples should be copied
1310 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001311 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001312 num_smpls = len;
1313
1314 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001315 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001316
1317 // Read it
1318 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001319 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001320 memcpy(buf, data + read_start, numBytes);
1321 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001322 size_t first_cp = (buf_len - read_start) * type_sz;
1323 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001324
1325 memcpy(buf, data + read_start, first_cp);
1326 memcpy((char*) buf + first_cp, data, second_cp);
1327 }
1328
1329 data_start = (read_start + len) % buf_len;
1330 time_start = timestamp + len;
1331
1332 if (time_start > time_end)
1333 return ERROR_READ;
1334 else
1335 return num_smpls;
1336}
1337
1338ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1339{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001340 return read(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001341}
1342
1343ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1344{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001345 int type_sz = 2 * sizeof(short);
1346
kurtis.heimerl965e7572011-11-26 03:16:54 +00001347 // Check for valid write
1348 if ((len == 0) || (len >= buf_len))
1349 return ERROR_WRITE;
1350 if ((timestamp + len) <= time_end)
1351 return ERROR_TIMESTAMP;
1352
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001353 if (timestamp < time_end) {
1354 LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001355 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1356 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 -04001357 // Do not return error here, because it's a rounding error and is not fatal
1358 }
1359 if (timestamp > time_end && time_end != 0) {
1360 LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001361 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1362 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 -04001363 // Do not return error here, because it's a rounding error and is not fatal
1364 }
1365
kurtis.heimerl965e7572011-11-26 03:16:54 +00001366 // Starting index
1367 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1368
1369 // Write it
1370 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001371 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001372 memcpy(data + write_start, buf, numBytes);
1373 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001374 size_t first_cp = (buf_len - write_start) * type_sz;
1375 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001376
1377 memcpy(data + write_start, buf, first_cp);
1378 memcpy(data, (char*) buf + first_cp, second_cp);
1379 }
1380
1381 data_end = (write_start + len) % buf_len;
1382 time_end = timestamp + len;
1383
Thomas Tsou18d3b832014-02-13 14:55:23 -05001384 if (!data_start)
1385 data_start = write_start;
1386
kurtis.heimerl965e7572011-11-26 03:16:54 +00001387 if (((write_start + len) > buf_len) && (data_end > data_start))
1388 return ERROR_OVERFLOW;
1389 else if (time_end <= time_start)
1390 return ERROR_WRITE;
1391 else
1392 return len;
1393}
1394
1395ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1396{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001397 return write(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001398}
1399
Tom Tsou1ae25562014-12-05 12:56:34 -08001400std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001401{
1402 std::ostringstream ost("Sample buffer: ");
1403
Tom Tsou1ae25562014-12-05 12:56:34 -08001404 ost << "timestamp = " << ts;
1405 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001406 ost << ", time_start = " << time_start;
1407 ost << ", time_end = " << time_end;
1408 ost << ", data_start = " << data_start;
1409 ost << ", data_end = " << data_end;
1410
1411 return ost.str();
1412}
1413
1414std::string smpl_buf::str_code(ssize_t code)
1415{
1416 switch (code) {
1417 case ERROR_TIMESTAMP:
1418 return "Sample buffer: Requested timestamp is not valid";
1419 case ERROR_READ:
1420 return "Sample buffer: Read error";
1421 case ERROR_WRITE:
1422 return "Sample buffer: Write error";
1423 case ERROR_OVERFLOW:
1424 return "Sample buffer: Overrun";
1425 default:
1426 return "Sample buffer: Unknown error";
1427 }
1428}
1429
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001430RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
Tom Tsou05c6feb2016-06-22 16:09:44 -07001431 InterfaceType iface, size_t chans, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001432{
Tom Tsou05c6feb2016-06-22 16:09:44 -07001433 return new uhd_device(tx_sps, rx_sps, iface, chans, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001434}