blob: a0052c8927dd5aa867b083095ffd3f1f2550b020 [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
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +0200258 bool requiresRadioAlign();
259
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +0200260 GSM::Time minLatency();
261
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400262 inline double getSampleRate() { return tx_rate; }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000263 inline double numberRead() { return rx_pkt_cnt; }
264 inline double numberWritten() { return 0; }
265
266 /** Receive and process asynchronous message
267 @return true if message received or false on timeout or error
268 */
269 bool recv_async_msg();
270
kurtis.heimerld4be0742011-11-26 03:17:46 +0000271 enum err_code {
272 ERROR_TIMING = -1,
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800273 ERROR_TIMEOUT = -2,
274 ERROR_UNRECOVERABLE = -3,
275 ERROR_UNHANDLED = -4,
kurtis.heimerld4be0742011-11-26 03:17:46 +0000276 };
277
kurtis.heimerl965e7572011-11-26 03:16:54 +0000278private:
kurtis.heimerl856bbbe2011-12-15 00:50:16 +0000279 uhd::usrp::multi_usrp::sptr usrp_dev;
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400280 uhd::tx_streamer::sptr tx_stream;
281 uhd::rx_streamer::sptr rx_stream;
Thomas Tsou02d88d12013-04-05 15:36:30 -0400282 enum TxWindowType tx_window;
283 enum uhd_dev_type dev_type;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000284
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800285 size_t tx_sps, rx_sps, chans;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400286 double tx_rate, rx_rate;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000287
Thomas Tsou204a9f12013-10-29 18:34:16 -0400288 double tx_gain_min, tx_gain_max;
289 double rx_gain_min, rx_gain_max;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500290 double offset;
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;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100294 std::vector<std::string> tx_paths, rx_paths;
kurtis.heimerl02d04052011-11-26 03:17:10 +0000295 size_t tx_spp, rx_spp;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000296
297 bool started;
298 bool aligned;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000299
300 size_t rx_pkt_cnt;
301 size_t drop_cnt;
302 uhd::time_spec_t prev_ts;
303
Thomas Tsou18d3b832014-02-13 14:55:23 -0500304 TIMESTAMP ts_initial, ts_offset;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400305 std::vector<smpl_buf *> rx_buffers;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000306
kurtis.heimerl02d04052011-11-26 03:17:10 +0000307 void init_gains();
Tom Tsou980525c2017-06-09 15:37:19 -0700308 void set_channels(bool swap);
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700309 void set_rates();
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100310 bool set_antennas();
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000311 bool parse_dev_type();
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000312 bool flush_recv(size_t num_pkts);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000313 int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
kurtis.heimerl24481de2011-11-26 03:17:18 +0000314
kurtis.heimerl965e7572011-11-26 03:16:54 +0000315 std::string str_code(uhd::rx_metadata_t metadata);
316 std::string str_code(uhd::async_metadata_t metadata);
317
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500318 uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
319 bool set_freq(double freq, size_t chan, bool tx);
320
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800321 Thread *async_event_thrd;
Tom Tsou05c6feb2016-06-22 16:09:44 -0700322 InterfaceType iface;
Tom Tsou93b7f372014-12-15 20:23:33 -0800323 Mutex tune_lock;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000324};
325
326void *async_event_loop(uhd_device *dev)
327{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500328 dev->setPriority(0.43);
329
kurtis.heimerl965e7572011-11-26 03:16:54 +0000330 while (1) {
331 dev->recv_async_msg();
332 pthread_testcancel();
333 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500334
335 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000336}
337
Tom Tsou72bf7622017-03-07 14:16:46 -0800338#ifndef USE_UHD_3_11
339/*
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000340 Catch and drop underrun 'U' and overrun 'O' messages from stdout
341 since we already report using the logging facility. Direct
342 everything else appropriately.
343 */
344void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
345{
346 switch (type) {
347 case uhd::msg::status:
348 LOG(INFO) << msg;
349 break;
350 case uhd::msg::warning:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000351 LOG(WARNING) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000352 break;
353 case uhd::msg::error:
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000354 LOG(ERR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000355 break;
356 case uhd::msg::fastpath:
357 break;
358 }
359}
Tom Tsou72bf7622017-03-07 14:16:46 -0800360#endif
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000361
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800362static void thread_enable_cancel(bool cancel)
363{
364 cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
365 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
366}
367
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800368uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100369 InterfaceType iface, size_t chans, double offset,
370 const std::vector<std::string>& tx_paths,
371 const std::vector<std::string>& rx_paths)
Thomas Tsou204a9f12013-10-29 18:34:16 -0400372 : tx_gain_min(0.0), tx_gain_max(0.0),
373 rx_gain_min(0.0), rx_gain_max(0.0),
374 tx_spp(0), rx_spp(0),
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000375 started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
Pau Espin Pedrolb7095c72018-02-09 16:19:31 +0100376 prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000377{
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800378 this->tx_sps = tx_sps;
379 this->rx_sps = rx_sps;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400380 this->chans = chans;
Thomas Tsou8e17df72014-03-06 14:16:11 -0500381 this->offset = offset;
Tom Tsou05c6feb2016-06-22 16:09:44 -0700382 this->iface = iface;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100383 this->tx_paths = tx_paths;
384 this->rx_paths = rx_paths;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000385}
386
387uhd_device::~uhd_device()
388{
389 stop();
390
Thomas Tsou204a9f12013-10-29 18:34:16 -0400391 for (size_t i = 0; i < rx_buffers.size(); i++)
392 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000393}
394
kurtis.heimerl24481de2011-11-26 03:17:18 +0000395void uhd_device::init_gains()
396{
397 uhd::gain_range_t range;
398
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400399 if (dev_type == UMTRX) {
400 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
401 if (gain_stages[0] == "VGA") {
402 LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
403 }
404 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
405 range = usrp_dev->get_tx_gain_range();
406 tx_gain_min = range.start();
407 tx_gain_max = range.stop();
408 } else {
409 range = usrp_dev->get_tx_gain_range("VGA2");
410 tx_gain_min = UMTRX_VGA1_DEF + range.start();
411 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
412 }
413 } else {
414 range = usrp_dev->get_tx_gain_range();
415 tx_gain_min = range.start();
416 tx_gain_max = range.stop();
417 }
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400418 LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000419
420 range = usrp_dev->get_rx_gain_range();
421 rx_gain_min = range.start();
422 rx_gain_max = range.stop();
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400423 LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000424
Thomas Tsou204a9f12013-10-29 18:34:16 -0400425 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400426 double gain = (tx_gain_min + tx_gain_max) / 2;
427 LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
428 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400429 tx_gains[i] = usrp_dev->get_tx_gain(i);
430 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000431
Thomas Tsou204a9f12013-10-29 18:34:16 -0400432 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400433 double gain = (rx_gain_min + rx_gain_max) / 2;
434 LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
435 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400436 rx_gains[i] = usrp_dev->get_rx_gain(i);
437 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000438
439 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400440
kurtis.heimerl24481de2011-11-26 03:17:18 +0000441}
442
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700443void uhd_device::set_rates()
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700444{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700445 dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
446 if (desc.mcr != 0.0)
447 usrp_dev->set_master_clock_rate(desc.mcr);
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700448
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700449 tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate;
450 rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate;
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700451
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700452 usrp_dev->set_tx_rate(tx_rate);
453 usrp_dev->set_rx_rate(rx_rate);
454 tx_rate = usrp_dev->get_tx_rate();
455 rx_rate = usrp_dev->get_rx_rate();
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700456
Tom Tsou1b6ab7d2017-06-15 15:31:08 -0700457 ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700458 LOG(INFO) << "Rates configured for " << desc.str;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000459}
460
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100461bool uhd_device::set_antennas()
462{
463 unsigned int i;
464
465 for (i = 0; i < tx_paths.size(); i++) {
466 if (tx_paths[i] == "")
467 continue;
468 LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i];
469 if (!setTxAntenna(tx_paths[i], i)) {
470 LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i];
471 return false;
472 }
473 }
474
475 for (i = 0; i < rx_paths.size(); i++) {
476 if (rx_paths[i] == "")
477 continue;
478 LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i];
479 if (!setRxAntenna(rx_paths[i], i)) {
480 LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i];
481 return false;
482 }
483 }
484 LOG(INFO) << "Antennas configured successfully";
485 return true;
486}
487
Thomas Tsou204a9f12013-10-29 18:34:16 -0400488double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000489{
Tom Tsou76764272016-06-24 14:25:39 -0700490 if (iface == MULTI_ARFCN)
491 chan = 0;
492
Thomas Tsou204a9f12013-10-29 18:34:16 -0400493 if (chan >= tx_gains.size()) {
494 LOG(ALERT) << "Requested non-existent channel" << chan;
495 return 0.0f;
496 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000497
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400498 if (dev_type == UMTRX) {
499 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
500 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
501 usrp_dev->set_tx_gain(db, chan);
502 } else {
503 // New UHD versions support split configuration of
504 // Tx gain stages. We utilize this to set the gain
505 // configuration, optimal for the Tx signal quality.
506 // From our measurements, VGA1 must be 18dB plus-minus
507 // one and VGA2 is the best when 23dB or lower.
508 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
509 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
510 }
511 } else {
512 usrp_dev->set_tx_gain(db, chan);
513 }
514
Thomas Tsou204a9f12013-10-29 18:34:16 -0400515 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000516
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400517 LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400518
519 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000520}
521
Thomas Tsou204a9f12013-10-29 18:34:16 -0400522double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000523{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400524 if (chan >= rx_gains.size()) {
525 LOG(ALERT) << "Requested non-existent channel " << chan;
526 return 0.0f;
527 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000528
Thomas Tsou204a9f12013-10-29 18:34:16 -0400529 usrp_dev->set_rx_gain(db, chan);
530 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000531
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400532 LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400533
534 return rx_gains[chan];
535}
536
537double uhd_device::getRxGain(size_t chan)
538{
Tom Tsou76764272016-06-24 14:25:39 -0700539 if (iface == MULTI_ARFCN)
540 chan = 0;
541
Thomas Tsou204a9f12013-10-29 18:34:16 -0400542 if (chan >= rx_gains.size()) {
543 LOG(ALERT) << "Requested non-existent channel " << chan;
544 return 0.0f;
545 }
546
547 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000548}
549
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000550/*
551 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400552 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000553 deal with the transport latency. Reject the USRP1 because UHD doesn't
554 support timestamped samples with it.
555 */
556bool uhd_device::parse_dev_type()
557{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700558 uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree();
559 std::string devString = prop_tree->access<std::string>("/name").get();
560 std::string mboardString = usrp_dev->get_mboard_name();
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000561
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700562 const std::map<std::string, std::pair<uhd_dev_type, TxWindowType>> devStringMap {
563 { "B100", { B100, TX_WINDOW_USRP1 } },
564 { "B200", { B200, TX_WINDOW_USRP1 } },
565 { "B200mini", { B200, TX_WINDOW_USRP1 } },
Piotr Krysikaa60dda2017-12-04 00:29:16 +0700566 { "B205mini", { B200, TX_WINDOW_USRP1 } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700567 { "B210", { B210, TX_WINDOW_USRP1 } },
568 { "E100", { E1XX, TX_WINDOW_FIXED } },
569 { "E110", { E1XX, TX_WINDOW_FIXED } },
570 { "E310", { E3XX, TX_WINDOW_FIXED } },
571 { "E3XX", { E3XX, TX_WINDOW_FIXED } },
572 { "X300", { X3XX, TX_WINDOW_FIXED } },
573 { "X310", { X3XX, TX_WINDOW_FIXED } },
Tom Tsou988a4642017-06-15 09:16:53 -0700574 { "USRP2", { USRP2, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700575 { "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
ignasj87ed77b2017-06-13 23:37:46 +0300576 { "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700577 };
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000578
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700579 // Compare UHD motherboard and device strings */
Tom Tsou988a4642017-06-15 09:16:53 -0700580 auto mapIter = devStringMap.begin();
581 while (mapIter != devStringMap.end()) {
582 if (devString.find(mapIter->first) != std::string::npos ||
583 mboardString.find(mapIter->first) != std::string::npos) {
584 dev_type = std::get<0>(mapIter->second);
585 tx_window = std::get<1>(mapIter->second);
586 return true;
587 }
588 mapIter++;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000589 }
590
Tom Tsou988a4642017-06-15 09:16:53 -0700591 LOG(ALERT) << "Unsupported device " << devString;
592 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000593}
594
Tom Tsou3b093bb2016-05-03 19:04:15 -0700595/*
596 * Check for UHD version > 3.9.0 for E3XX support
597 */
598static bool uhd_e3xx_version_chk()
599{
600 std::string ver = uhd::get_version_string();
601 std::string major_str(ver.begin(), ver.begin() + 3);
602 std::string minor_str(ver.begin() + 4, ver.begin() + 7);
603
604 int major_val = atoi(major_str.c_str());
605 int minor_val = atoi(minor_str.c_str());
606
607 if (major_val < 3)
608 return false;
609 if (minor_val < 9)
610 return false;
611
612 return true;
613}
614
Tom Tsou980525c2017-06-09 15:37:19 -0700615void uhd_device::set_channels(bool swap)
616{
617 if (iface == MULTI_ARFCN) {
618 if (dev_type != B200 && dev_type != B210)
619 throw std::invalid_argument("Device does not support MCBTS");
620 dev_type = B2XX_MCBTS;
621 chans = 1;
622 }
623
Tom Tsouf6115692017-06-26 10:13:25 -0700624 if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
Tom Tsou980525c2017-06-09 15:37:19 -0700625 throw std::invalid_argument("Device does not support number of requested channels");
626
627 std::string subdev_string;
628 switch (dev_type) {
629 case B210:
630 case E3XX:
631 if (chans == 1)
632 subdev_string = swap ? "A:B" : "A:A";
633 else if (chans == 2)
634 subdev_string = swap ? "A:B A:A" : "A:A A:B";
635 break;
636 case X3XX:
637 case UMTRX:
638 if (chans == 1)
639 subdev_string = swap ? "B:0" : "A:0";
640 else if (chans == 2)
641 subdev_string = swap ? "B:0 A:0" : "A:0 B:0";
642 break;
643 default:
644 break;
645 }
646
647 if (!subdev_string.empty()) {
648 uhd::usrp::subdev_spec_t spec(subdev_string);
649 usrp_dev->set_tx_subdev_spec(spec);
650 usrp_dev->set_rx_subdev_spec(spec);
651 }
652}
653
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700654int uhd_device::open(const std::string &args, int ref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000655{
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700656 const char *refstr;
657
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000658 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000659 uhd::device_addr_t addr(args);
660 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000661 if (dev_addrs.size() == 0) {
ttsouf60dafa2012-10-22 00:07:14 +0000662 LOG(ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400663 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000664 }
665
666 // Use the first found device
667 LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000668 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700669 usrp_dev = uhd::usrp::multi_usrp::make(addr);
kurtis.heimerle380af32011-11-26 03:18:55 +0000670 } catch(...) {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700671 LOG(ALERT) << "UHD make failed, device " << args;
Thomas Tsoucb69f082013-04-08 14:18:26 -0400672 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000673 }
674
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000675 // Check for a valid device type and set bus type
676 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400677 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000678
Tom Tsou3b093bb2016-05-03 19:04:15 -0700679 if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
680 LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater";
681 return -1;
682 }
683
Tom Tsou980525c2017-06-09 15:37:19 -0700684 try {
685 set_channels(swap_channels);
686 } catch (const std::exception &e) {
687 LOG(ALERT) << "Channel setting failed - " << e.what();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400688 return -1;
689 }
690
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100691 if (!set_antennas()) {
692 LOG(ALERT) << "UHD antenna setting failed";
693 return -1;
694 }
695
Thomas Tsou204a9f12013-10-29 18:34:16 -0400696 tx_freqs.resize(chans);
697 rx_freqs.resize(chans);
698 tx_gains.resize(chans);
699 rx_gains.resize(chans);
700 rx_buffers.resize(chans);
701
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700702 switch (ref) {
703 case REF_INTERNAL:
704 refstr = "internal";
705 break;
706 case REF_EXTERNAL:
707 refstr = "external";
708 break;
709 case REF_GPS:
710 refstr = "gpsdo";
711 break;
712 default:
713 LOG(ALERT) << "Invalid reference type";
714 return -1;
715 }
716
717 usrp_dev->set_clock_source(refstr);
Thomas Tsou010fff72013-10-16 00:31:18 -0400718
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700719 try {
720 set_rates();
721 } catch (const std::exception &e) {
722 LOG(ALERT) << "UHD rate setting failed - " << e.what();
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500723 return -1;
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700724 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000725
Alexander Chemerise1714252015-04-28 23:09:02 -0400726 // Set RF frontend bandwidth
727 if (dev_type == UMTRX) {
728 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
729 for (size_t i = 0; i < chans; i++) {
730 usrp_dev->set_tx_bandwidth(500*1000*2, i);
Tom Tsoud6ae8642017-03-30 17:22:58 -0700731 usrp_dev->set_rx_bandwidth(500*1000*2, i);
Alexander Chemerise1714252015-04-28 23:09:02 -0400732 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800733 } else if (dev_type == LIMESDR) {
734 for (size_t i = 0; i < chans; i++) {
Pau Espin Pedrol55df1e42018-05-08 20:49:00 +0200735 usrp_dev->set_tx_bandwidth(5.2e6, i);
736 usrp_dev->set_rx_bandwidth(1.4001e6, i);
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800737 }
Alexander Chemerise1714252015-04-28 23:09:02 -0400738 }
739
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400740 /* Create TX and RX streamers */
741 uhd::stream_args_t stream_args("sc16");
742 for (size_t i = 0; i < chans; i++)
743 stream_args.channels.push_back(i);
744
745 tx_stream = usrp_dev->get_tx_stream(stream_args);
746 rx_stream = usrp_dev->get_rx_stream(stream_args);
747
748 /* Number of samples per over-the-wire packet */
749 tx_spp = tx_stream->get_max_num_samps();
750 rx_spp = rx_stream->get_max_num_samps();
751
kurtis.heimerl965e7572011-11-26 03:16:54 +0000752 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400753 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400754 for (size_t i = 0; i < rx_buffers.size(); i++)
755 rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000756
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100757 // Initialize and shadow gain values
kurtis.heimerl02d04052011-11-26 03:17:10 +0000758 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000759
kurtis.heimerl965e7572011-11-26 03:16:54 +0000760 // Print configuration
kurtis.heimerl3998da72011-11-26 03:19:00 +0000761 LOG(INFO) << "\n" << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000762
Tom Tsou76764272016-06-24 14:25:39 -0700763 if (iface == MULTI_ARFCN)
764 return MULTI_ARFCN;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500765
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400766 switch (dev_type) {
767 case B100:
768 return RESAMP_64M;
769 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800770 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400771 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500772 case B200:
773 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100774 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800775 case E3XX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800776 case LIMESDR:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500777 default:
778 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400779 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400780
781 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000782}
783
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000784bool uhd_device::flush_recv(size_t num_pkts)
785{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000786 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000787 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800788 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000789
Thomas Tsou18d3b832014-02-13 14:55:23 -0500790 std::vector<std::vector<short> >
791 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000792
Thomas Tsou18d3b832014-02-13 14:55:23 -0500793 std::vector<short *> pkt_ptrs;
794 for (size_t i = 0; i < pkt_bufs.size(); i++)
795 pkt_ptrs.push_back(&pkt_bufs[i].front());
796
797 ts_initial = 0;
798 while (!ts_initial || (num_pkts-- > 0)) {
799 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400800 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000801 if (!num_smpls) {
802 switch (md.error_code) {
803 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800804 LOG(ALERT) << "Device timed out";
805 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000806 default:
807 continue;
808 }
809 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500810
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700811 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000812 }
813
Thomas Tsou18d3b832014-02-13 14:55:23 -0500814 LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
815
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000816 return true;
817}
818
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800819bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000820{
Thomas Tsouc2839162014-03-27 13:52:58 -0400821 /* Allow 100 ms delay to align multi-channel streams */
822 double delay = 0.1;
823
kurtis.heimerl68292102011-11-26 03:17:28 +0000824 aligned = false;
825
Thomas Tsouc2839162014-03-27 13:52:58 -0400826 uhd::time_spec_t current = usrp_dev->get_time_now();
827
Thomas Tsou204a9f12013-10-29 18:34:16 -0400828 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400829 cmd.stream_now = false;
830 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400831
kurtis.heimerl68292102011-11-26 03:17:28 +0000832 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500833
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800834 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000835}
836
kurtis.heimerl965e7572011-11-26 03:16:54 +0000837bool uhd_device::start()
838{
839 LOG(INFO) << "Starting USRP...";
840
841 if (started) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000842 LOG(ERR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000843 return false;
844 }
845
Tom Tsou72bf7622017-03-07 14:16:46 -0800846#ifndef USE_UHD_3_11
Thomas Tsou61b4a6a2013-10-15 20:31:44 -0400847 // Register msg handler
848 uhd::msg::register_handler(&uhd_msg_handler);
Tom Tsou72bf7622017-03-07 14:16:46 -0800849#endif
kurtis.heimerl965e7572011-11-26 03:16:54 +0000850 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800851 async_event_thrd = new Thread();
852 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000853
854 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800855 if (!restart())
856 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000857
kurtis.heimerl965e7572011-11-26 03:16:54 +0000858 // Display usrp time
859 double time_now = usrp_dev->get_time_now().get_real_secs();
860 LOG(INFO) << "The current time is " << time_now << " seconds";
861
862 started = true;
863 return true;
864}
865
866bool uhd_device::stop()
867{
Thomas Tsou2c791102013-11-15 23:00:28 -0500868 if (!started)
869 return false;
870
871 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000872 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
873
874 usrp_dev->issue_stream_cmd(stream_cmd);
875
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800876 async_event_thrd->cancel();
877 async_event_thrd->join();
878 delete async_event_thrd;
879
kurtis.heimerl965e7572011-11-26 03:16:54 +0000880 started = false;
881 return true;
882}
883
Thomas Tsou7553aa92013-11-08 12:50:03 -0500884void uhd_device::setPriority(float prio)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000885{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500886 uhd::set_thread_priority_safe(prio);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000887 return;
888}
889
kurtis.heimerld4be0742011-11-26 03:17:46 +0000890int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000891{
kurtis.heimerld4be0742011-11-26 03:17:46 +0000892 if (!num_smpls) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000893 LOG(ERR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000894
895 switch (md.error_code) {
896 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
ttsoub371ed52012-01-09 18:11:34 +0000897 LOG(ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800898 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000899 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
900 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
901 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
902 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
903 default:
904 return ERROR_UNHANDLED;
905 }
906 }
907
kurtis.heimerl965e7572011-11-26 03:16:54 +0000908 // Missing timestamp
909 if (!md.has_time_spec) {
ttsoub371ed52012-01-09 18:11:34 +0000910 LOG(ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000911 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000912 }
913
914 // Monotonicity check
Tom Tsoua93f7892017-03-07 17:54:06 -0800915 if (md.time_spec < prev_ts) {
ttsoub371ed52012-01-09 18:11:34 +0000916 LOG(ALERT) << "UHD: Loss of monotonic time";
Tom Tsoua93f7892017-03-07 17:54:06 -0800917 LOG(ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
ttsou724eb362012-03-13 02:20:01 +0000918 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000919 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000920 }
921
Tom Tsoua93f7892017-03-07 17:54:06 -0800922 // Workaround for UHD tick rounding bug
923 TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
924 if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
925 md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
926
927 prev_ts = md.time_spec;
928
kurtis.heimerl965e7572011-11-26 03:16:54 +0000929 return 0;
930}
931
Thomas Tsou204a9f12013-10-29 18:34:16 -0400932int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
933 TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000934{
935 ssize_t rc;
936 uhd::time_spec_t ts;
937 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000938
Thomas Tsou204a9f12013-10-29 18:34:16 -0400939 if (bufs.size() != chans) {
940 LOG(ALERT) << "Invalid channel combination " << bufs.size();
941 return -1;
942 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000943
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000944 *overrun = false;
945 *underrun = false;
946
kurtis.heimerl965e7572011-11-26 03:16:54 +0000947 // Shift read time with respect to transmit clock
948 timestamp += ts_offset;
949
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700950 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000951 LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000952
953 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400954 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000955 if (rc < 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400956 LOG(ERR) << rx_buffers[0]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -0800957 LOG(ERR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000958 return 0;
959 }
960
Thomas Tsou204a9f12013-10-29 18:34:16 -0400961 // Create vector buffer
962 std::vector<std::vector<short> >
963 pkt_bufs(chans, std::vector<short>(2 * rx_spp));
964
965 std::vector<short *> pkt_ptrs;
966 for (size_t i = 0; i < pkt_bufs.size(); i++)
967 pkt_ptrs.push_back(&pkt_bufs[i].front());
968
kurtis.heimerl965e7572011-11-26 03:16:54 +0000969 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400970 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800971 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400972 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
973 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800974 thread_enable_cancel(true);
975
kurtis.heimerl965e7572011-11-26 03:16:54 +0000976 rx_pkt_cnt++;
977
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100978 // Check for errors
kurtis.heimerld4be0742011-11-26 03:17:46 +0000979 rc = check_rx_md_err(metadata, num_smpls);
980 switch (rc) {
981 case ERROR_UNRECOVERABLE:
ttsoub371ed52012-01-09 18:11:34 +0000982 LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
983 LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000984 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800985 case ERROR_TIMEOUT:
986 // Assume stopping condition
987 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000988 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -0400989 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000990 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000991 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000992 }
993
kurtis.heimerl965e7572011-11-26 03:16:54 +0000994 ts = metadata.time_spec;
kurtis.heimerl4e59d662011-11-26 03:19:11 +0000995 LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000996
Thomas Tsou204a9f12013-10-29 18:34:16 -0400997 for (size_t i = 0; i < rx_buffers.size(); i++) {
998 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
999 num_smpls,
1000 metadata.time_spec);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001001
Thomas Tsou204a9f12013-10-29 18:34:16 -04001002 // Continue on local overrun, exit on other errors
1003 if ((rc < 0)) {
1004 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001005 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001006 if (rc != smpl_buf::ERROR_OVERFLOW)
1007 return 0;
1008 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001009 }
1010 }
1011
1012 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -04001013 for (size_t i = 0; i < rx_buffers.size(); i++) {
1014 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
1015 if ((rc < 0) || (rc != len)) {
1016 LOG(ERR) << rx_buffers[i]->str_code(rc);
Tom Tsou1ae25562014-12-05 12:56:34 -08001017 LOG(ERR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001018 return 0;
1019 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001020 }
1021
1022 return len;
1023}
1024
Thomas Tsou204a9f12013-10-29 18:34:16 -04001025int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
1026 unsigned long long timestamp,bool isControl)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001027{
1028 uhd::tx_metadata_t metadata;
1029 metadata.has_time_spec = true;
1030 metadata.start_of_burst = false;
1031 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001032 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001033
Thomas Tsoucf910dc2013-10-25 14:42:00 +00001034 *underrun = false;
1035
kurtis.heimerl965e7572011-11-26 03:16:54 +00001036 // No control packets
1037 if (isControl) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001038 LOG(ERR) << "Control packets not supported";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001039 return 0;
1040 }
1041
Thomas Tsou204a9f12013-10-29 18:34:16 -04001042 if (bufs.size() != chans) {
1043 LOG(ALERT) << "Invalid channel combination " << bufs.size();
1044 return -1;
1045 }
1046
kurtis.heimerl965e7572011-11-26 03:16:54 +00001047 // Drop a fixed number of packets (magic value)
1048 if (!aligned) {
1049 drop_cnt++;
1050
1051 if (drop_cnt == 1) {
1052 LOG(DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +00001053 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001054 metadata.end_of_burst = true;
1055 } else if (drop_cnt < 30) {
kurtis.heimerl4e59d662011-11-26 03:19:11 +00001056 LOG(DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +00001057 return len;
1058 } else {
1059 LOG(DEBUG) << "Aligning transmitter: start burst";
1060 metadata.start_of_burst = true;
1061 aligned = true;
1062 drop_cnt = 0;
1063 }
1064 }
1065
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001066 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001067 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001068 thread_enable_cancel(true);
1069
ttsoub371ed52012-01-09 18:11:34 +00001070 if (num_smpls != (unsigned) len) {
1071 LOG(ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +00001072 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001073
1074 return num_smpls;
1075}
1076
1077bool uhd_device::updateAlignment(TIMESTAMP timestamp)
1078{
kurtis.heimerl965e7572011-11-26 03:16:54 +00001079 return true;
1080}
1081
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001082uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
1083{
1084 double rf_spread, rf_freq;
1085 std::vector<double> freqs;
1086 uhd::tune_request_t treq(freq);
1087
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001088 if (dev_type == UMTRX) {
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001089 if (offset != 0.0)
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001090 return uhd::tune_request_t(freq, offset);
1091
1092 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
1093 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
1094 // only distort the signal (because cordic is not ideal).
1095 treq.target_freq = freq;
1096 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1097 treq.rf_freq = freq;
1098 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1099 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +03001100 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +03001101 } else if (chans == 1) {
Thomas Tsou8e17df72014-03-06 14:16:11 -05001102 if (offset == 0.0)
1103 return treq;
1104
1105 return uhd::tune_request_t(freq, offset);
1106 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001107 LOG(ALERT) << chans << " channels unsupported";
1108 return treq;
1109 }
1110
1111 if (tx)
1112 freqs = tx_freqs;
1113 else
1114 freqs = rx_freqs;
1115
1116 /* Tune directly if other channel isn't tuned */
1117 if (freqs[!chan] < 10.0)
1118 return treq;
1119
1120 /* Find center frequency between channels */
1121 rf_spread = fabs(freqs[!chan] - freq);
Tom Tsouf6115692017-06-26 10:13:25 -07001122 if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001123 LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n";
1124 return treq;
1125 }
1126
1127 rf_freq = (freqs[!chan] + freq) / 2.0f;
1128
1129 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1130 treq.target_freq = freq;
1131 treq.rf_freq = rf_freq;
1132
1133 return treq;
1134}
1135
1136bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1137{
1138 std::vector<double> freqs;
1139 uhd::tune_result_t tres;
1140 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1141
1142 if (tx) {
1143 tres = usrp_dev->set_tx_freq(treq, chan);
1144 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1145 } else {
1146 tres = usrp_dev->set_rx_freq(treq, chan);
1147 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1148 }
1149 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1150
Thomas Tsou8e17df72014-03-06 14:16:11 -05001151 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1152 return true;
1153
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001154 /* Manual RF policy means we intentionally tuned with a baseband
1155 * offset for dual-channel purposes. Now retune the other channel
1156 * with the opposite corresponding frequency offset
1157 */
1158 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1159 if (tx) {
1160 treq = select_freq(tx_freqs[!chan], !chan, true);
1161 tres = usrp_dev->set_tx_freq(treq, !chan);
1162 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1163 } else {
1164 treq = select_freq(rx_freqs[!chan], !chan, false);
1165 tres = usrp_dev->set_rx_freq(treq, !chan);
1166 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1167
1168 }
1169 LOG(INFO) << "\n" << tres.to_pp_string() << std::endl;
1170 }
1171
1172 return true;
1173}
1174
Thomas Tsou204a9f12013-10-29 18:34:16 -04001175bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001176{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001177 if (chan >= tx_freqs.size()) {
1178 LOG(ALERT) << "Requested non-existent channel " << chan;
1179 return false;
1180 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001181 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001182
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001183 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001184}
1185
Thomas Tsou204a9f12013-10-29 18:34:16 -04001186bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001187{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001188 if (chan >= rx_freqs.size()) {
1189 LOG(ALERT) << "Requested non-existent channel " << chan;
1190 return false;
1191 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001192 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001193
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001194 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001195}
1196
Thomas Tsou204a9f12013-10-29 18:34:16 -04001197double uhd_device::getTxFreq(size_t chan)
1198{
1199 if (chan >= tx_freqs.size()) {
1200 LOG(ALERT) << "Requested non-existent channel " << chan;
1201 return 0.0;
1202 }
1203
1204 return tx_freqs[chan];
1205}
1206
1207double uhd_device::getRxFreq(size_t chan)
1208{
1209 if (chan >= rx_freqs.size()) {
1210 LOG(ALERT) << "Requested non-existent channel " << chan;
1211 return 0.0;
1212 }
1213
1214 return rx_freqs[chan];
1215}
1216
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001217bool uhd_device::setRxAntenna(const std::string &ant, size_t chan)
1218{
1219 std::vector<std::string> avail;
1220 if (chan >= rx_paths.size()) {
1221 LOG(ALERT) << "Requested non-existent channel " << chan;
1222 return false;
1223 }
1224
1225 avail = usrp_dev->get_rx_antennas(chan);
1226 if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
1227 LOG(ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan;
1228 LOG(INFO) << "Available Rx antennas: ";
1229 for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
1230 LOG(INFO) << "- '" << *i << "'";
1231 return false;
1232 }
1233 usrp_dev->set_rx_antenna(ant, chan);
1234 rx_paths[chan] = usrp_dev->get_rx_antenna(chan);
1235
1236 if (ant != rx_paths[chan]) {
1237 LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan];
1238 return false;
1239 }
1240
1241 return true;
1242}
1243
1244std::string uhd_device::getRxAntenna(size_t chan)
1245{
1246 if (chan >= rx_paths.size()) {
1247 LOG(ALERT) << "Requested non-existent channel " << chan;
1248 return "";
1249 }
1250 return usrp_dev->get_rx_antenna(chan);
1251}
1252
1253bool uhd_device::setTxAntenna(const std::string &ant, size_t chan)
1254{
1255 std::vector<std::string> avail;
1256 if (chan >= tx_paths.size()) {
1257 LOG(ALERT) << "Requested non-existent channel " << chan;
1258 return false;
1259 }
1260
1261 avail = usrp_dev->get_tx_antennas(chan);
1262 if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
1263 LOG(ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan;
1264 LOG(INFO) << "Available Tx antennas: ";
1265 for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
1266 LOG(INFO) << "- '" << *i << "'";
1267 return false;
1268 }
1269 usrp_dev->set_tx_antenna(ant, chan);
1270 tx_paths[chan] = usrp_dev->get_tx_antenna(chan);
1271
1272 if (ant != tx_paths[chan]) {
1273 LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan];
1274 return false;
1275 }
1276
1277 return true;
1278}
1279
1280std::string uhd_device::getTxAntenna(size_t chan)
1281{
1282 if (chan >= tx_paths.size()) {
1283 LOG(ALERT) << "Requested non-existent channel " << chan;
1284 return "";
1285 }
1286 return usrp_dev->get_tx_antenna(chan);
1287}
1288
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +02001289bool uhd_device::requiresRadioAlign()
1290{
1291 return false;
1292}
1293
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001294GSM::Time uhd_device::minLatency() {
1295 /* Empirical data from a handful of
1296 relatively recent machines shows that the B100 will underrun when
1297 the transmit threshold is reduced to a time of 6 and a half frames,
1298 so we set a minimum 7 frame threshold. */
1299 return GSM::Time(6,7);
1300}
1301
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001302/*
1303 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1304 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1305 * combination.
1306 */
1307TIMESTAMP uhd_device::initialWriteTimestamp()
1308{
Tom Tsou76764272016-06-24 14:25:39 -07001309 if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001310 return ts_initial;
1311 else
1312 return ts_initial * tx_sps;
1313}
1314
1315TIMESTAMP uhd_device::initialReadTimestamp()
1316{
1317 return ts_initial;
1318}
1319
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001320double uhd_device::fullScaleInputValue()
1321{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001322 if (dev_type == LIMESDR)
ignasj28d80812017-06-13 23:37:46 +03001323 return (double) SHRT_MAX * LIMESDR_TX_AMPL;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001324 if (dev_type == UMTRX)
1325 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1326 else
1327 return (double) SHRT_MAX * USRP_TX_AMPL;
1328}
1329
1330double uhd_device::fullScaleOutputValue()
1331{
1332 return (double) SHRT_MAX;
1333}
1334
kurtis.heimerl965e7572011-11-26 03:16:54 +00001335bool uhd_device::recv_async_msg()
1336{
ttsou60dc4c92012-08-08 23:30:23 +00001337 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001338
1339 thread_enable_cancel(false);
1340 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1341 thread_enable_cancel(true);
1342 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001343 return false;
1344
1345 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001346 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001347 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001348
1349 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1350 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
1351 LOG(ERR) << str_code(md);
1352 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001353 }
1354
1355 return true;
1356}
1357
1358std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1359{
1360 std::ostringstream ost("UHD: ");
1361
1362 switch (metadata.error_code) {
1363 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1364 ost << "No error";
1365 break;
1366 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1367 ost << "No packet received, implementation timed-out";
1368 break;
1369 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1370 ost << "A stream command was issued in the past";
1371 break;
1372 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1373 ost << "Expected another stream command";
1374 break;
1375 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1376 ost << "An internal receive buffer has filled";
1377 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001378 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1379 ost << "Multi-channel alignment failed";
1380 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001381 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1382 ost << "The packet could not be parsed";
1383 break;
1384 default:
1385 ost << "Unknown error " << metadata.error_code;
1386 }
1387
1388 if (metadata.has_time_spec)
1389 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1390
1391 return ost.str();
1392}
1393
1394std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1395{
1396 std::ostringstream ost("UHD: ");
1397
1398 switch (metadata.event_code) {
1399 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1400 ost << "A packet was successfully transmitted";
1401 break;
1402 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1403 ost << "An internal send buffer has emptied";
1404 break;
1405 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1406 ost << "Packet loss between host and device";
1407 break;
1408 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1409 ost << "Packet time was too late or too early";
1410 break;
1411 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1412 ost << "Underflow occurred inside a packet";
1413 break;
1414 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1415 ost << "Packet loss within a burst";
1416 break;
1417 default:
1418 ost << "Unknown error " << metadata.event_code;
1419 }
1420
1421 if (metadata.has_time_spec)
1422 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1423
1424 return ost.str();
1425}
1426
1427smpl_buf::smpl_buf(size_t len, double rate)
1428 : buf_len(len), clk_rt(rate),
1429 time_start(0), time_end(0), data_start(0), data_end(0)
1430{
1431 data = new uint32_t[len];
1432}
1433
1434smpl_buf::~smpl_buf()
1435{
1436 delete[] data;
1437}
1438
1439ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
1440{
1441 if (timestamp < time_start)
1442 return ERROR_TIMESTAMP;
1443 else if (timestamp >= time_end)
1444 return 0;
1445 else
1446 return time_end - timestamp;
1447}
1448
1449ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
1450{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001451 return avail_smpls(timespec.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001452}
1453
1454ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
1455{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001456 int type_sz = 2 * sizeof(short);
1457
kurtis.heimerl965e7572011-11-26 03:16:54 +00001458 // Check for valid read
1459 if (timestamp < time_start)
1460 return ERROR_TIMESTAMP;
1461 if (timestamp >= time_end)
1462 return 0;
1463 if (len >= buf_len)
1464 return ERROR_READ;
1465
1466 // How many samples should be copied
1467 size_t num_smpls = time_end - timestamp;
kurtis.heimerlec842de2012-11-23 08:37:32 +00001468 if (num_smpls > len)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001469 num_smpls = len;
1470
1471 // Starting index
Thomas Tsou18d3b832014-02-13 14:55:23 -05001472 size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001473
1474 // Read it
1475 if (read_start + num_smpls < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001476 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001477 memcpy(buf, data + read_start, numBytes);
1478 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001479 size_t first_cp = (buf_len - read_start) * type_sz;
1480 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001481
1482 memcpy(buf, data + read_start, first_cp);
1483 memcpy((char*) buf + first_cp, data, second_cp);
1484 }
1485
1486 data_start = (read_start + len) % buf_len;
1487 time_start = timestamp + len;
1488
1489 if (time_start > time_end)
1490 return ERROR_READ;
1491 else
1492 return num_smpls;
1493}
1494
1495ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
1496{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001497 return read(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001498}
1499
1500ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
1501{
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001502 int type_sz = 2 * sizeof(short);
1503
kurtis.heimerl965e7572011-11-26 03:16:54 +00001504 // Check for valid write
1505 if ((len == 0) || (len >= buf_len))
1506 return ERROR_WRITE;
1507 if ((timestamp + len) <= time_end)
1508 return ERROR_TIMESTAMP;
1509
Alexander Chemerisc052aa12015-06-10 21:47:33 -04001510 if (timestamp < time_end) {
1511 LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001512 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1513 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 -04001514 // Do not return error here, because it's a rounding error and is not fatal
1515 }
1516 if (timestamp > time_end && time_end != 0) {
1517 LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001518 uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
1519 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 -04001520 // Do not return error here, because it's a rounding error and is not fatal
1521 }
1522
kurtis.heimerl965e7572011-11-26 03:16:54 +00001523 // Starting index
1524 size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
1525
1526 // Write it
1527 if ((write_start + len) < buf_len) {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001528 size_t numBytes = len * type_sz;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001529 memcpy(data + write_start, buf, numBytes);
1530 } else {
kurtis.heimerl1979c6f2011-11-26 03:18:24 +00001531 size_t first_cp = (buf_len - write_start) * type_sz;
1532 size_t second_cp = len * type_sz - first_cp;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001533
1534 memcpy(data + write_start, buf, first_cp);
1535 memcpy(data, (char*) buf + first_cp, second_cp);
1536 }
1537
1538 data_end = (write_start + len) % buf_len;
1539 time_end = timestamp + len;
1540
Thomas Tsou18d3b832014-02-13 14:55:23 -05001541 if (!data_start)
1542 data_start = write_start;
1543
kurtis.heimerl965e7572011-11-26 03:16:54 +00001544 if (((write_start + len) > buf_len) && (data_end > data_start))
1545 return ERROR_OVERFLOW;
1546 else if (time_end <= time_start)
1547 return ERROR_WRITE;
1548 else
1549 return len;
1550}
1551
1552ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
1553{
Tom Tsoud4d3daa2015-08-21 19:21:28 -07001554 return write(buf, len, ts.to_ticks(clk_rt));
kurtis.heimerl965e7572011-11-26 03:16:54 +00001555}
1556
Tom Tsou1ae25562014-12-05 12:56:34 -08001557std::string smpl_buf::str_status(size_t ts) const
kurtis.heimerl965e7572011-11-26 03:16:54 +00001558{
1559 std::ostringstream ost("Sample buffer: ");
1560
Tom Tsou1ae25562014-12-05 12:56:34 -08001561 ost << "timestamp = " << ts;
1562 ost << ", length = " << buf_len;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001563 ost << ", time_start = " << time_start;
1564 ost << ", time_end = " << time_end;
1565 ost << ", data_start = " << data_start;
1566 ost << ", data_end = " << data_end;
1567
1568 return ost.str();
1569}
1570
1571std::string smpl_buf::str_code(ssize_t code)
1572{
1573 switch (code) {
1574 case ERROR_TIMESTAMP:
1575 return "Sample buffer: Requested timestamp is not valid";
1576 case ERROR_READ:
1577 return "Sample buffer: Read error";
1578 case ERROR_WRITE:
1579 return "Sample buffer: Write error";
1580 case ERROR_OVERFLOW:
1581 return "Sample buffer: Overrun";
1582 default:
1583 return "Sample buffer: Unknown error";
1584 }
1585}
1586
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001587RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001588 InterfaceType iface, size_t chans, double offset,
1589 const std::vector<std::string>& tx_paths,
1590 const std::vector<std::string>& rx_paths)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001591{
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001592 return new uhd_device(tx_sps, rx_sps, iface, chans, offset, tx_paths, rx_paths);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001593}