blob: 3db09a8b1474421071bd089cc15052c0cfdfa9ad [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
kurtis.heimerlce317332011-11-26 03:18:39 +000032#ifdef HAVE_CONFIG_H
33#include "config.h"
34#endif
35
Tom Tsou72bf7622017-03-07 14:16:46 -080036#ifndef USE_UHD_3_11
37#include <uhd/utils/msg.hpp>
Pau Espin Pedrol1f4a0092018-08-30 17:24:49 +020038#include <uhd/utils/thread_priority.hpp>
39#else
40#include <uhd/utils/thread.hpp>
Tom Tsou72bf7622017-03-07 14:16:46 -080041#endif
42
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +030043#define USRP_TX_AMPL 0.3
44#define UMTRX_TX_AMPL 0.7
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -080045#define LIMESDR_TX_AMPL 0.3
Thomas Tsoue3e88142013-04-05 20:42:41 -040046#define SAMPLE_BUF_SZ (1 << 20)
Thomas Tsou02d88d12013-04-05 15:36:30 -040047
Tom Tsoueb54bdd2014-11-25 16:06:32 -080048/*
49 * UHD timeout value on streaming (re)start
50 *
51 * Allow some time for streaming to commence after the start command is issued,
52 * but consider a wait beyond one second to be a definite error condition.
53 */
54#define UHD_RESTART_TIMEOUT 1.0
55
Alexander Chemeris4d029d82014-04-19 17:25:00 +040056/*
57 * UmTRX specific settings
58 */
59#define UMTRX_VGA1_DEF -18
60
Thomas Tsou02d88d12013-04-05 15:36:30 -040061enum uhd_dev_type {
62 USRP1,
63 USRP2,
64 B100,
Thomas Tsoue7882392014-02-13 14:46:23 -050065 B200,
66 B210,
Tom Tsou76764272016-06-24 14:25:39 -070067 B2XX_MCBTS,
Thomas Tsoua5c83ae2014-04-02 03:31:44 +010068 E1XX,
Tom Tsou4ad9ea62014-12-03 18:47:20 -080069 E3XX,
70 X3XX,
Thomas Tsouc88d8d52013-08-21 17:55:54 -040071 UMTRX,
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -080072 LIMESDR,
Thomas Tsoue3e88142013-04-05 20:42:41 -040073};
74
kurtis.heimerl965e7572011-11-26 03:16:54 +000075/*
Tom Tsouc3129052015-08-21 18:28:52 -070076 * USRP version dependent device timings
77 */
Tom Tsou72bf7622017-03-07 14:16:46 -080078#if defined(USE_UHD_3_9) || defined(USE_UHD_3_11)
Tom Tsouc3129052015-08-21 18:28:52 -070079#define B2XX_TIMING_1SPS 1.7153e-4
80#define B2XX_TIMING_4SPS 1.1696e-4
Tom Tsoud2b07032016-04-26 19:28:59 -070081#define B2XX_TIMING_4_4SPS 6.18462e-5
Tom Tsoua93f7892017-03-07 17:54:06 -080082#define B2XX_TIMING_MCBTS 7e-5
Tom Tsou80cb0802017-01-19 13:44:02 -080083#else
84#define B2XX_TIMING_1SPS 9.9692e-5
85#define B2XX_TIMING_4SPS 6.9248e-5
86#define B2XX_TIMING_4_4SPS 4.52308e-5
Tom Tsoua93f7892017-03-07 17:54:06 -080087#define B2XX_TIMING_MCBTS 6.42452e-5
Tom Tsou80cb0802017-01-19 13:44:02 -080088#endif
Tom Tsouc3129052015-08-21 18:28:52 -070089
90/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040091 * Tx / Rx sample offset values. In a perfect world, there is no group delay
92 * though analog components, and behaviour through digital filters exactly
93 * matches calculated values. In reality, there are unaccounted factors,
94 * which are captured in these empirically measured (using a loopback test)
95 * timing correction values.
96 *
97 * Notes:
98 * USRP1 with timestamps is not supported by UHD.
99 */
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700100
101/* Device Type, Tx-SPS, Rx-SPS */
102typedef std::tuple<uhd_dev_type, int, int> dev_key;
103
104/* Device parameter descriptor */
105struct dev_desc {
106 unsigned channels;
107 double mcr;
108 double rate;
109 double offset;
110 std::string str;
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800111};
kurtis.heimerl965e7572011-11-26 03:16:54 +0000112
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700113static const std::map<dev_key, dev_desc> dev_param_map {
114 { std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } },
115 { std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } },
116 { std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } },
117 { std::make_tuple(B100, 1, 1), { 1, 0.0, 400000, 1.2104e-4, "B100 1 SPS" } },
118 { std::make_tuple(B100, 4, 1), { 1, 0.0, 400000, 7.9307e-5, "B100 4/1 Tx/Rx SPS" } },
119 { std::make_tuple(B200, 1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS" } },
120 { std::make_tuple(B200, 4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } },
121 { std::make_tuple(B200, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
122 { std::make_tuple(B210, 1, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B210 1 SPS" } },
123 { std::make_tuple(B210, 4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" } },
124 { std::make_tuple(B210, 4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B210 4 SPS" } },
125 { std::make_tuple(E1XX, 1, 1), { 1, 52e6, GSMRATE, 9.5192e-5, "E1XX 1 SPS" } },
126 { std::make_tuple(E1XX, 4, 1), { 1, 52e6, GSMRATE, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" } },
127 { std::make_tuple(E3XX, 1, 1), { 2, 26e6, GSMRATE, 1.8462e-4, "E3XX 1 SPS" } },
128 { std::make_tuple(E3XX, 4, 1), { 2, 26e6, GSMRATE, 1.2923e-4, "E3XX 4/1 Tx/Rx SPS" } },
129 { std::make_tuple(X3XX, 1, 1), { 2, 0.0, 390625, 1.5360e-4, "X3XX 1 SPS" } },
130 { std::make_tuple(X3XX, 4, 1), { 2, 0.0, 390625, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS" } },
131 { std::make_tuple(X3XX, 4, 4), { 2, 0.0, 390625, 5.6567e-5, "X3XX 4 SPS" } },
132 { std::make_tuple(UMTRX, 1, 1), { 2, 0.0, GSMRATE, 9.9692e-5, "UmTRX 1 SPS" } },
133 { std::make_tuple(UMTRX, 4, 1), { 2, 0.0, GSMRATE, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS"} },
134 { std::make_tuple(UMTRX, 4, 4), { 2, 0.0, GSMRATE, 5.1503e-5, "UmTRX 4 SPS" } },
Tom Tsou4cafb0f2017-06-26 10:13:25 -0700135 { std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS" } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700136 { std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
137};
Thomas Tsoucb69f082013-04-08 14:18:26 -0400138
kurtis.heimerl965e7572011-11-26 03:16:54 +0000139/*
Alexander Chemeris1ba69e72016-06-18 10:50:11 +0300140 Sample Buffer - Allows reading and writing of timed samples using osmo-trx
kurtis.heimerl965e7572011-11-26 03:16:54 +0000141 or UHD style timestamps. Time conversions are handled
142 internally or accessable through the static convert calls.
143*/
144class smpl_buf {
145public:
146 /** Sample buffer constructor
147 @param len number of 32-bit samples the buffer should hold
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100148 @param rate sample clockrate
149 @param timestamp
kurtis.heimerl965e7572011-11-26 03:16:54 +0000150 */
151 smpl_buf(size_t len, double rate);
152 ~smpl_buf();
153
154 /** Query number of samples available for reading
155 @param timestamp time of first sample
156 @return number of available samples or error
157 */
158 ssize_t avail_smpls(TIMESTAMP timestamp) const;
159 ssize_t avail_smpls(uhd::time_spec_t timestamp) const;
160
161 /** Read and write
162 @param buf pointer to buffer
163 @param len number of samples desired to read or write
164 @param timestamp time of first stample
165 @return number of actual samples read or written or error
166 */
167 ssize_t read(void *buf, size_t len, TIMESTAMP timestamp);
168 ssize_t read(void *buf, size_t len, uhd::time_spec_t timestamp);
169 ssize_t write(void *buf, size_t len, TIMESTAMP timestamp);
170 ssize_t write(void *buf, size_t len, uhd::time_spec_t timestamp);
171
172 /** Buffer status string
173 @return a formatted string describing internal buffer state
174 */
Tom Tsou1ae25562014-12-05 12:56:34 -0800175 std::string str_status(size_t ts) const;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000176
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100177 /** Formatted error string
kurtis.heimerl965e7572011-11-26 03:16:54 +0000178 @param code an error code
179 @return a formatted error string
180 */
181 static std::string str_code(ssize_t code);
182
183 enum err_code {
184 ERROR_TIMESTAMP = -1,
185 ERROR_READ = -2,
186 ERROR_WRITE = -3,
187 ERROR_OVERFLOW = -4
188 };
189
190private:
191 uint32_t *data;
192 size_t buf_len;
193
194 double clk_rt;
195
196 TIMESTAMP time_start;
197 TIMESTAMP time_end;
198
199 size_t data_start;
200 size_t data_end;
201};
202
203/*
204 uhd_device - UHD implementation of the Device interface. Timestamped samples
205 are sent to and received from the device. An intermediate buffer
206 on the receive side collects and aligns packets of samples.
207 Events and errors such as underruns are reported asynchronously
208 by the device and received in a separate thread.
209*/
210class uhd_device : public RadioDevice {
211public:
Tom Tsou05c6feb2016-06-22 16:09:44 -0700212 uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100213 size_t chans, double offset,
214 const std::vector<std::string>& tx_paths,
215 const std::vector<std::string>& rx_paths);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000216 ~uhd_device();
217
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700218 int open(const std::string &args, int ref, bool swap_channels);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000219 bool start();
220 bool stop();
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800221 bool restart();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500222 void setPriority(float prio);
Thomas Tsou02d88d12013-04-05 15:36:30 -0400223 enum TxWindowType getWindowType() { return tx_window; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000224
Thomas Tsou204a9f12013-10-29 18:34:16 -0400225 int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000226 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
227
Thomas Tsou204a9f12013-10-29 18:34:16 -0400228 int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
kurtis.heimerl965e7572011-11-26 03:16:54 +0000229 TIMESTAMP timestamp, bool isControl);
230
231 bool updateAlignment(TIMESTAMP timestamp);
232
Thomas Tsou204a9f12013-10-29 18:34:16 -0400233 bool setTxFreq(double wFreq, size_t chan);
234 bool setRxFreq(double wFreq, size_t chan);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000235
Tom Tsou05c6feb2016-06-22 16:09:44 -0700236 TIMESTAMP initialWriteTimestamp();
237 TIMESTAMP initialReadTimestamp();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000238
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +0300239 double fullScaleInputValue();
240 double fullScaleOutputValue();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000241
Thomas Tsou204a9f12013-10-29 18:34:16 -0400242 double setRxGain(double db, size_t chan);
243 double getRxGain(size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000244 double maxRxGain(void) { return rx_gain_max; }
245 double minRxGain(void) { return rx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000246
Thomas Tsou204a9f12013-10-29 18:34:16 -0400247 double setTxGain(double db, size_t chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000248 double maxTxGain(void) { return tx_gain_max; }
249 double minTxGain(void) { return tx_gain_min; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000250
Thomas Tsou204a9f12013-10-29 18:34:16 -0400251 double getTxFreq(size_t chan);
252 double getRxFreq(size_t chan);
253 double getRxFreq();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000254
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100255 bool setRxAntenna(const std::string &ant, size_t chan);
256 std::string getRxAntenna(size_t chan);
257 bool setTxAntenna(const std::string &ant, size_t chan);
258 std::string getTxAntenna(size_t chan);
259
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +0200260 bool requiresRadioAlign();
261
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +0200262 GSM::Time minLatency();
263
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400264 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000265 inline double numberRead() { return rx_pkt_cnt; }
266 inline double numberWritten() { return 0; }
267
268 /** Receive and process asynchronous message
269 @return true if message received or false on timeout or error
270 */
271 bool recv_async_msg();
272
kurtis.heimerld4be0742011-11-26 03:17:46 +0000273 enum err_code {
274 ERROR_TIMING = -1,
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800275 ERROR_TIMEOUT = -2,
276 ERROR_UNRECOVERABLE = -3,
277 ERROR_UNHANDLED = -4,
kurtis.heimerld4be0742011-11-26 03:17:46 +0000278 };
279
kurtis.heimerl965e7572011-11-26 03:16:54 +0000280private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000281 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400282 uhd::tx_streamer::sptr tx_stream;
283 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400284 enum TxWindowType tx_window;
285 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000286
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400287 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000288
Thomas Tsou204a9f12013-10-29 18:34:16 -0400289 double tx_gain_min, tx_gain_max;
290 double rx_gain_min, rx_gain_max;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000291
Thomas Tsou204a9f12013-10-29 18:34:16 -0400292 std::vector<double> tx_gains, rx_gains;
293 std::vector<double> tx_freqs, rx_freqs;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000294 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000295
296 bool started;
297 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000298
299 size_t rx_pkt_cnt;
300 size_t drop_cnt;
301 uhd::time_spec_t prev_ts;
302
Thomas Tsou18d3b832014-02-13 14:55:23 -0500303 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400304 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000305
kurtis.heimerl02d04052011-11-26 03:17:10 +0000306 void init_gains();
Tom Tsou980525c2017-06-09 15:37:19 -0700307 void set_channels(bool swap);
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700308 void set_rates();
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000309 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000310 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000311 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000312
kurtis.heimerl965e7572011-11-26 03:16:54 +0000313 std::string str_code(uhd::rx_metadata_t metadata);
314 std::string str_code(uhd::async_metadata_t metadata);
315
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500316 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
317 bool set_freq(double freq, size_t chan, bool tx);
318
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800319 Thread *async_event_thrd;
Tom Tsou93b7f372014-12-15 20:23:33 -0800320 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000321};
322
323void *async_event_loop(uhd_device *dev)
324{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +0200325 set_selfthread_name("UHDAsyncEvent");
Thomas Tsou7553aa92013-11-08 12:50:03 -0500326 dev->setPriority(0.43);
327
kurtis.heimerl965e7572011-11-26 03:16:54 +0000328 while (1) {
329 dev->recv_async_msg();
330 pthread_testcancel();
331 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500332
333 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000334}
335
Tom Tsou72bf7622017-03-07 14:16:46 -0800336#ifndef USE_UHD_3_11
337/*
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000338 Catch and drop underrun 'U' and overrun 'O' messages from stdout
339 since we already report using the logging facility. Direct
340 everything else appropriately.
341 */
342void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
343{
344 switch (type) {
345 case uhd::msg::status:
Harald Welte5cc88582018-08-17 19:55:38 +0200346 LOGC(DDEV, INFO) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000347 break;
348 case uhd::msg::warning:
Harald Welte5cc88582018-08-17 19:55:38 +0200349 LOGC(DDEV, WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000350 break;
351 case uhd::msg::error:
Harald Welte5cc88582018-08-17 19:55:38 +0200352 LOGC(DDEV, ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000353 break;
354 case uhd::msg::fastpath:
355 break;
356 }
357}
Tom Tsou72bf7622017-03-07 14:16:46 -0800358#endif
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000359
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800360static void thread_enable_cancel(bool cancel)
361{
362 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
363 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
364}
365
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800366uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
Harald Welte61707e82018-06-13 23:21:57 +0200367 InterfaceType iface, size_t chans, double lo_offset,
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100368 const std::vector<std::string>& tx_paths,
369 const std::vector<std::string>& rx_paths)
Harald Welte61707e82018-06-13 23:21:57 +0200370 : RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths),
371 tx_gain_min(0.0), tx_gain_max(0.0),
Thomas Tsou204a9f12013-10-29 18:34:16 -0400372 rx_gain_min(0.0), rx_gain_max(0.0),
373 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000374 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Pau Espin Pedrolb7095c72018-02-09 16:19:31 +0100375 prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000376{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000377}
378
379uhd_device::~uhd_device()
380{
381 stop();
382
Thomas Tsou204a9f12013-10-29 18:34:16 -0400383 for (size_t i = 0; i < rx_buffers.size(); i++)
384 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000385}
386
kurtis.heimerl24481de2011-11-26 03:17:18 +0000387void uhd_device::init_gains()
388{
389 uhd::gain_range_t range;
390
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400391 if (dev_type == UMTRX) {
392 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
393 if (gain_stages[0] == "VGA") {
Harald Welte5cc88582018-08-17 19:55:38 +0200394 LOGC(DDEV, WARNING) << "Update your UHD version for a proper Tx gain support";
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400395 }
396 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
397 range = usrp_dev->get_tx_gain_range();
398 tx_gain_min = range.start();
399 tx_gain_max = range.stop();
400 } else {
401 range = usrp_dev->get_tx_gain_range("VGA2");
402 tx_gain_min = UMTRX_VGA1_DEF + range.start();
403 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
404 }
405 } else {
406 range = usrp_dev->get_tx_gain_range();
407 tx_gain_min = range.start();
408 tx_gain_max = range.stop();
409 }
Harald Welte5cc88582018-08-17 19:55:38 +0200410 LOGC(DDEV, INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000411
412 range = usrp_dev->get_rx_gain_range();
413 rx_gain_min = range.start();
414 rx_gain_max = range.stop();
Harald Welte5cc88582018-08-17 19:55:38 +0200415 LOGC(DDEV, INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000416
Thomas Tsou204a9f12013-10-29 18:34:16 -0400417 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400418 double gain = (tx_gain_min + tx_gain_max) / 2;
Harald Welte5cc88582018-08-17 19:55:38 +0200419 LOGC(DDEV, INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400420 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400421 tx_gains[i] = usrp_dev->get_tx_gain(i);
422 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000423
Thomas Tsou204a9f12013-10-29 18:34:16 -0400424 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400425 double gain = (rx_gain_min + rx_gain_max) / 2;
Harald Welte5cc88582018-08-17 19:55:38 +0200426 LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400427 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400428 rx_gains[i] = usrp_dev->get_rx_gain(i);
429 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000430
431 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400432
kurtis.heimerl24481de2011-11-26 03:17:18 +0000433}
434
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700435void uhd_device::set_rates()
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700436{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700437 dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
438 if (desc.mcr != 0.0)
439 usrp_dev->set_master_clock_rate(desc.mcr);
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700440
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700441 tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate;
442 rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate;
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700443
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700444 usrp_dev->set_tx_rate(tx_rate);
445 usrp_dev->set_rx_rate(rx_rate);
446 tx_rate = usrp_dev->get_tx_rate();
447 rx_rate = usrp_dev->get_rx_rate();
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700448
Tom Tsou1b6ab7d2017-06-15 15:31:08 -0700449 ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
Harald Welte5cc88582018-08-17 19:55:38 +0200450 LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000451}
452
Thomas Tsou204a9f12013-10-29 18:34:16 -0400453double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000454{
Tom Tsou76764272016-06-24 14:25:39 -0700455 if (iface == MULTI_ARFCN)
456 chan = 0;
457
Thomas Tsou204a9f12013-10-29 18:34:16 -0400458 if (chan >= tx_gains.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200459 LOGC(DDEV, ALERT) << "Requested non-existent channel" << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400460 return 0.0f;
461 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000462
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400463 if (dev_type == UMTRX) {
464 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
465 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
466 usrp_dev->set_tx_gain(db, chan);
467 } else {
468 // New UHD versions support split configuration of
469 // Tx gain stages. We utilize this to set the gain
470 // configuration, optimal for the Tx signal quality.
471 // From our measurements, VGA1 must be 18dB plus-minus
472 // one and VGA2 is the best when 23dB or lower.
473 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
474 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
475 }
476 } else {
477 usrp_dev->set_tx_gain(db, chan);
478 }
479
Thomas Tsou204a9f12013-10-29 18:34:16 -0400480 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000481
Harald Welte5cc88582018-08-17 19:55:38 +0200482 LOGC(DDEV, INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400483
484 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000485}
486
Thomas Tsou204a9f12013-10-29 18:34:16 -0400487double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000488{
Pau Espin Pedrol1c4bbad2018-11-01 18:30:12 +0100489 if (iface == MULTI_ARFCN)
490 chan = 0;
491
Thomas Tsou204a9f12013-10-29 18:34:16 -0400492 if (chan >= rx_gains.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200493 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400494 return 0.0f;
495 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000496
Thomas Tsou204a9f12013-10-29 18:34:16 -0400497 usrp_dev->set_rx_gain(db, chan);
498 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000499
Harald Welte5cc88582018-08-17 19:55:38 +0200500 LOGC(DDEV, INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400501
502 return rx_gains[chan];
503}
504
505double uhd_device::getRxGain(size_t chan)
506{
Tom Tsou76764272016-06-24 14:25:39 -0700507 if (iface == MULTI_ARFCN)
508 chan = 0;
509
Thomas Tsou204a9f12013-10-29 18:34:16 -0400510 if (chan >= rx_gains.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200511 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400512 return 0.0f;
513 }
514
515 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000516}
517
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000518/*
519 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400520 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000521 deal with the transport latency. Reject the USRP1 because UHD doesn't
522 support timestamped samples with it.
523 */
524bool uhd_device::parse_dev_type()
525{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700526 uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree();
527 std::string devString = prop_tree->access<std::string>("/name").get();
528 std::string mboardString = usrp_dev->get_mboard_name();
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000529
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700530 const std::map<std::string, std::pair<uhd_dev_type, TxWindowType>> devStringMap {
531 { "B100", { B100, TX_WINDOW_USRP1 } },
532 { "B200", { B200, TX_WINDOW_USRP1 } },
533 { "B200mini", { B200, TX_WINDOW_USRP1 } },
Piotr Krysikaa60dda2017-12-04 00:29:16 +0700534 { "B205mini", { B200, TX_WINDOW_USRP1 } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700535 { "B210", { B210, TX_WINDOW_USRP1 } },
536 { "E100", { E1XX, TX_WINDOW_FIXED } },
537 { "E110", { E1XX, TX_WINDOW_FIXED } },
538 { "E310", { E3XX, TX_WINDOW_FIXED } },
539 { "E3XX", { E3XX, TX_WINDOW_FIXED } },
540 { "X300", { X3XX, TX_WINDOW_FIXED } },
541 { "X310", { X3XX, TX_WINDOW_FIXED } },
Tom Tsou988a4642017-06-15 09:16:53 -0700542 { "USRP2", { USRP2, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700543 { "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
ignasj87ed77b2017-06-13 23:37:46 +0300544 { "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700545 };
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000546
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700547 // Compare UHD motherboard and device strings */
Tom Tsou988a4642017-06-15 09:16:53 -0700548 auto mapIter = devStringMap.begin();
549 while (mapIter != devStringMap.end()) {
550 if (devString.find(mapIter->first) != std::string::npos ||
551 mboardString.find(mapIter->first) != std::string::npos) {
552 dev_type = std::get<0>(mapIter->second);
553 tx_window = std::get<1>(mapIter->second);
554 return true;
555 }
556 mapIter++;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000557 }
558
Harald Welte5cc88582018-08-17 19:55:38 +0200559 LOGC(DDEV, ALERT) << "Unsupported device " << devString;
Tom Tsou988a4642017-06-15 09:16:53 -0700560 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000561}
562
Tom Tsou3b093bb2016-05-03 19:04:15 -0700563/*
564 * Check for UHD version > 3.9.0 for E3XX support
565 */
566static bool uhd_e3xx_version_chk()
567{
568 std::string ver = uhd::get_version_string();
569 std::string major_str(ver.begin(), ver.begin() + 3);
570 std::string minor_str(ver.begin() + 4, ver.begin() + 7);
571
572 int major_val = atoi(major_str.c_str());
573 int minor_val = atoi(minor_str.c_str());
574
575 if (major_val < 3)
576 return false;
577 if (minor_val < 9)
578 return false;
579
580 return true;
581}
582
Tom Tsou980525c2017-06-09 15:37:19 -0700583void uhd_device::set_channels(bool swap)
584{
585 if (iface == MULTI_ARFCN) {
586 if (dev_type != B200 && dev_type != B210)
587 throw std::invalid_argument("Device does not support MCBTS");
588 dev_type = B2XX_MCBTS;
589 chans = 1;
590 }
591
Tom Tsouf6115692017-06-26 10:13:25 -0700592 if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
Tom Tsou980525c2017-06-09 15:37:19 -0700593 throw std::invalid_argument("Device does not support number of requested channels");
594
595 std::string subdev_string;
596 switch (dev_type) {
597 case B210:
598 case E3XX:
599 if (chans == 1)
600 subdev_string = swap ? "A:B" : "A:A";
601 else if (chans == 2)
602 subdev_string = swap ? "A:B A:A" : "A:A A:B";
603 break;
604 case X3XX:
605 case UMTRX:
606 if (chans == 1)
607 subdev_string = swap ? "B:0" : "A:0";
608 else if (chans == 2)
609 subdev_string = swap ? "B:0 A:0" : "A:0 B:0";
610 break;
611 default:
612 break;
613 }
614
615 if (!subdev_string.empty()) {
616 uhd::usrp::subdev_spec_t spec(subdev_string);
617 usrp_dev->set_tx_subdev_spec(spec);
618 usrp_dev->set_rx_subdev_spec(spec);
619 }
620}
621
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700622int uhd_device::open(const std::string &args, int ref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000623{
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700624 const char *refstr;
625
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000626 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000627 uhd::device_addr_t addr(args);
628 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000629 if (dev_addrs.size() == 0) {
Harald Welte5cc88582018-08-17 19:55:38 +0200630 LOGC(DDEV, ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400631 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000632 }
633
634 // Use the first found device
Harald Welte5cc88582018-08-17 19:55:38 +0200635 LOGC(DDEV, INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000636 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700637 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000638 } catch(...) {
Harald Welte5cc88582018-08-17 19:55:38 +0200639 LOGC(DDEV, ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400640 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000641 }
642
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000643 // Check for a valid device type and set bus type
644 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400645 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000646
Tom Tsou3b093bb2016-05-03 19:04:15 -0700647 if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200648 LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater";
Tom Tsou3b093bb2016-05-03 19:04:15 -0700649 return -1;
650 }
651
Tom Tsou980525c2017-06-09 15:37:19 -0700652 try {
653 set_channels(swap_channels);
654 } catch (const std::exception &e) {
Harald Welte5cc88582018-08-17 19:55:38 +0200655 LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400656 return -1;
657 }
658
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100659 if (!set_antennas()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200660 LOGC(DDEV, ALERT) << "UHD antenna setting failed";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100661 return -1;
662 }
663
Thomas Tsou204a9f12013-10-29 18:34:16 -0400664 tx_freqs.resize(chans);
665 rx_freqs.resize(chans);
666 tx_gains.resize(chans);
667 rx_gains.resize(chans);
668 rx_buffers.resize(chans);
669
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700670 switch (ref) {
671 case REF_INTERNAL:
672 refstr = "internal";
673 break;
674 case REF_EXTERNAL:
675 refstr = "external";
676 break;
677 case REF_GPS:
678 refstr = "gpsdo";
679 break;
680 default:
Harald Welte5cc88582018-08-17 19:55:38 +0200681 LOGC(DDEV, ALERT) << "Invalid reference type";
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700682 return -1;
683 }
684
685 usrp_dev->set_clock_source(refstr);
Thomas Tsou010fff72013-10-16 00:31:18 -0400686
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700687 try {
688 set_rates();
689 } catch (const std::exception &e) {
Harald Welte5cc88582018-08-17 19:55:38 +0200690 LOGC(DDEV, ALERT) << "UHD rate setting failed - " << e.what();
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500691 return -1;
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700692 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000693
Alexander Chemerise1714252015-04-28 23:09:02 -0400694 // Set RF frontend bandwidth
695 if (dev_type == UMTRX) {
696 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
697 for (size_t i = 0; i < chans; i++) {
698 usrp_dev->set_tx_bandwidth(500*1000*2, i);
Tom Tsoud6ae8642017-03-30 17:22:58 -0700699 usrp_dev->set_rx_bandwidth(500*1000*2, i);
Alexander Chemerise1714252015-04-28 23:09:02 -0400700 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800701 } else if (dev_type == LIMESDR) {
702 for (size_t i = 0; i < chans; i++) {
Pau Espin Pedrol55df1e42018-05-08 20:49:00 +0200703 usrp_dev->set_tx_bandwidth(5.2e6, i);
704 usrp_dev->set_rx_bandwidth(1.4001e6, i);
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800705 }
Alexander Chemerise1714252015-04-28 23:09:02 -0400706 }
707
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400708 /* Create TX and RX streamers */
709 uhd::stream_args_t stream_args("sc16");
710 for (size_t i = 0; i < chans; i++)
711 stream_args.channels.push_back(i);
712
713 tx_stream = usrp_dev->get_tx_stream(stream_args);
714 rx_stream = usrp_dev->get_rx_stream(stream_args);
715
716 /* Number of samples per over-the-wire packet */
717 tx_spp = tx_stream->get_max_num_samps();
718 rx_spp = rx_stream->get_max_num_samps();
719
kurtis.heimerl965e7572011-11-26 03:16:54 +0000720 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400721 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400722 for (size_t i = 0; i < rx_buffers.size(); i++)
723 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000724
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100725 // Initialize and shadow gain values
kurtis.heimerl02d04052011-11-26 03:17:10 +0000726 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000727
kurtis.heimerl965e7572011-11-26 03:16:54 +0000728 // Print configuration
Harald Welte5cc88582018-08-17 19:55:38 +0200729 LOGC(DDEV, INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000730
Tom Tsou76764272016-06-24 14:25:39 -0700731 if (iface == MULTI_ARFCN)
732 return MULTI_ARFCN;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500733
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400734 switch (dev_type) {
735 case B100:
736 return RESAMP_64M;
737 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800738 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400739 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500740 case B200:
741 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100742 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800743 case E3XX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800744 case LIMESDR:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500745 default:
746 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400747 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400748
749 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000750}
751
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000752bool uhd_device::flush_recv(size_t num_pkts)
753{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000754 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000755 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800756 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000757
Thomas Tsou18d3b832014-02-13 14:55:23 -0500758 std::vector<std::vector<short> >
759 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000760
Thomas Tsou18d3b832014-02-13 14:55:23 -0500761 std::vector<short *> pkt_ptrs;
762 for (size_t i = 0; i < pkt_bufs.size(); i++)
763 pkt_ptrs.push_back(&pkt_bufs[i].front());
764
765 ts_initial = 0;
766 while (!ts_initial || (num_pkts-- > 0)) {
767 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400768 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000769 if (!num_smpls) {
770 switch (md.error_code) {
771 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Harald Welte5cc88582018-08-17 19:55:38 +0200772 LOGC(DDEV, ALERT) << "Device timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800773 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000774 default:
775 continue;
776 }
777 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500778
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700779 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000780 }
781
Harald Welte5cc88582018-08-17 19:55:38 +0200782 LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
Thomas Tsou18d3b832014-02-13 14:55:23 -0500783
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000784 return true;
785}
786
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800787bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000788{
Thomas Tsouc2839162014-03-27 13:52:58 -0400789 /* Allow 100 ms delay to align multi-channel streams */
790 double delay = 0.1;
791
kurtis.heimerl68292102011-11-26 03:17:28 +0000792 aligned = false;
793
Thomas Tsouc2839162014-03-27 13:52:58 -0400794 uhd::time_spec_t current = usrp_dev->get_time_now();
795
Thomas Tsou204a9f12013-10-29 18:34:16 -0400796 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400797 cmd.stream_now = false;
798 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400799
kurtis.heimerl68292102011-11-26 03:17:28 +0000800 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500801
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800802 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000803}
804
kurtis.heimerl965e7572011-11-26 03:16:54 +0000805bool uhd_device::start()
806{
Harald Welte5cc88582018-08-17 19:55:38 +0200807 LOGC(DDEV, INFO) << "Starting USRP...";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000808
809 if (started) {
Harald Welte5cc88582018-08-17 19:55:38 +0200810 LOGC(DDEV, ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000811 return false;
812 }
813
Tom Tsou72bf7622017-03-07 14:16:46 -0800814#ifndef USE_UHD_3_11
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400815 // Register msg handler
816 uhd::msg::register_handler(&uhd_msg_handler);
Tom Tsou72bf7622017-03-07 14:16:46 -0800817#endif
kurtis.heimerl965e7572011-11-26 03:16:54 +0000818 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800819 async_event_thrd = new Thread();
820 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000821
822 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800823 if (!restart())
824 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000825
kurtis.heimerl965e7572011-11-26 03:16:54 +0000826 // Display usrp time
827 double time_now = usrp_dev->get_time_now().get_real_secs();
Harald Welte5cc88582018-08-17 19:55:38 +0200828 LOGC(DDEV, INFO) << "The current time is " << time_now << " seconds";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000829
830 started = true;
831 return true;
832}
833
834bool uhd_device::stop()
835{
Thomas Tsou2c791102013-11-15 23:00:28 -0500836 if (!started)
837 return false;
838
839 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000840 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
841
842 usrp_dev->issue_stream_cmd(stream_cmd);
843
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800844 async_event_thrd->cancel();
845 async_event_thrd->join();
846 delete async_event_thrd;
847
kurtis.heimerl965e7572011-11-26 03:16:54 +0000848 started = false;
849 return true;
850}
851
Thomas Tsou7553aa92013-11-08 12:50:03 -0500852void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000853{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500854 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000855 return;
856}
857
kurtis.heimerld4be0742011-11-26 03:17:46 +0000858int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000859{
kurtis.heimerld4be0742011-11-26 03:17:46 +0000860 if (!num_smpls) {
Harald Welte5cc88582018-08-17 19:55:38 +0200861 LOGC(DDEV, ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000862
863 switch (md.error_code) {
864 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Harald Welte5cc88582018-08-17 19:55:38 +0200865 LOGC(DDEV, ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800866 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000867 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
868 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
869 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
870 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
871 default:
872 return ERROR_UNHANDLED;
873 }
874 }
875
kurtis.heimerl965e7572011-11-26 03:16:54 +0000876 // Missing timestamp
877 if (!md.has_time_spec) {
Harald Welte5cc88582018-08-17 19:55:38 +0200878 LOGC(DDEV, ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000879 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000880 }
881
882 // Monotonicity check
Tom Tsoua93f7892017-03-07 17:54:06 -0800883 if (md.time_spec < prev_ts) {
Harald Welte5cc88582018-08-17 19:55:38 +0200884 LOGC(DDEV, ALERT) << "UHD: Loss of monotonic time";
885 LOGC(DDEV, ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
ttsou724eb362012-03-13 02:20:01 +0000886 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000887 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000888 }
889
Tom Tsoua93f7892017-03-07 17:54:06 -0800890 // Workaround for UHD tick rounding bug
891 TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
892 if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
893 md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
894
895 prev_ts = md.time_spec;
896
kurtis.heimerl965e7572011-11-26 03:16:54 +0000897 return 0;
898}
899
Thomas Tsou204a9f12013-10-29 18:34:16 -0400900int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
901 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000902{
903 ssize_t rc;
904 uhd::time_spec_t ts;
905 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000906
Thomas Tsou204a9f12013-10-29 18:34:16 -0400907 if (bufs.size() != chans) {
Harald Welte5cc88582018-08-17 19:55:38 +0200908 LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400909 return -1;
910 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000911
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000912 *overrun = false;
913 *underrun = false;
914
kurtis.heimerl965e7572011-11-26 03:16:54 +0000915 // Shift read time with respect to transmit clock
916 timestamp += ts_offset;
917
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700918 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
Harald Welte5cc88582018-08-17 19:55:38 +0200919 LOGC(DDEV, DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000920
921 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400922 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000923 if (rc < 0) {
Harald Welte5cc88582018-08-17 19:55:38 +0200924 LOGC(DDEV, ERR) << rx_buffers[0]->str_code(rc);
925 LOGC(DDEV, ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000926 return 0;
927 }
928
Thomas Tsou204a9f12013-10-29 18:34:16 -0400929 // Create vector buffer
930 std::vector<std::vector<short> >
931 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
932
933 std::vector<short *> pkt_ptrs;
934 for (size_t i = 0; i < pkt_bufs.size(); i++)
935 pkt_ptrs.push_back(&pkt_bufs[i].front());
936
kurtis.heimerl965e7572011-11-26 03:16:54 +0000937 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400938 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800939 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400940 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
941 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800942 thread_enable_cancel(true);
943
kurtis.heimerl965e7572011-11-26 03:16:54 +0000944 rx_pkt_cnt++;
945
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100946 // Check for errors
kurtis.heimerld4be0742011-11-26 03:17:46 +0000947 rc = check_rx_md_err(metadata, num_smpls);
948 switch (rc) {
949 case ERROR_UNRECOVERABLE:
Harald Welte5cc88582018-08-17 19:55:38 +0200950 LOGC(DDEV, ALERT) << "UHD: Version " << uhd::get_version_string();
951 LOGC(DDEV, ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000952 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800953 case ERROR_TIMEOUT:
954 // Assume stopping condition
955 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000956 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -0400957 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000958 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000959 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000960 }
961
kurtis.heimerl965e7572011-11-26 03:16:54 +0000962 ts = metadata.time_spec;
Harald Welte5cc88582018-08-17 19:55:38 +0200963 LOGC(DDEV, DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000964
Thomas Tsou204a9f12013-10-29 18:34:16 -0400965 for (size_t i = 0; i < rx_buffers.size(); i++) {
966 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
967 num_smpls,
968 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000969
Thomas Tsou204a9f12013-10-29 18:34:16 -0400970 // Continue on local overrun, exit on other errors
971 if ((rc < 0)) {
Harald Welte5cc88582018-08-17 19:55:38 +0200972 LOGC(DDEV, ERR) << rx_buffers[i]->str_code(rc);
973 LOGC(DDEV, ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400974 if (rc != smpl_buf::ERROR_OVERFLOW)
975 return 0;
976 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000977 }
978 }
979
980 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -0400981 for (size_t i = 0; i < rx_buffers.size(); i++) {
982 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
983 if ((rc < 0) || (rc != len)) {
Harald Welte5cc88582018-08-17 19:55:38 +0200984 LOGC(DDEV, ERR) << rx_buffers[i]->str_code(rc);
985 LOGC(DDEV, ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400986 return 0;
987 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000988 }
989
990 return len;
991}
992
Thomas Tsou204a9f12013-10-29 18:34:16 -0400993int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
994 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000995{
996 uhd::tx_metadata_t metadata;
997 metadata.has_time_spec = true;
998 metadata.start_of_burst = false;
999 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001000 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001001
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001002 *underrun = false;
1003
kurtis.heimerl965e7572011-11-26 03:16:54 +00001004 // No control packets
1005 if (isControl) {
Harald Welte5cc88582018-08-17 19:55:38 +02001006 LOGC(DDEV, ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001007 return 0;
1008 }
1009
Thomas Tsou204a9f12013-10-29 18:34:16 -04001010 if (bufs.size() != chans) {
Harald Welte5cc88582018-08-17 19:55:38 +02001011 LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
Thomas Tsou204a9f12013-10-29 18:34:16 -04001012 return -1;
1013 }
1014
kurtis.heimerl965e7572011-11-26 03:16:54 +00001015 // Drop a fixed number of packets (magic value)
1016 if (!aligned) {
1017 drop_cnt++;
1018
1019 if (drop_cnt == 1) {
Harald Welte5cc88582018-08-17 19:55:38 +02001020 LOGC(DDEV, DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001021 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001022 metadata.end_of_burst = true;
1023 } else if (drop_cnt < 30) {
Harald Welte5cc88582018-08-17 19:55:38 +02001024 LOGC(DDEV, DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001025 return len;
1026 } else {
Harald Welte5cc88582018-08-17 19:55:38 +02001027 LOGC(DDEV, DEBUG) << "Aligning transmitter: start burst";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001028 metadata.start_of_burst = true;
1029 aligned = true;
1030 drop_cnt = 0;
1031 }
1032 }
1033
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001034 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001035 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001036 thread_enable_cancel(true);
1037
ttsoub371ed52012-01-09 18:11:34 +00001038 if (num_smpls != (unsigned) len) {
Harald Welte5cc88582018-08-17 19:55:38 +02001039 LOGC(DDEV, ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001040 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001041
1042 return num_smpls;
1043}
1044
1045bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1046{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001047 return true;
1048}
1049
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001050uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1051{
1052 double rf_spread, rf_freq;
1053 std::vector<double> freqs;
1054 uhd::tune_request_t treq(freq);
1055
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001056 if (dev_type == UMTRX) {
Harald Welte61707e82018-06-13 23:21:57 +02001057 if (lo_offset != 0.0)
1058 return uhd::tune_request_t(freq, lo_offset);
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001059
1060 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1061 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1062 // only distort the signal (because cordic is not ideal).
1063 treq.target_freq = freq;
1064 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1065 treq.rf_freq = freq;
1066 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1067 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001068 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001069 } else if (chans == 1) {
Harald Welte61707e82018-06-13 23:21:57 +02001070 if (lo_offset == 0.0)
Thomas Tsou8e17df72014-03-06 14:16:11 -05001071 return treq;
1072
Harald Welte61707e82018-06-13 23:21:57 +02001073 return uhd::tune_request_t(freq, lo_offset);
Thomas Tsou8e17df72014-03-06 14:16:11 -05001074 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Harald Welte5cc88582018-08-17 19:55:38 +02001075 LOGC(DDEV, ALERT) << chans << " channels unsupported";
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001076 return treq;
1077 }
1078
1079 if (tx)
1080 freqs = tx_freqs;
1081 else
1082 freqs = rx_freqs;
1083
1084 /* Tune directly if other channel isn't tuned */
1085 if (freqs[!chan] < 10.0)
1086 return treq;
1087
1088 /* Find center frequency between channels */
1089 rf_spread = fabs(freqs[!chan] - freq);
Tom Tsouf6115692017-06-26 10:13:25 -07001090 if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
Harald Welte5cc88582018-08-17 19:55:38 +02001091 LOGC(DDEV, ALERT) << rf_spread << "Hz tuning spread not supported\n";
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001092 return treq;
1093 }
1094
1095 rf_freq = (freqs[!chan] + freq) / 2.0f;
1096
1097 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1098 treq.target_freq = freq;
1099 treq.rf_freq = rf_freq;
1100
1101 return treq;
1102}
1103
1104bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1105{
1106 std::vector<double> freqs;
1107 uhd::tune_result_t tres;
1108 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1109
1110 if (tx) {
1111 tres = usrp_dev->set_tx_freq(treq, chan);
1112 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1113 } else {
1114 tres = usrp_dev->set_rx_freq(treq, chan);
1115 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1116 }
Harald Welte5cc88582018-08-17 19:55:38 +02001117 LOGC(DDEV, INFO) << "\n" << tres.to_pp_string() << std::endl;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001118
Thomas Tsou8e17df72014-03-06 14:16:11 -05001119 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1120 return true;
1121
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001122 /* Manual RF policy means we intentionally tuned with a baseband
1123 * offset for dual-channel purposes. Now retune the other channel
1124 * with the opposite corresponding frequency offset
1125 */
1126 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1127 if (tx) {
1128 treq = select_freq(tx_freqs[!chan], !chan, true);
1129 tres = usrp_dev->set_tx_freq(treq, !chan);
1130 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1131 } else {
1132 treq = select_freq(rx_freqs[!chan], !chan, false);
1133 tres = usrp_dev->set_rx_freq(treq, !chan);
1134 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1135
1136 }
Harald Welte5cc88582018-08-17 19:55:38 +02001137 LOGC(DDEV, INFO) << "\n" << tres.to_pp_string() << std::endl;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001138 }
1139
1140 return true;
1141}
1142
Thomas Tsou204a9f12013-10-29 18:34:16 -04001143bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001144{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001145 if (chan >= tx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001146 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001147 return false;
1148 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001149 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001150
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001151 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001152}
1153
Thomas Tsou204a9f12013-10-29 18:34:16 -04001154bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001155{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001156 if (chan >= rx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001157 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001158 return false;
1159 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001160 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001161
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001162 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001163}
1164
Thomas Tsou204a9f12013-10-29 18:34:16 -04001165double uhd_device::getTxFreq(size_t chan)
1166{
1167 if (chan >= tx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001168 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001169 return 0.0;
1170 }
1171
1172 return tx_freqs[chan];
1173}
1174
1175double uhd_device::getRxFreq(size_t chan)
1176{
1177 if (chan >= rx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001178 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001179 return 0.0;
1180 }
1181
1182 return rx_freqs[chan];
1183}
1184
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001185bool uhd_device::setRxAntenna(const std::string &ant, size_t chan)
1186{
1187 std::vector<std::string> avail;
1188 if (chan >= rx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001189 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001190 return false;
1191 }
1192
1193 avail = usrp_dev->get_rx_antennas(chan);
1194 if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001195 LOGC(DDEV, ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan;
1196 LOGC(DDEV, INFO) << "Available Rx antennas: ";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001197 for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
Harald Welte5cc88582018-08-17 19:55:38 +02001198 LOGC(DDEV, INFO) << "- '" << *i << "'";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001199 return false;
1200 }
1201 usrp_dev->set_rx_antenna(ant, chan);
1202 rx_paths[chan] = usrp_dev->get_rx_antenna(chan);
1203
1204 if (ant != rx_paths[chan]) {
Harald Welte5cc88582018-08-17 19:55:38 +02001205 LOGC(DDEV, ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan];
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001206 return false;
1207 }
1208
1209 return true;
1210}
1211
1212std::string uhd_device::getRxAntenna(size_t chan)
1213{
1214 if (chan >= rx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001215 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001216 return "";
1217 }
1218 return usrp_dev->get_rx_antenna(chan);
1219}
1220
1221bool uhd_device::setTxAntenna(const std::string &ant, size_t chan)
1222{
1223 std::vector<std::string> avail;
1224 if (chan >= tx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001225 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001226 return false;
1227 }
1228
1229 avail = usrp_dev->get_tx_antennas(chan);
1230 if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001231 LOGC(DDEV, ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan;
1232 LOGC(DDEV, INFO) << "Available Tx antennas: ";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001233 for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
Harald Welte5cc88582018-08-17 19:55:38 +02001234 LOGC(DDEV, INFO) << "- '" << *i << "'";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001235 return false;
1236 }
1237 usrp_dev->set_tx_antenna(ant, chan);
1238 tx_paths[chan] = usrp_dev->get_tx_antenna(chan);
1239
1240 if (ant != tx_paths[chan]) {
Harald Welte5cc88582018-08-17 19:55:38 +02001241 LOGC(DDEV, ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan];
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001242 return false;
1243 }
1244
1245 return true;
1246}
1247
1248std::string uhd_device::getTxAntenna(size_t chan)
1249{
1250 if (chan >= tx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001251 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001252 return "";
1253 }
1254 return usrp_dev->get_tx_antenna(chan);
1255}
1256
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +02001257bool uhd_device::requiresRadioAlign()
1258{
1259 return false;
1260}
1261
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001262GSM::Time uhd_device::minLatency() {
1263 /* Empirical data from a handful of
1264 relatively recent machines shows that the B100 will underrun when
1265 the transmit threshold is reduced to a time of 6 and a half frames,
1266 so we set a minimum 7 frame threshold. */
1267 return GSM::Time(6,7);
1268}
1269
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001270/*
1271 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1272 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1273 * combination.
1274 */
1275TIMESTAMP uhd_device::initialWriteTimestamp()
1276{
Tom Tsou76764272016-06-24 14:25:39 -07001277 if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001278 return ts_initial;
1279 else
1280 return ts_initial * tx_sps;
1281}
1282
1283TIMESTAMP uhd_device::initialReadTimestamp()
1284{
1285 return ts_initial;
1286}
1287
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001288double uhd_device::fullScaleInputValue()
1289{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001290 if (dev_type == LIMESDR)
ignasj28d80812017-06-13 23:37:46 +03001291 return (double) SHRT_MAX * LIMESDR_TX_AMPL;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001292 if (dev_type == UMTRX)
1293 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1294 else
1295 return (double) SHRT_MAX * USRP_TX_AMPL;
1296}
1297
1298double uhd_device::fullScaleOutputValue()
1299{
1300 return (double) SHRT_MAX;
1301}
1302
kurtis.heimerl965e7572011-11-26 03:16:54 +00001303bool uhd_device::recv_async_msg()
1304{
ttsou60dc4c92012-08-08 23:30:23 +00001305 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001306
1307 thread_enable_cancel(false);
1308 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1309 thread_enable_cancel(true);
1310 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001311 return false;
1312
1313 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001314 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001315 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001316
1317 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1318 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
Harald Welte5cc88582018-08-17 19:55:38 +02001319 LOGC(DDEV, ERR) << str_code(md);
ttsou60dc4c92012-08-08 23:30:23 +00001320 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001321 }
1322
1323 return true;
1324}
1325
1326std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1327{
1328 std::ostringstream ost("UHD: ");
1329
1330 switch (metadata.error_code) {
1331 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1332 ost << "No error";
1333 break;
1334 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1335 ost << "No packet received, implementation timed-out";
1336 break;
1337 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1338 ost << "A stream command was issued in the past";
1339 break;
1340 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1341 ost << "Expected another stream command";
1342 break;
1343 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1344 ost << "An internal receive buffer has filled";
1345 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001346 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1347 ost << "Multi-channel alignment failed";
1348 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001349 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1350 ost << "The packet could not be parsed";
1351 break;
1352 default:
1353 ost << "Unknown error " << metadata.error_code;
1354 }
1355
1356 if (metadata.has_time_spec)
1357 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1358
1359 return ost.str();
1360}
1361
1362std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1363{
1364 std::ostringstream ost("UHD: ");
1365
1366 switch (metadata.event_code) {
1367 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1368 ost << "A packet was successfully transmitted";
1369 break;
1370 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1371 ost << "An internal send buffer has emptied";
1372 break;
1373 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1374 ost << "Packet loss between host and device";
1375 break;
1376 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1377 ost << "Packet time was too late or too early";
1378 break;
1379 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1380 ost << "Underflow occurred inside a packet";
1381 break;
1382 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1383 ost << "Packet loss within a burst";
1384 break;
1385 default:
1386 ost << "Unknown error " << metadata.event_code;
1387 }
1388
1389 if (metadata.has_time_spec)
1390 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1391
1392 return ost.str();
1393}
1394
1395smpl_buf::smpl_buf(size_t len, double rate)
1396 : buf_len(len), clk_rt(rate),
1397 time_start(0), time_end(0), data_start(0), data_end(0)
1398{
1399 data = new uint32_t[len];
1400}
1401
1402smpl_buf::~smpl_buf()
1403{
1404 delete[] data;
1405}
1406
1407ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1408{
1409 if (timestamp < time_start)
1410 return ERROR_TIMESTAMP;
1411 else if (timestamp >= time_end)
1412 return 0;
1413 else
1414 return time_end - timestamp;
1415}
1416
1417ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1418{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001419 return avail_smpls(timespec.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001420}
1421
1422ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1423{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001424 int type_sz = 2 * sizeof(short);
1425
kurtis.heimerl965e7572011-11-26 03:16:54 +00001426 // Check for valid read
1427 if (timestamp < time_start)
1428 return ERROR_TIMESTAMP;
1429 if (timestamp >= time_end)
1430 return 0;
1431 if (len >= buf_len)
1432 return ERROR_READ;
1433
1434 // How many samples should be copied
1435 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001436 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001437 num_smpls = len;
1438
1439 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001440 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001441
1442 // Read it
1443 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001444 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001445 memcpy(buf, data + read_start, numBytes);
1446 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001447 size_t first_cp = (buf_len - read_start) * type_sz;
1448 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001449
1450 memcpy(buf, data + read_start, first_cp);
1451 memcpy((char*) buf + first_cp, data, second_cp);
1452 }
1453
1454 data_start = (read_start + len) % buf_len;
1455 time_start = timestamp + len;
1456
1457 if (time_start > time_end)
1458 return ERROR_READ;
1459 else
1460 return num_smpls;
1461}
1462
1463ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1464{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001465 return read(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001466}
1467
1468ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1469{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001470 int type_sz = 2 * sizeof(short);
1471
kurtis.heimerl965e7572011-11-26 03:16:54 +00001472 // Check for valid write
1473 if ((len == 0) || (len >= buf_len))
1474 return ERROR_WRITE;
1475 if ((timestamp + len) <= time_end)
1476 return ERROR_TIMESTAMP;
1477
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001478 if (timestamp < time_end) {
Harald Welte5cc88582018-08-17 19:55:38 +02001479 LOGC(DDEV, ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001480 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
Harald Welte5cc88582018-08-17 19:55:38 +02001481 LOGC(DDEV, 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 -04001482 // Do not return error here, because it's a rounding error and is not fatal
1483 }
1484 if (timestamp > time_end && time_end != 0) {
Harald Welte5cc88582018-08-17 19:55:38 +02001485 LOGC(DDEV, ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001486 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
Harald Welte5cc88582018-08-17 19:55:38 +02001487 LOGC(DDEV, 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 -04001488 // Do not return error here, because it's a rounding error and is not fatal
1489 }
1490
kurtis.heimerl965e7572011-11-26 03:16:54 +00001491 // Starting index
1492 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1493
1494 // Write it
1495 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001496 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001497 memcpy(data + write_start, buf, numBytes);
1498 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001499 size_t first_cp = (buf_len - write_start) * type_sz;
1500 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001501
1502 memcpy(data + write_start, buf, first_cp);
1503 memcpy(data, (char*) buf + first_cp, second_cp);
1504 }
1505
1506 data_end = (write_start + len) % buf_len;
1507 time_end = timestamp + len;
1508
Thomas Tsou18d3b832014-02-13 14:55:23 -05001509 if (!data_start)
1510 data_start = write_start;
1511
kurtis.heimerl965e7572011-11-26 03:16:54 +00001512 if (((write_start + len) > buf_len) && (data_end > data_start))
1513 return ERROR_OVERFLOW;
1514 else if (time_end <= time_start)
1515 return ERROR_WRITE;
1516 else
1517 return len;
1518}
1519
1520ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1521{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001522 return write(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001523}
1524
Tom Tsou1ae25562014-12-05 12:56:34 -08001525std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001526{
1527 std::ostringstream ost("Sample buffer: ");
1528
Tom Tsou1ae25562014-12-05 12:56:34 -08001529 ost << "timestamp = " << ts;
1530 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001531 ost << ", time_start = " << time_start;
1532 ost << ", time_end = " << time_end;
1533 ost << ", data_start = " << data_start;
1534 ost << ", data_end = " << data_end;
1535
1536 return ost.str();
1537}
1538
1539std::string smpl_buf::str_code(ssize_t code)
1540{
1541 switch (code) {
1542 case ERROR_TIMESTAMP:
1543 return "Sample buffer: Requested timestamp is not valid";
1544 case ERROR_READ:
1545 return "Sample buffer: Read error";
1546 case ERROR_WRITE:
1547 return "Sample buffer: Write error";
1548 case ERROR_OVERFLOW:
1549 return "Sample buffer: Overrun";
1550 default:
1551 return "Sample buffer: Unknown error";
1552 }
1553}
1554
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001555RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
Harald Welte61707e82018-06-13 23:21:57 +02001556 InterfaceType iface, size_t chans, double lo_offset,
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001557 const std::vector<std::string>& tx_paths,
1558 const std::vector<std::string>& rx_paths)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001559{
Harald Welte61707e82018-06-13 23:21:57 +02001560 return new uhd_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001561}