blob: 778c772dbfc244d0dc3216f9c09775ac21a75f52 [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 } },
522 { "B210", { B210, TX_WINDOW_USRP1 } },
523 { "E100", { E1XX, TX_WINDOW_FIXED } },
524 { "E110", { E1XX, TX_WINDOW_FIXED } },
525 { "E310", { E3XX, TX_WINDOW_FIXED } },
526 { "E3XX", { E3XX, TX_WINDOW_FIXED } },
527 { "X300", { X3XX, TX_WINDOW_FIXED } },
528 { "X310", { X3XX, TX_WINDOW_FIXED } },
Tom Tsou988a4642017-06-15 09:16:53 -0700529 { "USRP2", { USRP2, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700530 { "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
ignasj87ed77b2017-06-13 23:37:46 +0300531 { "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700532 };
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000533
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700534 // Compare UHD motherboard and device strings */
Tom Tsou988a4642017-06-15 09:16:53 -0700535 auto mapIter = devStringMap.begin();
536 while (mapIter != devStringMap.end()) {
537 if (devString.find(mapIter->first) != std::string::npos ||
538 mboardString.find(mapIter->first) != std::string::npos) {
539 dev_type = std::get<0>(mapIter->second);
540 tx_window = std::get<1>(mapIter->second);
541 return true;
542 }
543 mapIter++;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000544 }
545
Tom Tsou988a4642017-06-15 09:16:53 -0700546 LOG(ALERT) << "Unsupported device " << devString;
547 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000548}
549
Tom Tsou3b093bb2016-05-03 19:04:15 -0700550/*
551 * Check for UHD version > 3.9.0 for E3XX support
552 */
553static bool uhd_e3xx_version_chk()
554{
555 std::string ver = uhd::get_version_string();
556 std::string major_str(ver.begin(), ver.begin() + 3);
557 std::string minor_str(ver.begin() + 4, ver.begin() + 7);
558
559 int major_val = atoi(major_str.c_str());
560 int minor_val = atoi(minor_str.c_str());
561
562 if (major_val < 3)
563 return false;
564 if (minor_val < 9)
565 return false;
566
567 return true;
568}
569
Tom Tsou980525c2017-06-09 15:37:19 -0700570void uhd_device::set_channels(bool swap)
571{
572 if (iface == MULTI_ARFCN) {
573 if (dev_type != B200 && dev_type != B210)
574 throw std::invalid_argument("Device does not support MCBTS");
575 dev_type = B2XX_MCBTS;
576 chans = 1;
577 }
578
Tom Tsouf6115692017-06-26 10:13:25 -0700579 if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
Tom Tsou980525c2017-06-09 15:37:19 -0700580 throw std::invalid_argument("Device does not support number of requested channels");
581
582 std::string subdev_string;
583 switch (dev_type) {
584 case B210:
585 case E3XX:
586 if (chans == 1)
587 subdev_string = swap ? "A:B" : "A:A";
588 else if (chans == 2)
589 subdev_string = swap ? "A:B A:A" : "A:A A:B";
590 break;
591 case X3XX:
592 case UMTRX:
593 if (chans == 1)
594 subdev_string = swap ? "B:0" : "A:0";
595 else if (chans == 2)
596 subdev_string = swap ? "B:0 A:0" : "A:0 B:0";
597 break;
598 default:
599 break;
600 }
601
602 if (!subdev_string.empty()) {
603 uhd::usrp::subdev_spec_t spec(subdev_string);
604 usrp_dev->set_tx_subdev_spec(spec);
605 usrp_dev->set_rx_subdev_spec(spec);
606 }
607}
608
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700609int uhd_device::open(const std::string &args, int ref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000610{
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700611 const char *refstr;
612
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000613 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000614 uhd::device_addr_t addr(args);
615 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000616 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000617 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400618 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000619 }
620
621 // Use the first found device
622 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000623 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700624 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000625 } catch(...) {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700626 LOG(ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400627 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000628 }
629
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000630 // Check for a valid device type and set bus type
631 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400632 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000633
Tom Tsou3b093bb2016-05-03 19:04:15 -0700634 if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
635 LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater";
636 return -1;
637 }
638
Tom Tsou980525c2017-06-09 15:37:19 -0700639 try {
640 set_channels(swap_channels);
641 } catch (const std::exception &e) {
642 LOG(ALERT) << "Channel setting failed - " << e.what();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400643 return -1;
644 }
645
646 tx_freqs.resize(chans);
647 rx_freqs.resize(chans);
648 tx_gains.resize(chans);
649 rx_gains.resize(chans);
650 rx_buffers.resize(chans);
651
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700652 switch (ref) {
653 case REF_INTERNAL:
654 refstr = "internal";
655 break;
656 case REF_EXTERNAL:
657 refstr = "external";
658 break;
659 case REF_GPS:
660 refstr = "gpsdo";
661 break;
662 default:
663 LOG(ALERT) << "Invalid reference type";
664 return -1;
665 }
666
667 usrp_dev->set_clock_source(refstr);
Thomas Tsou010fff72013-10-16 00:31:18 -0400668
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700669 try {
670 set_rates();
671 } catch (const std::exception &e) {
672 LOG(ALERT) << "UHD rate setting failed - " << e.what();
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500673 return -1;
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700674 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000675
Alexander Chemerise1714252015-04-28 23:09:02 -0400676 // Set RF frontend bandwidth
677 if (dev_type == UMTRX) {
678 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
679 for (size_t i = 0; i < chans; i++) {
680 usrp_dev->set_tx_bandwidth(500*1000*2, i);
Tom Tsoud6ae8642017-03-30 17:22:58 -0700681 usrp_dev->set_rx_bandwidth(500*1000*2, i);
Alexander Chemerise1714252015-04-28 23:09:02 -0400682 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800683 } else if (dev_type == LIMESDR) {
684 for (size_t i = 0; i < chans; i++) {
685 usrp_dev->set_tx_bandwidth(5e6, i);
686 usrp_dev->set_rx_bandwidth(5e6, i);
687 }
Alexander Chemerise1714252015-04-28 23:09:02 -0400688 }
689
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400690 /* Create TX and RX streamers */
691 uhd::stream_args_t stream_args("sc16");
692 for (size_t i = 0; i < chans; i++)
693 stream_args.channels.push_back(i);
694
695 tx_stream = usrp_dev->get_tx_stream(stream_args);
696 rx_stream = usrp_dev->get_rx_stream(stream_args);
697
698 /* Number of samples per over-the-wire packet */
699 tx_spp = tx_stream->get_max_num_samps();
700 rx_spp = rx_stream->get_max_num_samps();
701
kurtis.heimerl965e7572011-11-26 03:16:54 +0000702 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400703 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400704 for (size_t i = 0; i < rx_buffers.size(); i++)
705 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000706
kurtis.heimerl02d04052011-11-26 03:17:10 +0000707 // Initialize and shadow gain values
708 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000709
kurtis.heimerl965e7572011-11-26 03:16:54 +0000710 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000711 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000712
Tom Tsou76764272016-06-24 14:25:39 -0700713 if (iface == MULTI_ARFCN)
714 return MULTI_ARFCN;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500715
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400716 switch (dev_type) {
717 case B100:
718 return RESAMP_64M;
719 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800720 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400721 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500722 case B200:
723 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100724 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800725 case E3XX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800726 case LIMESDR:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500727 default:
728 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400729 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400730
731 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000732}
733
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000734bool uhd_device::flush_recv(size_t num_pkts)
735{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000736 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000737 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800738 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000739
Thomas Tsou18d3b832014-02-13 14:55:23 -0500740 std::vector<std::vector<short> >
741 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000742
Thomas Tsou18d3b832014-02-13 14:55:23 -0500743 std::vector<short *> pkt_ptrs;
744 for (size_t i = 0; i < pkt_bufs.size(); i++)
745 pkt_ptrs.push_back(&pkt_bufs[i].front());
746
747 ts_initial = 0;
748 while (!ts_initial || (num_pkts-- > 0)) {
749 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400750 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000751 if (!num_smpls) {
752 switch (md.error_code) {
753 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800754 LOG(ALERT) << "Device timed out";
755 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000756 default:
757 continue;
758 }
759 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500760
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700761 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000762 }
763
Thomas Tsou18d3b832014-02-13 14:55:23 -0500764 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
765
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000766 return true;
767}
768
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800769bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000770{
Thomas Tsouc2839162014-03-27 13:52:58 -0400771 /* Allow 100 ms delay to align multi-channel streams */
772 double delay = 0.1;
773
kurtis.heimerl68292102011-11-26 03:17:28 +0000774 aligned = false;
775
Thomas Tsouc2839162014-03-27 13:52:58 -0400776 uhd::time_spec_t current = usrp_dev->get_time_now();
777
Thomas Tsou204a9f12013-10-29 18:34:16 -0400778 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400779 cmd.stream_now = false;
780 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400781
kurtis.heimerl68292102011-11-26 03:17:28 +0000782 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500783
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800784 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000785}
786
kurtis.heimerl965e7572011-11-26 03:16:54 +0000787bool uhd_device::start()
788{
789 LOG(INFO) << "Starting USRP...";
790
791 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000792 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000793 return false;
794 }
795
Tom Tsou72bf7622017-03-07 14:16:46 -0800796#ifndef USE_UHD_3_11
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400797 // Register msg handler
798 uhd::msg::register_handler(&uhd_msg_handler);
Tom Tsou72bf7622017-03-07 14:16:46 -0800799#endif
kurtis.heimerl965e7572011-11-26 03:16:54 +0000800 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800801 async_event_thrd = new Thread();
802 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000803
804 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800805 if (!restart())
806 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000807
kurtis.heimerl965e7572011-11-26 03:16:54 +0000808 // Display usrp time
809 double time_now = usrp_dev->get_time_now().get_real_secs();
810 LOG(INFO) << "The current time is " << time_now << " seconds";
811
812 started = true;
813 return true;
814}
815
816bool uhd_device::stop()
817{
Thomas Tsou2c791102013-11-15 23:00:28 -0500818 if (!started)
819 return false;
820
821 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000822 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
823
824 usrp_dev->issue_stream_cmd(stream_cmd);
825
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800826 async_event_thrd->cancel();
827 async_event_thrd->join();
828 delete async_event_thrd;
829
kurtis.heimerl965e7572011-11-26 03:16:54 +0000830 started = false;
831 return true;
832}
833
Thomas Tsou7553aa92013-11-08 12:50:03 -0500834void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000835{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500836 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000837 return;
838}
839
kurtis.heimerld4be0742011-11-26 03:17:46 +0000840int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000841{
kurtis.heimerld4be0742011-11-26 03:17:46 +0000842 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000843 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000844
845 switch (md.error_code) {
846 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000847 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800848 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000849 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
850 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
851 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
852 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
853 default:
854 return ERROR_UNHANDLED;
855 }
856 }
857
kurtis.heimerl965e7572011-11-26 03:16:54 +0000858 // Missing timestamp
859 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000860 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000861 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000862 }
863
864 // Monotonicity check
Tom Tsoua93f7892017-03-07 17:54:06 -0800865 if (md.time_spec < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000866 LOG(ALERT) << "UHD: Loss of monotonic time";
Tom Tsoua93f7892017-03-07 17:54:06 -0800867 LOG(ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
ttsou724eb362012-03-13 02:20:01 +0000868 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000869 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000870 }
871
Tom Tsoua93f7892017-03-07 17:54:06 -0800872 // Workaround for UHD tick rounding bug
873 TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
874 if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
875 md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
876
877 prev_ts = md.time_spec;
878
kurtis.heimerl965e7572011-11-26 03:16:54 +0000879 return 0;
880}
881
Thomas Tsou204a9f12013-10-29 18:34:16 -0400882int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
883 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000884{
885 ssize_t rc;
886 uhd::time_spec_t ts;
887 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000888
Thomas Tsou204a9f12013-10-29 18:34:16 -0400889 if (bufs.size() != chans) {
890 LOG(ALERT) << "Invalid channel combination " << bufs.size();
891 return -1;
892 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000893
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000894 *overrun = false;
895 *underrun = false;
896
kurtis.heimerl965e7572011-11-26 03:16:54 +0000897 // Shift read time with respect to transmit clock
898 timestamp += ts_offset;
899
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700900 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000901 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000902
903 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400904 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000905 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400906 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800907 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000908 return 0;
909 }
910
Thomas Tsou204a9f12013-10-29 18:34:16 -0400911 // Create vector buffer
912 std::vector<std::vector<short> >
913 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
914
915 std::vector<short *> pkt_ptrs;
916 for (size_t i = 0; i < pkt_bufs.size(); i++)
917 pkt_ptrs.push_back(&pkt_bufs[i].front());
918
kurtis.heimerl965e7572011-11-26 03:16:54 +0000919 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400920 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800921 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400922 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
923 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800924 thread_enable_cancel(true);
925
kurtis.heimerl965e7572011-11-26 03:16:54 +0000926 rx_pkt_cnt++;
927
kurtis.heimerld4be0742011-11-26 03:17:46 +0000928 // Check for errors
929 rc = check_rx_md_err(metadata, num_smpls);
930 switch (rc) {
931 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000932 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
933 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000934 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800935 case ERROR_TIMEOUT:
936 // Assume stopping condition
937 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000938 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -0400939 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000940 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000941 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000942 }
943
kurtis.heimerl965e7572011-11-26 03:16:54 +0000944 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000945 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000946
Thomas Tsou204a9f12013-10-29 18:34:16 -0400947 for (size_t i = 0; i < rx_buffers.size(); i++) {
948 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
949 num_smpls,
950 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000951
Thomas Tsou204a9f12013-10-29 18:34:16 -0400952 // Continue on local overrun, exit on other errors
953 if ((rc < 0)) {
954 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800955 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400956 if (rc != smpl_buf::ERROR_OVERFLOW)
957 return 0;
958 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000959 }
960 }
961
962 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -0400963 for (size_t i = 0; i < rx_buffers.size(); i++) {
964 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
965 if ((rc < 0) || (rc != len)) {
966 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800967 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400968 return 0;
969 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000970 }
971
972 return len;
973}
974
Thomas Tsou204a9f12013-10-29 18:34:16 -0400975int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
976 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000977{
978 uhd::tx_metadata_t metadata;
979 metadata.has_time_spec = true;
980 metadata.start_of_burst = false;
981 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700982 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000983
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000984 *underrun = false;
985
kurtis.heimerl965e7572011-11-26 03:16:54 +0000986 // No control packets
987 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000988 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000989 return 0;
990 }
991
Thomas Tsou204a9f12013-10-29 18:34:16 -0400992 if (bufs.size() != chans) {
993 LOG(ALERT) << "Invalid channel combination " << bufs.size();
994 return -1;
995 }
996
kurtis.heimerl965e7572011-11-26 03:16:54 +0000997 // Drop a fixed number of packets (magic value)
998 if (!aligned) {
999 drop_cnt++;
1000
1001 if (drop_cnt == 1) {
1002 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001003 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001004 metadata.end_of_burst = true;
1005 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001006 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001007 return len;
1008 } else {
1009 LOG(DEBUG) << "Aligning transmitter: start burst";
1010 metadata.start_of_burst = true;
1011 aligned = true;
1012 drop_cnt = 0;
1013 }
1014 }
1015
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001016 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001017 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001018 thread_enable_cancel(true);
1019
ttsoub371ed52012-01-09 18:11:34 +00001020 if (num_smpls != (unsigned) len) {
1021 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001022 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001023
1024 return num_smpls;
1025}
1026
1027bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1028{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001029 return true;
1030}
1031
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001032uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1033{
1034 double rf_spread, rf_freq;
1035 std::vector<double> freqs;
1036 uhd::tune_request_t treq(freq);
1037
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001038 if (dev_type == UMTRX) {
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001039 if (offset != 0.0)
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001040 return uhd::tune_request_t(freq, offset);
1041
1042 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1043 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1044 // only distort the signal (because cordic is not ideal).
1045 treq.target_freq = freq;
1046 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1047 treq.rf_freq = freq;
1048 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1049 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001050 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001051 } else if (chans == 1) {
Thomas Tsou8e17df72014-03-06 14:16:11 -05001052 if (offset == 0.0)
1053 return treq;
1054
1055 return uhd::tune_request_t(freq, offset);
1056 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001057 LOG(ALERT) << chans << " channels unsupported";
1058 return treq;
1059 }
1060
1061 if (tx)
1062 freqs = tx_freqs;
1063 else
1064 freqs = rx_freqs;
1065
1066 /* Tune directly if other channel isn't tuned */
1067 if (freqs[!chan] < 10.0)
1068 return treq;
1069
1070 /* Find center frequency between channels */
1071 rf_spread = fabs(freqs[!chan] - freq);
Tom Tsouf6115692017-06-26 10:13:25 -07001072 if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001073 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1074 return treq;
1075 }
1076
1077 rf_freq = (freqs[!chan] + freq) / 2.0f;
1078
1079 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1080 treq.target_freq = freq;
1081 treq.rf_freq = rf_freq;
1082
1083 return treq;
1084}
1085
1086bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1087{
1088 std::vector<double> freqs;
1089 uhd::tune_result_t tres;
1090 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1091
1092 if (tx) {
1093 tres = usrp_dev->set_tx_freq(treq, chan);
1094 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1095 } else {
1096 tres = usrp_dev->set_rx_freq(treq, chan);
1097 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1098 }
1099 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1100
Thomas Tsou8e17df72014-03-06 14:16:11 -05001101 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1102 return true;
1103
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001104 /* Manual RF policy means we intentionally tuned with a baseband
1105 * offset for dual-channel purposes. Now retune the other channel
1106 * with the opposite corresponding frequency offset
1107 */
1108 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1109 if (tx) {
1110 treq = select_freq(tx_freqs[!chan], !chan, true);
1111 tres = usrp_dev->set_tx_freq(treq, !chan);
1112 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1113 } else {
1114 treq = select_freq(rx_freqs[!chan], !chan, false);
1115 tres = usrp_dev->set_rx_freq(treq, !chan);
1116 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1117
1118 }
1119 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1120 }
1121
1122 return true;
1123}
1124
Thomas Tsou204a9f12013-10-29 18:34:16 -04001125bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001126{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001127 if (chan >= tx_freqs.size()) {
1128 LOG(ALERT) << "Requested non-existent channel " << chan;
1129 return false;
1130 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001131 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001132
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001133 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001134}
1135
Thomas Tsou204a9f12013-10-29 18:34:16 -04001136bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001137{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001138 if (chan >= rx_freqs.size()) {
1139 LOG(ALERT) << "Requested non-existent channel " << chan;
1140 return false;
1141 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001142 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001143
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001144 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001145}
1146
Thomas Tsou204a9f12013-10-29 18:34:16 -04001147double uhd_device::getTxFreq(size_t chan)
1148{
1149 if (chan >= tx_freqs.size()) {
1150 LOG(ALERT) << "Requested non-existent channel " << chan;
1151 return 0.0;
1152 }
1153
1154 return tx_freqs[chan];
1155}
1156
1157double uhd_device::getRxFreq(size_t chan)
1158{
1159 if (chan >= rx_freqs.size()) {
1160 LOG(ALERT) << "Requested non-existent channel " << chan;
1161 return 0.0;
1162 }
1163
1164 return rx_freqs[chan];
1165}
1166
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001167/*
1168 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1169 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1170 * combination.
1171 */
1172TIMESTAMP uhd_device::initialWriteTimestamp()
1173{
Tom Tsou76764272016-06-24 14:25:39 -07001174 if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001175 return ts_initial;
1176 else
1177 return ts_initial * tx_sps;
1178}
1179
1180TIMESTAMP uhd_device::initialReadTimestamp()
1181{
1182 return ts_initial;
1183}
1184
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001185double uhd_device::fullScaleInputValue()
1186{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001187 if (dev_type == LIMESDR)
ignasj28d80812017-06-13 23:37:46 +03001188 return (double) SHRT_MAX * LIMESDR_TX_AMPL;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001189 if (dev_type == UMTRX)
1190 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1191 else
1192 return (double) SHRT_MAX * USRP_TX_AMPL;
1193}
1194
1195double uhd_device::fullScaleOutputValue()
1196{
1197 return (double) SHRT_MAX;
1198}
1199
kurtis.heimerl965e7572011-11-26 03:16:54 +00001200bool uhd_device::recv_async_msg()
1201{
ttsou60dc4c92012-08-08 23:30:23 +00001202 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001203
1204 thread_enable_cancel(false);
1205 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1206 thread_enable_cancel(true);
1207 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001208 return false;
1209
1210 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001211 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001212 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001213
1214 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1215 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1216 LOG(ERR) << str_code(md);
1217 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001218 }
1219
1220 return true;
1221}
1222
1223std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1224{
1225 std::ostringstream ost("UHD: ");
1226
1227 switch (metadata.error_code) {
1228 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1229 ost << "No error";
1230 break;
1231 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1232 ost << "No packet received, implementation timed-out";
1233 break;
1234 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1235 ost << "A stream command was issued in the past";
1236 break;
1237 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1238 ost << "Expected another stream command";
1239 break;
1240 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1241 ost << "An internal receive buffer has filled";
1242 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001243 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1244 ost << "Multi-channel alignment failed";
1245 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001246 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1247 ost << "The packet could not be parsed";
1248 break;
1249 default:
1250 ost << "Unknown error " << metadata.error_code;
1251 }
1252
1253 if (metadata.has_time_spec)
1254 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1255
1256 return ost.str();
1257}
1258
1259std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1260{
1261 std::ostringstream ost("UHD: ");
1262
1263 switch (metadata.event_code) {
1264 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1265 ost << "A packet was successfully transmitted";
1266 break;
1267 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1268 ost << "An internal send buffer has emptied";
1269 break;
1270 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1271 ost << "Packet loss between host and device";
1272 break;
1273 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1274 ost << "Packet time was too late or too early";
1275 break;
1276 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1277 ost << "Underflow occurred inside a packet";
1278 break;
1279 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1280 ost << "Packet loss within a burst";
1281 break;
1282 default:
1283 ost << "Unknown error " << metadata.event_code;
1284 }
1285
1286 if (metadata.has_time_spec)
1287 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1288
1289 return ost.str();
1290}
1291
1292smpl_buf::smpl_buf(size_t len, double rate)
1293 : buf_len(len), clk_rt(rate),
1294 time_start(0), time_end(0), data_start(0), data_end(0)
1295{
1296 data = new uint32_t[len];
1297}
1298
1299smpl_buf::~smpl_buf()
1300{
1301 delete[] data;
1302}
1303
1304ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1305{
1306 if (timestamp < time_start)
1307 return ERROR_TIMESTAMP;
1308 else if (timestamp >= time_end)
1309 return 0;
1310 else
1311 return time_end - timestamp;
1312}
1313
1314ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1315{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001316 return avail_smpls(timespec.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001317}
1318
1319ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1320{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001321 int type_sz = 2 * sizeof(short);
1322
kurtis.heimerl965e7572011-11-26 03:16:54 +00001323 // Check for valid read
1324 if (timestamp < time_start)
1325 return ERROR_TIMESTAMP;
1326 if (timestamp >= time_end)
1327 return 0;
1328 if (len >= buf_len)
1329 return ERROR_READ;
1330
1331 // How many samples should be copied
1332 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001333 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001334 num_smpls = len;
1335
1336 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001337 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001338
1339 // Read it
1340 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001341 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001342 memcpy(buf, data + read_start, numBytes);
1343 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001344 size_t first_cp = (buf_len - read_start) * type_sz;
1345 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001346
1347 memcpy(buf, data + read_start, first_cp);
1348 memcpy((char*) buf + first_cp, data, second_cp);
1349 }
1350
1351 data_start = (read_start + len) % buf_len;
1352 time_start = timestamp + len;
1353
1354 if (time_start > time_end)
1355 return ERROR_READ;
1356 else
1357 return num_smpls;
1358}
1359
1360ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1361{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001362 return read(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001363}
1364
1365ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1366{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001367 int type_sz = 2 * sizeof(short);
1368
kurtis.heimerl965e7572011-11-26 03:16:54 +00001369 // Check for valid write
1370 if ((len == 0) || (len >= buf_len))
1371 return ERROR_WRITE;
1372 if ((timestamp + len) <= time_end)
1373 return ERROR_TIMESTAMP;
1374
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001375 if (timestamp < time_end) {
1376 LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001377 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1378 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 -04001379 // Do not return error here, because it's a rounding error and is not fatal
1380 }
1381 if (timestamp > time_end && time_end != 0) {
1382 LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001383 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1384 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 -04001385 // Do not return error here, because it's a rounding error and is not fatal
1386 }
1387
kurtis.heimerl965e7572011-11-26 03:16:54 +00001388 // Starting index
1389 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1390
1391 // Write it
1392 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001393 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001394 memcpy(data + write_start, buf, numBytes);
1395 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001396 size_t first_cp = (buf_len - write_start) * type_sz;
1397 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001398
1399 memcpy(data + write_start, buf, first_cp);
1400 memcpy(data, (char*) buf + first_cp, second_cp);
1401 }
1402
1403 data_end = (write_start + len) % buf_len;
1404 time_end = timestamp + len;
1405
Thomas Tsou18d3b832014-02-13 14:55:23 -05001406 if (!data_start)
1407 data_start = write_start;
1408
kurtis.heimerl965e7572011-11-26 03:16:54 +00001409 if (((write_start + len) > buf_len) && (data_end > data_start))
1410 return ERROR_OVERFLOW;
1411 else if (time_end <= time_start)
1412 return ERROR_WRITE;
1413 else
1414 return len;
1415}
1416
1417ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1418{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001419 return write(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001420}
1421
Tom Tsou1ae25562014-12-05 12:56:34 -08001422std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001423{
1424 std::ostringstream ost("Sample buffer: ");
1425
Tom Tsou1ae25562014-12-05 12:56:34 -08001426 ost << "timestamp = " << ts;
1427 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001428 ost << ", time_start = " << time_start;
1429 ost << ", time_end = " << time_end;
1430 ost << ", data_start = " << data_start;
1431 ost << ", data_end = " << data_end;
1432
1433 return ost.str();
1434}
1435
1436std::string smpl_buf::str_code(ssize_t code)
1437{
1438 switch (code) {
1439 case ERROR_TIMESTAMP:
1440 return "Sample buffer: Requested timestamp is not valid";
1441 case ERROR_READ:
1442 return "Sample buffer: Read error";
1443 case ERROR_WRITE:
1444 return "Sample buffer: Write error";
1445 case ERROR_OVERFLOW:
1446 return "Sample buffer: Overrun";
1447 default:
1448 return "Sample buffer: Unknown error";
1449 }
1450}
1451
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001452RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
Tom Tsou05c6feb2016-06-22 16:09:44 -07001453 InterfaceType iface, size_t chans, double offset)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001454{
Tom Tsou05c6feb2016-06-22 16:09:44 -07001455 return new uhd_device(tx_sps, rx_sps, iface, chans, offset);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001456}