blob: 4466da4a61add58b63e39247f2549c69978a46b1 [file] [log] [blame]
kurtis.heimerl965e7572011-11-26 03:16:54 +00001/*
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +01002 * 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
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100146 @param rate sample clockrate
147 @param timestamp
kurtis.heimerl965e7572011-11-26 03:16:54 +0000148 */
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
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100175 /** Formatted error string
kurtis.heimerl965e7572011-11-26 03:16:54 +0000176 @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,
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100211 size_t chans, double offset,
212 const std::vector<std::string>& tx_paths,
213 const std::vector<std::string>& rx_paths);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000214 ~uhd_device();
215
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700216 int open(const std::string &args, int ref, bool swap_channels);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000217 bool start();
218 bool stop();
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800219 bool restart();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500220 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400221 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000222
Thomas Tsou204a9f12013-10-29 18:34:16 -0400223 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000224 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
225
Thomas Tsou204a9f12013-10-29 18:34:16 -0400226 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000227 TIMESTAMP timestamp, bool isControl);
228
229 bool updateAlignment(TIMESTAMP timestamp);
230
Thomas Tsou204a9f12013-10-29 18:34:16 -0400231 bool setTxFreq(double wFreq, size_t chan);
232 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000233
Tom Tsou05c6feb2016-06-22 16:09:44 -0700234 TIMESTAMP initialWriteTimestamp();
235 TIMESTAMP initialReadTimestamp();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000236
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +0300237 double fullScaleInputValue();
238 double fullScaleOutputValue();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000239
Thomas Tsou204a9f12013-10-29 18:34:16 -0400240 double setRxGain(double db, size_t chan);
241 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000242 double maxRxGain(void) { return rx_gain_max; }
243 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000244
Thomas Tsou204a9f12013-10-29 18:34:16 -0400245 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000246 double maxTxGain(void) { return tx_gain_max; }
247 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000248
Thomas Tsou204a9f12013-10-29 18:34:16 -0400249 double getTxFreq(size_t chan);
250 double getRxFreq(size_t chan);
251 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000252
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100253 bool setRxAntenna(const std::string &ant, size_t chan);
254 std::string getRxAntenna(size_t chan);
255 bool setTxAntenna(const std::string &ant, size_t chan);
256 std::string getTxAntenna(size_t chan);
257
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400258 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000259 inline double numberRead() { return rx_pkt_cnt; }
260 inline double numberWritten() { return 0; }
261
262 /** Receive and process asynchronous message
263 @return true if message received or false on timeout or error
264 */
265 bool recv_async_msg();
266
kurtis.heimerld4be0742011-11-26 03:17:46 +0000267 enum err_code {
268 ERROR_TIMING = -1,
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800269 ERROR_TIMEOUT = -2,
270 ERROR_UNRECOVERABLE = -3,
271 ERROR_UNHANDLED = -4,
kurtis.heimerld4be0742011-11-26 03:17:46 +0000272 };
273
kurtis.heimerl965e7572011-11-26 03:16:54 +0000274private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000275 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400276 uhd::tx_streamer::sptr tx_stream;
277 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400278 enum TxWindowType tx_window;
279 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000280
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800281 size_t tx_sps, rx_sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400282 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000283
Thomas Tsou204a9f12013-10-29 18:34:16 -0400284 double tx_gain_min, tx_gain_max;
285 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500286 double offset;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000287
Thomas Tsou204a9f12013-10-29 18:34:16 -0400288 std::vector<double> tx_gains, rx_gains;
289 std::vector<double> tx_freqs, rx_freqs;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100290 std::vector<std::string> tx_paths, rx_paths;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000291 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000292
293 bool started;
294 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000295
296 size_t rx_pkt_cnt;
297 size_t drop_cnt;
298 uhd::time_spec_t prev_ts;
299
Thomas Tsou18d3b832014-02-13 14:55:23 -0500300 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400301 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000302
kurtis.heimerl02d04052011-11-26 03:17:10 +0000303 void init_gains();
Tom Tsou980525c2017-06-09 15:37:19 -0700304 void set_channels(bool swap);
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700305 void set_rates();
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100306 bool set_antennas();
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000307 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000308 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000309 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000310
kurtis.heimerl965e7572011-11-26 03:16:54 +0000311 std::string str_code(uhd::rx_metadata_t metadata);
312 std::string str_code(uhd::async_metadata_t metadata);
313
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500314 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
315 bool set_freq(double freq, size_t chan, bool tx);
316
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800317 Thread *async_event_thrd;
Tom Tsou05c6feb2016-06-22 16:09:44 -0700318 InterfaceType iface;
Tom Tsou93b7f372014-12-15 20:23:33 -0800319 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000320};
321
322void *async_event_loop(uhd_device *dev)
323{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500324 dev->setPriority(0.43);
325
kurtis.heimerl965e7572011-11-26 03:16:54 +0000326 while (1) {
327 dev->recv_async_msg();
328 pthread_testcancel();
329 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500330
331 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000332}
333
Tom Tsou72bf7622017-03-07 14:16:46 -0800334#ifndef USE_UHD_3_11
335/*
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000336 Catch and drop underrun 'U' and overrun 'O' messages from stdout
337 since we already report using the logging facility. Direct
338 everything else appropriately.
339 */
340void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
341{
342 switch (type) {
343 case uhd::msg::status:
344 LOG(INFO) << msg;
345 break;
346 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000347 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000348 break;
349 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000350 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000351 break;
352 case uhd::msg::fastpath:
353 break;
354 }
355}
Tom Tsou72bf7622017-03-07 14:16:46 -0800356#endif
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000357
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800358static void thread_enable_cancel(bool cancel)
359{
360 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
361 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
362}
363
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800364uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100365 InterfaceType iface, size_t chans, double offset,
366 const std::vector<std::string>& tx_paths,
367 const std::vector<std::string>& rx_paths)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400368 : tx_gain_min(0.0), tx_gain_max(0.0),
369 rx_gain_min(0.0), rx_gain_max(0.0),
370 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000371 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Pau Espin Pedrolb7095c72018-02-09 16:19:31 +0100372 prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000373{
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800374 this->tx_sps = tx_sps;
375 this->rx_sps = rx_sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400376 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500377 this->offset = offset;
Tom Tsou05c6feb2016-06-22 16:09:44 -0700378 this->iface = iface;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100379 this->tx_paths = tx_paths;
380 this->rx_paths = rx_paths;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000381}
382
383uhd_device::~uhd_device()
384{
385 stop();
386
Thomas Tsou204a9f12013-10-29 18:34:16 -0400387 for (size_t i = 0; i < rx_buffers.size(); i++)
388 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000389}
390
kurtis.heimerl24481de2011-11-26 03:17:18 +0000391void uhd_device::init_gains()
392{
393 uhd::gain_range_t range;
394
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400395 if (dev_type == UMTRX) {
396 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
397 if (gain_stages[0] == "VGA") {
398 LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
399 }
400 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
401 range = usrp_dev->get_tx_gain_range();
402 tx_gain_min = range.start();
403 tx_gain_max = range.stop();
404 } else {
405 range = usrp_dev->get_tx_gain_range("VGA2");
406 tx_gain_min = UMTRX_VGA1_DEF + range.start();
407 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
408 }
409 } else {
410 range = usrp_dev->get_tx_gain_range();
411 tx_gain_min = range.start();
412 tx_gain_max = range.stop();
413 }
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400414 LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000415
416 range = usrp_dev->get_rx_gain_range();
417 rx_gain_min = range.start();
418 rx_gain_max = range.stop();
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400419 LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000420
Thomas Tsou204a9f12013-10-29 18:34:16 -0400421 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400422 double gain = (tx_gain_min + tx_gain_max) / 2;
423 LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
424 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400425 tx_gains[i] = usrp_dev->get_tx_gain(i);
426 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000427
Thomas Tsou204a9f12013-10-29 18:34:16 -0400428 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400429 double gain = (rx_gain_min + rx_gain_max) / 2;
430 LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
431 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400432 rx_gains[i] = usrp_dev->get_rx_gain(i);
433 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000434
435 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400436
kurtis.heimerl24481de2011-11-26 03:17:18 +0000437}
438
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700439void uhd_device::set_rates()
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700440{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700441 dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
442 if (desc.mcr != 0.0)
443 usrp_dev->set_master_clock_rate(desc.mcr);
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700444
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700445 tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate;
446 rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate;
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700447
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700448 usrp_dev->set_tx_rate(tx_rate);
449 usrp_dev->set_rx_rate(rx_rate);
450 tx_rate = usrp_dev->get_tx_rate();
451 rx_rate = usrp_dev->get_rx_rate();
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700452
Tom Tsou1b6ab7d2017-06-15 15:31:08 -0700453 ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700454 LOG(INFO) << "Rates configured for " << desc.str;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000455}
456
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100457bool uhd_device::set_antennas()
458{
459 unsigned int i;
460
461 for (i = 0; i < tx_paths.size(); i++) {
462 if (tx_paths[i] == "")
463 continue;
464 LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i];
465 if (!setTxAntenna(tx_paths[i], i)) {
466 LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i];
467 return false;
468 }
469 }
470
471 for (i = 0; i < rx_paths.size(); i++) {
472 if (rx_paths[i] == "")
473 continue;
474 LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i];
475 if (!setRxAntenna(rx_paths[i], i)) {
476 LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i];
477 return false;
478 }
479 }
480 LOG(INFO) << "Antennas configured successfully";
481 return true;
482}
483
Thomas Tsou204a9f12013-10-29 18:34:16 -0400484double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000485{
Tom Tsou76764272016-06-24 14:25:39 -0700486 if (iface == MULTI_ARFCN)
487 chan = 0;
488
Thomas Tsou204a9f12013-10-29 18:34:16 -0400489 if (chan >= tx_gains.size()) {
490 LOG(ALERT) << "Requested non-existent channel" << chan;
491 return 0.0f;
492 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000493
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400494 if (dev_type == UMTRX) {
495 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
496 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
497 usrp_dev->set_tx_gain(db, chan);
498 } else {
499 // New UHD versions support split configuration of
500 // Tx gain stages. We utilize this to set the gain
501 // configuration, optimal for the Tx signal quality.
502 // From our measurements, VGA1 must be 18dB plus-minus
503 // one and VGA2 is the best when 23dB or lower.
504 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
505 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
506 }
507 } else {
508 usrp_dev->set_tx_gain(db, chan);
509 }
510
Thomas Tsou204a9f12013-10-29 18:34:16 -0400511 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000512
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400513 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400514
515 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000516}
517
Thomas Tsou204a9f12013-10-29 18:34:16 -0400518double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000519{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400520 if (chan >= rx_gains.size()) {
521 LOG(ALERT) << "Requested non-existent channel " << chan;
522 return 0.0f;
523 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000524
Thomas Tsou204a9f12013-10-29 18:34:16 -0400525 usrp_dev->set_rx_gain(db, chan);
526 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000527
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400528 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400529
530 return rx_gains[chan];
531}
532
533double uhd_device::getRxGain(size_t chan)
534{
Tom Tsou76764272016-06-24 14:25:39 -0700535 if (iface == MULTI_ARFCN)
536 chan = 0;
537
Thomas Tsou204a9f12013-10-29 18:34:16 -0400538 if (chan >= rx_gains.size()) {
539 LOG(ALERT) << "Requested non-existent channel " << chan;
540 return 0.0f;
541 }
542
543 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000544}
545
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000546/*
547 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400548 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000549 deal with the transport latency. Reject the USRP1 because UHD doesn't
550 support timestamped samples with it.
551 */
552bool uhd_device::parse_dev_type()
553{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700554 uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree();
555 std::string devString = prop_tree->access<std::string>("/name").get();
556 std::string mboardString = usrp_dev->get_mboard_name();
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000557
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700558 const std::map<std::string, std::pair<uhd_dev_type, TxWindowType>> devStringMap {
559 { "B100", { B100, TX_WINDOW_USRP1 } },
560 { "B200", { B200, TX_WINDOW_USRP1 } },
561 { "B200mini", { B200, TX_WINDOW_USRP1 } },
Piotr Krysikaa60dda2017-12-04 00:29:16 +0700562 { "B205mini", { B200, TX_WINDOW_USRP1 } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700563 { "B210", { B210, TX_WINDOW_USRP1 } },
564 { "E100", { E1XX, TX_WINDOW_FIXED } },
565 { "E110", { E1XX, TX_WINDOW_FIXED } },
566 { "E310", { E3XX, TX_WINDOW_FIXED } },
567 { "E3XX", { E3XX, TX_WINDOW_FIXED } },
568 { "X300", { X3XX, TX_WINDOW_FIXED } },
569 { "X310", { X3XX, TX_WINDOW_FIXED } },
Tom Tsou988a4642017-06-15 09:16:53 -0700570 { "USRP2", { USRP2, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700571 { "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
ignasj87ed77b2017-06-13 23:37:46 +0300572 { "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700573 };
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000574
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700575 // Compare UHD motherboard and device strings */
Tom Tsou988a4642017-06-15 09:16:53 -0700576 auto mapIter = devStringMap.begin();
577 while (mapIter != devStringMap.end()) {
578 if (devString.find(mapIter->first) != std::string::npos ||
579 mboardString.find(mapIter->first) != std::string::npos) {
580 dev_type = std::get<0>(mapIter->second);
581 tx_window = std::get<1>(mapIter->second);
582 return true;
583 }
584 mapIter++;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000585 }
586
Tom Tsou988a4642017-06-15 09:16:53 -0700587 LOG(ALERT) << "Unsupported device " << devString;
588 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000589}
590
Tom Tsou3b093bb2016-05-03 19:04:15 -0700591/*
592 * Check for UHD version > 3.9.0 for E3XX support
593 */
594static bool uhd_e3xx_version_chk()
595{
596 std::string ver = uhd::get_version_string();
597 std::string major_str(ver.begin(), ver.begin() + 3);
598 std::string minor_str(ver.begin() + 4, ver.begin() + 7);
599
600 int major_val = atoi(major_str.c_str());
601 int minor_val = atoi(minor_str.c_str());
602
603 if (major_val < 3)
604 return false;
605 if (minor_val < 9)
606 return false;
607
608 return true;
609}
610
Tom Tsou980525c2017-06-09 15:37:19 -0700611void uhd_device::set_channels(bool swap)
612{
613 if (iface == MULTI_ARFCN) {
614 if (dev_type != B200 && dev_type != B210)
615 throw std::invalid_argument("Device does not support MCBTS");
616 dev_type = B2XX_MCBTS;
617 chans = 1;
618 }
619
Tom Tsouf6115692017-06-26 10:13:25 -0700620 if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
Tom Tsou980525c2017-06-09 15:37:19 -0700621 throw std::invalid_argument("Device does not support number of requested channels");
622
623 std::string subdev_string;
624 switch (dev_type) {
625 case B210:
626 case E3XX:
627 if (chans == 1)
628 subdev_string = swap ? "A:B" : "A:A";
629 else if (chans == 2)
630 subdev_string = swap ? "A:B A:A" : "A:A A:B";
631 break;
632 case X3XX:
633 case UMTRX:
634 if (chans == 1)
635 subdev_string = swap ? "B:0" : "A:0";
636 else if (chans == 2)
637 subdev_string = swap ? "B:0 A:0" : "A:0 B:0";
638 break;
639 default:
640 break;
641 }
642
643 if (!subdev_string.empty()) {
644 uhd::usrp::subdev_spec_t spec(subdev_string);
645 usrp_dev->set_tx_subdev_spec(spec);
646 usrp_dev->set_rx_subdev_spec(spec);
647 }
648}
649
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700650int uhd_device::open(const std::string &args, int ref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000651{
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700652 const char *refstr;
653
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000654 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000655 uhd::device_addr_t addr(args);
656 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000657 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000658 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400659 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000660 }
661
662 // Use the first found device
663 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000664 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700665 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000666 } catch(...) {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700667 LOG(ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400668 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000669 }
670
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000671 // Check for a valid device type and set bus type
672 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400673 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000674
Tom Tsou3b093bb2016-05-03 19:04:15 -0700675 if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
676 LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater";
677 return -1;
678 }
679
Tom Tsou980525c2017-06-09 15:37:19 -0700680 try {
681 set_channels(swap_channels);
682 } catch (const std::exception &e) {
683 LOG(ALERT) << "Channel setting failed - " << e.what();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400684 return -1;
685 }
686
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100687 if (!set_antennas()) {
688 LOG(ALERT) << "UHD antenna setting failed";
689 return -1;
690 }
691
Thomas Tsou204a9f12013-10-29 18:34:16 -0400692 tx_freqs.resize(chans);
693 rx_freqs.resize(chans);
694 tx_gains.resize(chans);
695 rx_gains.resize(chans);
696 rx_buffers.resize(chans);
697
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700698 switch (ref) {
699 case REF_INTERNAL:
700 refstr = "internal";
701 break;
702 case REF_EXTERNAL:
703 refstr = "external";
704 break;
705 case REF_GPS:
706 refstr = "gpsdo";
707 break;
708 default:
709 LOG(ALERT) << "Invalid reference type";
710 return -1;
711 }
712
713 usrp_dev->set_clock_source(refstr);
Thomas Tsou010fff72013-10-16 00:31:18 -0400714
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700715 try {
716 set_rates();
717 } catch (const std::exception &e) {
718 LOG(ALERT) << "UHD rate setting failed - " << e.what();
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500719 return -1;
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700720 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000721
Alexander Chemerise1714252015-04-28 23:09:02 -0400722 // Set RF frontend bandwidth
723 if (dev_type == UMTRX) {
724 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
725 for (size_t i = 0; i < chans; i++) {
726 usrp_dev->set_tx_bandwidth(500*1000*2, i);
Tom Tsoud6ae8642017-03-30 17:22:58 -0700727 usrp_dev->set_rx_bandwidth(500*1000*2, i);
Alexander Chemerise1714252015-04-28 23:09:02 -0400728 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800729 } else if (dev_type == LIMESDR) {
730 for (size_t i = 0; i < chans; i++) {
731 usrp_dev->set_tx_bandwidth(5e6, i);
732 usrp_dev->set_rx_bandwidth(5e6, i);
733 }
Alexander Chemerise1714252015-04-28 23:09:02 -0400734 }
735
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400736 /* Create TX and RX streamers */
737 uhd::stream_args_t stream_args("sc16");
738 for (size_t i = 0; i < chans; i++)
739 stream_args.channels.push_back(i);
740
741 tx_stream = usrp_dev->get_tx_stream(stream_args);
742 rx_stream = usrp_dev->get_rx_stream(stream_args);
743
744 /* Number of samples per over-the-wire packet */
745 tx_spp = tx_stream->get_max_num_samps();
746 rx_spp = rx_stream->get_max_num_samps();
747
kurtis.heimerl965e7572011-11-26 03:16:54 +0000748 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400749 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400750 for (size_t i = 0; i < rx_buffers.size(); i++)
751 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000752
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100753 // Initialize and shadow gain values
kurtis.heimerl02d04052011-11-26 03:17:10 +0000754 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000755
kurtis.heimerl965e7572011-11-26 03:16:54 +0000756 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000757 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000758
Tom Tsou76764272016-06-24 14:25:39 -0700759 if (iface == MULTI_ARFCN)
760 return MULTI_ARFCN;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500761
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400762 switch (dev_type) {
763 case B100:
764 return RESAMP_64M;
765 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800766 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400767 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500768 case B200:
769 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100770 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800771 case E3XX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800772 case LIMESDR:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500773 default:
774 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400775 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400776
777 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000778}
779
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000780bool uhd_device::flush_recv(size_t num_pkts)
781{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000782 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000783 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800784 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000785
Thomas Tsou18d3b832014-02-13 14:55:23 -0500786 std::vector<std::vector<short> >
787 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000788
Thomas Tsou18d3b832014-02-13 14:55:23 -0500789 std::vector<short *> pkt_ptrs;
790 for (size_t i = 0; i < pkt_bufs.size(); i++)
791 pkt_ptrs.push_back(&pkt_bufs[i].front());
792
793 ts_initial = 0;
794 while (!ts_initial || (num_pkts-- > 0)) {
795 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400796 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000797 if (!num_smpls) {
798 switch (md.error_code) {
799 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800800 LOG(ALERT) << "Device timed out";
801 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000802 default:
803 continue;
804 }
805 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500806
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700807 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000808 }
809
Thomas Tsou18d3b832014-02-13 14:55:23 -0500810 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
811
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000812 return true;
813}
814
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800815bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000816{
Thomas Tsouc2839162014-03-27 13:52:58 -0400817 /* Allow 100 ms delay to align multi-channel streams */
818 double delay = 0.1;
819
kurtis.heimerl68292102011-11-26 03:17:28 +0000820 aligned = false;
821
Thomas Tsouc2839162014-03-27 13:52:58 -0400822 uhd::time_spec_t current = usrp_dev->get_time_now();
823
Thomas Tsou204a9f12013-10-29 18:34:16 -0400824 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400825 cmd.stream_now = false;
826 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400827
kurtis.heimerl68292102011-11-26 03:17:28 +0000828 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500829
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800830 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000831}
832
kurtis.heimerl965e7572011-11-26 03:16:54 +0000833bool uhd_device::start()
834{
835 LOG(INFO) << "Starting USRP...";
836
837 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000838 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000839 return false;
840 }
841
Tom Tsou72bf7622017-03-07 14:16:46 -0800842#ifndef USE_UHD_3_11
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400843 // Register msg handler
844 uhd::msg::register_handler(&uhd_msg_handler);
Tom Tsou72bf7622017-03-07 14:16:46 -0800845#endif
kurtis.heimerl965e7572011-11-26 03:16:54 +0000846 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800847 async_event_thrd = new Thread();
848 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000849
850 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800851 if (!restart())
852 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000853
kurtis.heimerl965e7572011-11-26 03:16:54 +0000854 // Display usrp time
855 double time_now = usrp_dev->get_time_now().get_real_secs();
856 LOG(INFO) << "The current time is " << time_now << " seconds";
857
858 started = true;
859 return true;
860}
861
862bool uhd_device::stop()
863{
Thomas Tsou2c791102013-11-15 23:00:28 -0500864 if (!started)
865 return false;
866
867 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000868 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
869
870 usrp_dev->issue_stream_cmd(stream_cmd);
871
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800872 async_event_thrd->cancel();
873 async_event_thrd->join();
874 delete async_event_thrd;
875
kurtis.heimerl965e7572011-11-26 03:16:54 +0000876 started = false;
877 return true;
878}
879
Thomas Tsou7553aa92013-11-08 12:50:03 -0500880void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000881{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500882 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000883 return;
884}
885
kurtis.heimerld4be0742011-11-26 03:17:46 +0000886int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000887{
kurtis.heimerld4be0742011-11-26 03:17:46 +0000888 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000889 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000890
891 switch (md.error_code) {
892 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000893 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800894 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000895 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
896 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
897 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
898 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
899 default:
900 return ERROR_UNHANDLED;
901 }
902 }
903
kurtis.heimerl965e7572011-11-26 03:16:54 +0000904 // Missing timestamp
905 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000906 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000907 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000908 }
909
910 // Monotonicity check
Tom Tsoua93f7892017-03-07 17:54:06 -0800911 if (md.time_spec < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000912 LOG(ALERT) << "UHD: Loss of monotonic time";
Tom Tsoua93f7892017-03-07 17:54:06 -0800913 LOG(ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
ttsou724eb362012-03-13 02:20:01 +0000914 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000915 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000916 }
917
Tom Tsoua93f7892017-03-07 17:54:06 -0800918 // Workaround for UHD tick rounding bug
919 TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
920 if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
921 md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
922
923 prev_ts = md.time_spec;
924
kurtis.heimerl965e7572011-11-26 03:16:54 +0000925 return 0;
926}
927
Thomas Tsou204a9f12013-10-29 18:34:16 -0400928int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
929 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000930{
931 ssize_t rc;
932 uhd::time_spec_t ts;
933 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000934
Thomas Tsou204a9f12013-10-29 18:34:16 -0400935 if (bufs.size() != chans) {
936 LOG(ALERT) << "Invalid channel combination " << bufs.size();
937 return -1;
938 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000939
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000940 *overrun = false;
941 *underrun = false;
942
kurtis.heimerl965e7572011-11-26 03:16:54 +0000943 // Shift read time with respect to transmit clock
944 timestamp += ts_offset;
945
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700946 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000947 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000948
949 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400950 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000951 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400952 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800953 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000954 return 0;
955 }
956
Thomas Tsou204a9f12013-10-29 18:34:16 -0400957 // Create vector buffer
958 std::vector<std::vector<short> >
959 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
960
961 std::vector<short *> pkt_ptrs;
962 for (size_t i = 0; i < pkt_bufs.size(); i++)
963 pkt_ptrs.push_back(&pkt_bufs[i].front());
964
kurtis.heimerl965e7572011-11-26 03:16:54 +0000965 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400966 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800967 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400968 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
969 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800970 thread_enable_cancel(true);
971
kurtis.heimerl965e7572011-11-26 03:16:54 +0000972 rx_pkt_cnt++;
973
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100974 // Check for errors
kurtis.heimerld4be0742011-11-26 03:17:46 +0000975 rc = check_rx_md_err(metadata, num_smpls);
976 switch (rc) {
977 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000978 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
979 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000980 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800981 case ERROR_TIMEOUT:
982 // Assume stopping condition
983 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000984 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -0400985 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000986 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000987 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000988 }
989
kurtis.heimerl965e7572011-11-26 03:16:54 +0000990 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000991 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000992
Thomas Tsou204a9f12013-10-29 18:34:16 -0400993 for (size_t i = 0; i < rx_buffers.size(); i++) {
994 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
995 num_smpls,
996 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000997
Thomas Tsou204a9f12013-10-29 18:34:16 -0400998 // Continue on local overrun, exit on other errors
999 if ((rc < 0)) {
1000 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001001 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001002 if (rc != smpl_buf::ERROR_OVERFLOW)
1003 return 0;
1004 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001005 }
1006 }
1007
1008 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -04001009 for (size_t i = 0; i < rx_buffers.size(); i++) {
1010 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
1011 if ((rc < 0) || (rc != len)) {
1012 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001013 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001014 return 0;
1015 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001016 }
1017
1018 return len;
1019}
1020
Thomas Tsou204a9f12013-10-29 18:34:16 -04001021int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
1022 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001023{
1024 uhd::tx_metadata_t metadata;
1025 metadata.has_time_spec = true;
1026 metadata.start_of_burst = false;
1027 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001028 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001029
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001030 *underrun = false;
1031
kurtis.heimerl965e7572011-11-26 03:16:54 +00001032 // No control packets
1033 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001034 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001035 return 0;
1036 }
1037
Thomas Tsou204a9f12013-10-29 18:34:16 -04001038 if (bufs.size() != chans) {
1039 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1040 return -1;
1041 }
1042
kurtis.heimerl965e7572011-11-26 03:16:54 +00001043 // Drop a fixed number of packets (magic value)
1044 if (!aligned) {
1045 drop_cnt++;
1046
1047 if (drop_cnt == 1) {
1048 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001049 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001050 metadata.end_of_burst = true;
1051 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001052 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001053 return len;
1054 } else {
1055 LOG(DEBUG) << "Aligning transmitter: start burst";
1056 metadata.start_of_burst = true;
1057 aligned = true;
1058 drop_cnt = 0;
1059 }
1060 }
1061
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001062 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001063 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001064 thread_enable_cancel(true);
1065
ttsoub371ed52012-01-09 18:11:34 +00001066 if (num_smpls != (unsigned) len) {
1067 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001068 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001069
1070 return num_smpls;
1071}
1072
1073bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1074{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001075 return true;
1076}
1077
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001078uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1079{
1080 double rf_spread, rf_freq;
1081 std::vector<double> freqs;
1082 uhd::tune_request_t treq(freq);
1083
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001084 if (dev_type == UMTRX) {
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001085 if (offset != 0.0)
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001086 return uhd::tune_request_t(freq, offset);
1087
1088 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1089 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1090 // only distort the signal (because cordic is not ideal).
1091 treq.target_freq = freq;
1092 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1093 treq.rf_freq = freq;
1094 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1095 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001096 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001097 } else if (chans == 1) {
Thomas Tsou8e17df72014-03-06 14:16:11 -05001098 if (offset == 0.0)
1099 return treq;
1100
1101 return uhd::tune_request_t(freq, offset);
1102 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001103 LOG(ALERT) << chans << " channels unsupported";
1104 return treq;
1105 }
1106
1107 if (tx)
1108 freqs = tx_freqs;
1109 else
1110 freqs = rx_freqs;
1111
1112 /* Tune directly if other channel isn't tuned */
1113 if (freqs[!chan] < 10.0)
1114 return treq;
1115
1116 /* Find center frequency between channels */
1117 rf_spread = fabs(freqs[!chan] - freq);
Tom Tsouf6115692017-06-26 10:13:25 -07001118 if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001119 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1120 return treq;
1121 }
1122
1123 rf_freq = (freqs[!chan] + freq) / 2.0f;
1124
1125 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1126 treq.target_freq = freq;
1127 treq.rf_freq = rf_freq;
1128
1129 return treq;
1130}
1131
1132bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1133{
1134 std::vector<double> freqs;
1135 uhd::tune_result_t tres;
1136 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1137
1138 if (tx) {
1139 tres = usrp_dev->set_tx_freq(treq, chan);
1140 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1141 } else {
1142 tres = usrp_dev->set_rx_freq(treq, chan);
1143 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1144 }
1145 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1146
Thomas Tsou8e17df72014-03-06 14:16:11 -05001147 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1148 return true;
1149
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001150 /* Manual RF policy means we intentionally tuned with a baseband
1151 * offset for dual-channel purposes. Now retune the other channel
1152 * with the opposite corresponding frequency offset
1153 */
1154 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1155 if (tx) {
1156 treq = select_freq(tx_freqs[!chan], !chan, true);
1157 tres = usrp_dev->set_tx_freq(treq, !chan);
1158 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1159 } else {
1160 treq = select_freq(rx_freqs[!chan], !chan, false);
1161 tres = usrp_dev->set_rx_freq(treq, !chan);
1162 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1163
1164 }
1165 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1166 }
1167
1168 return true;
1169}
1170
Thomas Tsou204a9f12013-10-29 18:34:16 -04001171bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001172{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001173 if (chan >= tx_freqs.size()) {
1174 LOG(ALERT) << "Requested non-existent channel " << chan;
1175 return false;
1176 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001177 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001178
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001179 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001180}
1181
Thomas Tsou204a9f12013-10-29 18:34:16 -04001182bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001183{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001184 if (chan >= rx_freqs.size()) {
1185 LOG(ALERT) << "Requested non-existent channel " << chan;
1186 return false;
1187 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001188 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001189
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001190 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001191}
1192
Thomas Tsou204a9f12013-10-29 18:34:16 -04001193double uhd_device::getTxFreq(size_t chan)
1194{
1195 if (chan >= tx_freqs.size()) {
1196 LOG(ALERT) << "Requested non-existent channel " << chan;
1197 return 0.0;
1198 }
1199
1200 return tx_freqs[chan];
1201}
1202
1203double uhd_device::getRxFreq(size_t chan)
1204{
1205 if (chan >= rx_freqs.size()) {
1206 LOG(ALERT) << "Requested non-existent channel " << chan;
1207 return 0.0;
1208 }
1209
1210 return rx_freqs[chan];
1211}
1212
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001213bool uhd_device::setRxAntenna(const std::string &ant, size_t chan)
1214{
1215 std::vector<std::string> avail;
1216 if (chan >= rx_paths.size()) {
1217 LOG(ALERT) << "Requested non-existent channel " << chan;
1218 return false;
1219 }
1220
1221 avail = usrp_dev->get_rx_antennas(chan);
1222 if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
1223 LOG(ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan;
1224 LOG(INFO) << "Available Rx antennas: ";
1225 for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
1226 LOG(INFO) << "- '" << *i << "'";
1227 return false;
1228 }
1229 usrp_dev->set_rx_antenna(ant, chan);
1230 rx_paths[chan] = usrp_dev->get_rx_antenna(chan);
1231
1232 if (ant != rx_paths[chan]) {
1233 LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan];
1234 return false;
1235 }
1236
1237 return true;
1238}
1239
1240std::string uhd_device::getRxAntenna(size_t chan)
1241{
1242 if (chan >= rx_paths.size()) {
1243 LOG(ALERT) << "Requested non-existent channel " << chan;
1244 return "";
1245 }
1246 return usrp_dev->get_rx_antenna(chan);
1247}
1248
1249bool uhd_device::setTxAntenna(const std::string &ant, size_t chan)
1250{
1251 std::vector<std::string> avail;
1252 if (chan >= tx_paths.size()) {
1253 LOG(ALERT) << "Requested non-existent channel " << chan;
1254 return false;
1255 }
1256
1257 avail = usrp_dev->get_tx_antennas(chan);
1258 if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
1259 LOG(ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan;
1260 LOG(INFO) << "Available Tx antennas: ";
1261 for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
1262 LOG(INFO) << "- '" << *i << "'";
1263 return false;
1264 }
1265 usrp_dev->set_tx_antenna(ant, chan);
1266 tx_paths[chan] = usrp_dev->get_tx_antenna(chan);
1267
1268 if (ant != tx_paths[chan]) {
1269 LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan];
1270 return false;
1271 }
1272
1273 return true;
1274}
1275
1276std::string uhd_device::getTxAntenna(size_t chan)
1277{
1278 if (chan >= tx_paths.size()) {
1279 LOG(ALERT) << "Requested non-existent channel " << chan;
1280 return "";
1281 }
1282 return usrp_dev->get_tx_antenna(chan);
1283}
1284
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001285/*
1286 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1287 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1288 * combination.
1289 */
1290TIMESTAMP uhd_device::initialWriteTimestamp()
1291{
Tom Tsou76764272016-06-24 14:25:39 -07001292 if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001293 return ts_initial;
1294 else
1295 return ts_initial * tx_sps;
1296}
1297
1298TIMESTAMP uhd_device::initialReadTimestamp()
1299{
1300 return ts_initial;
1301}
1302
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001303double uhd_device::fullScaleInputValue()
1304{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001305 if (dev_type == LIMESDR)
ignasj28d80812017-06-13 23:37:46 +03001306 return (double) SHRT_MAX * LIMESDR_TX_AMPL;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001307 if (dev_type == UMTRX)
1308 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1309 else
1310 return (double) SHRT_MAX * USRP_TX_AMPL;
1311}
1312
1313double uhd_device::fullScaleOutputValue()
1314{
1315 return (double) SHRT_MAX;
1316}
1317
kurtis.heimerl965e7572011-11-26 03:16:54 +00001318bool uhd_device::recv_async_msg()
1319{
ttsou60dc4c92012-08-08 23:30:23 +00001320 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001321
1322 thread_enable_cancel(false);
1323 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1324 thread_enable_cancel(true);
1325 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001326 return false;
1327
1328 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001329 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001330 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001331
1332 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1333 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1334 LOG(ERR) << str_code(md);
1335 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001336 }
1337
1338 return true;
1339}
1340
1341std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1342{
1343 std::ostringstream ost("UHD: ");
1344
1345 switch (metadata.error_code) {
1346 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1347 ost << "No error";
1348 break;
1349 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1350 ost << "No packet received, implementation timed-out";
1351 break;
1352 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1353 ost << "A stream command was issued in the past";
1354 break;
1355 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1356 ost << "Expected another stream command";
1357 break;
1358 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1359 ost << "An internal receive buffer has filled";
1360 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001361 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1362 ost << "Multi-channel alignment failed";
1363 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001364 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1365 ost << "The packet could not be parsed";
1366 break;
1367 default:
1368 ost << "Unknown error " << metadata.error_code;
1369 }
1370
1371 if (metadata.has_time_spec)
1372 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1373
1374 return ost.str();
1375}
1376
1377std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1378{
1379 std::ostringstream ost("UHD: ");
1380
1381 switch (metadata.event_code) {
1382 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1383 ost << "A packet was successfully transmitted";
1384 break;
1385 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1386 ost << "An internal send buffer has emptied";
1387 break;
1388 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1389 ost << "Packet loss between host and device";
1390 break;
1391 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1392 ost << "Packet time was too late or too early";
1393 break;
1394 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1395 ost << "Underflow occurred inside a packet";
1396 break;
1397 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1398 ost << "Packet loss within a burst";
1399 break;
1400 default:
1401 ost << "Unknown error " << metadata.event_code;
1402 }
1403
1404 if (metadata.has_time_spec)
1405 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1406
1407 return ost.str();
1408}
1409
1410smpl_buf::smpl_buf(size_t len, double rate)
1411 : buf_len(len), clk_rt(rate),
1412 time_start(0), time_end(0), data_start(0), data_end(0)
1413{
1414 data = new uint32_t[len];
1415}
1416
1417smpl_buf::~smpl_buf()
1418{
1419 delete[] data;
1420}
1421
1422ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1423{
1424 if (timestamp < time_start)
1425 return ERROR_TIMESTAMP;
1426 else if (timestamp >= time_end)
1427 return 0;
1428 else
1429 return time_end - timestamp;
1430}
1431
1432ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1433{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001434 return avail_smpls(timespec.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001435}
1436
1437ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1438{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001439 int type_sz = 2 * sizeof(short);
1440
kurtis.heimerl965e7572011-11-26 03:16:54 +00001441 // Check for valid read
1442 if (timestamp < time_start)
1443 return ERROR_TIMESTAMP;
1444 if (timestamp >= time_end)
1445 return 0;
1446 if (len >= buf_len)
1447 return ERROR_READ;
1448
1449 // How many samples should be copied
1450 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001451 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001452 num_smpls = len;
1453
1454 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001455 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001456
1457 // Read it
1458 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001459 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001460 memcpy(buf, data + read_start, numBytes);
1461 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001462 size_t first_cp = (buf_len - read_start) * type_sz;
1463 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001464
1465 memcpy(buf, data + read_start, first_cp);
1466 memcpy((char*) buf + first_cp, data, second_cp);
1467 }
1468
1469 data_start = (read_start + len) % buf_len;
1470 time_start = timestamp + len;
1471
1472 if (time_start > time_end)
1473 return ERROR_READ;
1474 else
1475 return num_smpls;
1476}
1477
1478ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1479{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001480 return read(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001481}
1482
1483ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1484{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001485 int type_sz = 2 * sizeof(short);
1486
kurtis.heimerl965e7572011-11-26 03:16:54 +00001487 // Check for valid write
1488 if ((len == 0) || (len >= buf_len))
1489 return ERROR_WRITE;
1490 if ((timestamp + len) <= time_end)
1491 return ERROR_TIMESTAMP;
1492
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001493 if (timestamp < time_end) {
1494 LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001495 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1496 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 -04001497 // Do not return error here, because it's a rounding error and is not fatal
1498 }
1499 if (timestamp > time_end && time_end != 0) {
1500 LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001501 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1502 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 -04001503 // Do not return error here, because it's a rounding error and is not fatal
1504 }
1505
kurtis.heimerl965e7572011-11-26 03:16:54 +00001506 // Starting index
1507 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1508
1509 // Write it
1510 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001511 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001512 memcpy(data + write_start, buf, numBytes);
1513 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001514 size_t first_cp = (buf_len - write_start) * type_sz;
1515 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001516
1517 memcpy(data + write_start, buf, first_cp);
1518 memcpy(data, (char*) buf + first_cp, second_cp);
1519 }
1520
1521 data_end = (write_start + len) % buf_len;
1522 time_end = timestamp + len;
1523
Thomas Tsou18d3b832014-02-13 14:55:23 -05001524 if (!data_start)
1525 data_start = write_start;
1526
kurtis.heimerl965e7572011-11-26 03:16:54 +00001527 if (((write_start + len) > buf_len) && (data_end > data_start))
1528 return ERROR_OVERFLOW;
1529 else if (time_end <= time_start)
1530 return ERROR_WRITE;
1531 else
1532 return len;
1533}
1534
1535ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1536{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001537 return write(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001538}
1539
Tom Tsou1ae25562014-12-05 12:56:34 -08001540std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001541{
1542 std::ostringstream ost("Sample buffer: ");
1543
Tom Tsou1ae25562014-12-05 12:56:34 -08001544 ost << "timestamp = " << ts;
1545 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001546 ost << ", time_start = " << time_start;
1547 ost << ", time_end = " << time_end;
1548 ost << ", data_start = " << data_start;
1549 ost << ", data_end = " << data_end;
1550
1551 return ost.str();
1552}
1553
1554std::string smpl_buf::str_code(ssize_t code)
1555{
1556 switch (code) {
1557 case ERROR_TIMESTAMP:
1558 return "Sample buffer: Requested timestamp is not valid";
1559 case ERROR_READ:
1560 return "Sample buffer: Read error";
1561 case ERROR_WRITE:
1562 return "Sample buffer: Write error";
1563 case ERROR_OVERFLOW:
1564 return "Sample buffer: Overrun";
1565 default:
1566 return "Sample buffer: Unknown error";
1567 }
1568}
1569
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001570RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001571 InterfaceType iface, size_t chans, double offset,
1572 const std::vector<std::string>& tx_paths,
1573 const std::vector<std::string>& rx_paths)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001574{
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001575 return new uhd_device(tx_sps, rx_sps, iface, chans, offset, tx_paths, rx_paths);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001576}