blob: 228e1668ec01d773099286e77c8a91050a472c93 [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 *
Pau Espin Pedrol21d03d32019-07-22 12:05:52 +02009 * SPDX-License-Identifier: AGPL-3.0+
10 *
kurtis.heimerl119ca9c2011-11-26 03:18:26 +000011 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Affero General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU Affero General Public License for more details.
20 *
21 * You should have received a copy of the GNU Affero General Public License
22 * along with this program. If not, see <http://www.gnu.org/licenses/>.
23 * See the COPYING file in the main directory for details.
24 */
kurtis.heimerl965e7572011-11-26 03:16:54 +000025
Tom Tsou1fb0ce62017-06-08 19:41:48 -070026#include <map>
kurtis.heimerl965e7572011-11-26 03:16:54 +000027#include "radioDevice.h"
Pau Espin Pedrol7bef2342019-04-29 17:23:21 +020028#include "UHDDevice.h"
kurtis.heimerl965e7572011-11-26 03:16:54 +000029#include "Threads.h"
30#include "Logger.h"
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
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +010036#ifdef USE_UHD_3_11
37#include <uhd/utils/log_add.hpp>
38#include <uhd/utils/thread.hpp>
39#else
Tom Tsou72bf7622017-03-07 14:16:46 -080040#include <uhd/utils/msg.hpp>
Pau Espin Pedrol1f4a0092018-08-30 17:24:49 +020041#include <uhd/utils/thread_priority.hpp>
Tom Tsou72bf7622017-03-07 14:16:46 -080042#endif
43
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +030044#define USRP_TX_AMPL 0.3
45#define UMTRX_TX_AMPL 0.7
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -080046#define LIMESDR_TX_AMPL 0.3
Thomas Tsoue3e88142013-04-05 20:42:41 -040047#define SAMPLE_BUF_SZ (1 << 20)
Thomas Tsou02d88d12013-04-05 15:36:30 -040048
Tom Tsoueb54bdd2014-11-25 16:06:32 -080049/*
50 * UHD timeout value on streaming (re)start
51 *
52 * Allow some time for streaming to commence after the start command is issued,
53 * but consider a wait beyond one second to be a definite error condition.
54 */
55#define UHD_RESTART_TIMEOUT 1.0
56
Alexander Chemeris4d029d82014-04-19 17:25:00 +040057/*
58 * UmTRX specific settings
59 */
60#define UMTRX_VGA1_DEF -18
61
kurtis.heimerl965e7572011-11-26 03:16:54 +000062/*
Tom Tsouc3129052015-08-21 18:28:52 -070063 * USRP version dependent device timings
64 */
Tom Tsou72bf7622017-03-07 14:16:46 -080065#if defined(USE_UHD_3_9) || defined(USE_UHD_3_11)
Tom Tsouc3129052015-08-21 18:28:52 -070066#define B2XX_TIMING_1SPS 1.7153e-4
67#define B2XX_TIMING_4SPS 1.1696e-4
Tom Tsoud2b07032016-04-26 19:28:59 -070068#define B2XX_TIMING_4_4SPS 6.18462e-5
Tom Tsoua93f7892017-03-07 17:54:06 -080069#define B2XX_TIMING_MCBTS 7e-5
Tom Tsou80cb0802017-01-19 13:44:02 -080070#else
71#define B2XX_TIMING_1SPS 9.9692e-5
72#define B2XX_TIMING_4SPS 6.9248e-5
73#define B2XX_TIMING_4_4SPS 4.52308e-5
Tom Tsoua93f7892017-03-07 17:54:06 -080074#define B2XX_TIMING_MCBTS 6.42452e-5
Tom Tsou80cb0802017-01-19 13:44:02 -080075#endif
Tom Tsouc3129052015-08-21 18:28:52 -070076
77/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040078 * Tx / Rx sample offset values. In a perfect world, there is no group delay
79 * though analog components, and behaviour through digital filters exactly
80 * matches calculated values. In reality, there are unaccounted factors,
81 * which are captured in these empirically measured (using a loopback test)
82 * timing correction values.
83 *
84 * Notes:
85 * USRP1 with timestamps is not supported by UHD.
86 */
Tom Tsou1fb0ce62017-06-08 19:41:48 -070087
88/* Device Type, Tx-SPS, Rx-SPS */
89typedef std::tuple<uhd_dev_type, int, int> dev_key;
90
91/* Device parameter descriptor */
92struct dev_desc {
93 unsigned channels;
94 double mcr;
95 double rate;
96 double offset;
97 std::string str;
Tom Tsou5cd70dc2016-03-06 01:28:40 -080098};
kurtis.heimerl965e7572011-11-26 03:16:54 +000099
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700100static const std::map<dev_key, dev_desc> dev_param_map {
101 { std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } },
102 { std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } },
103 { std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } },
104 { std::make_tuple(B100, 1, 1), { 1, 0.0, 400000, 1.2104e-4, "B100 1 SPS" } },
105 { std::make_tuple(B100, 4, 1), { 1, 0.0, 400000, 7.9307e-5, "B100 4/1 Tx/Rx SPS" } },
106 { std::make_tuple(B200, 1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS" } },
107 { std::make_tuple(B200, 4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } },
108 { std::make_tuple(B200, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
109 { std::make_tuple(B210, 1, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B210 1 SPS" } },
110 { std::make_tuple(B210, 4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" } },
111 { std::make_tuple(B210, 4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B210 4 SPS" } },
112 { std::make_tuple(E1XX, 1, 1), { 1, 52e6, GSMRATE, 9.5192e-5, "E1XX 1 SPS" } },
113 { std::make_tuple(E1XX, 4, 1), { 1, 52e6, GSMRATE, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" } },
114 { std::make_tuple(E3XX, 1, 1), { 2, 26e6, GSMRATE, 1.8462e-4, "E3XX 1 SPS" } },
115 { std::make_tuple(E3XX, 4, 1), { 2, 26e6, GSMRATE, 1.2923e-4, "E3XX 4/1 Tx/Rx SPS" } },
116 { std::make_tuple(X3XX, 1, 1), { 2, 0.0, 390625, 1.5360e-4, "X3XX 1 SPS" } },
117 { std::make_tuple(X3XX, 4, 1), { 2, 0.0, 390625, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS" } },
118 { std::make_tuple(X3XX, 4, 4), { 2, 0.0, 390625, 5.6567e-5, "X3XX 4 SPS" } },
119 { std::make_tuple(UMTRX, 1, 1), { 2, 0.0, GSMRATE, 9.9692e-5, "UmTRX 1 SPS" } },
120 { std::make_tuple(UMTRX, 4, 1), { 2, 0.0, GSMRATE, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS"} },
121 { std::make_tuple(UMTRX, 4, 4), { 2, 0.0, GSMRATE, 5.1503e-5, "UmTRX 4 SPS" } },
Tom Tsou4cafb0f2017-06-26 10:13:25 -0700122 { std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS" } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700123 { std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
Keith6d496c82020-03-20 12:50:19 -0500124 { std::make_tuple(OCR01, 4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "OCR01 4/1 Tx/Rx SPS"} },
Keith36290172020-03-23 14:22:36 -0500125 { std::make_tuple(OCR01, 4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "OCR01 4/4 Tx/Rx SPS"} },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700126};
Thomas Tsoucb69f082013-04-08 14:18:26 -0400127
kurtis.heimerl965e7572011-11-26 03:16:54 +0000128void *async_event_loop(uhd_device *dev)
129{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +0200130 set_selfthread_name("UHDAsyncEvent");
Thomas Tsou7553aa92013-11-08 12:50:03 -0500131
kurtis.heimerl965e7572011-11-26 03:16:54 +0000132 while (1) {
133 dev->recv_async_msg();
134 pthread_testcancel();
135 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500136
137 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000138}
139
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100140#ifdef USE_UHD_3_11
141static void uhd_log_handler(const uhd::log::logging_info &info)
142{
143 int level;
144
145 switch (info.verbosity)
146 {
147 case uhd::log::trace:
148 case uhd::log::debug:
149 level = LOGL_DEBUG;
150 break;
151 case uhd::log::info:
152 level = LOGL_INFO;
153 break;
154 case uhd::log::warning:
155 level = LOGL_NOTICE;
156 break;
157 case uhd::log::error:
158 level = LOGL_ERROR;
159 break;
160 case uhd::log::fatal:
161 level = LOGL_FATAL;
162 break;
163 default:
164 level = LOGL_NOTICE;
165 }
166
167 LOGSRC(DDEVDRV, level, info.file.c_str(), info.line) << "[" << info.component << "] " << info.message;
168}
169#else
Tom Tsou72bf7622017-03-07 14:16:46 -0800170/*
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000171 Catch and drop underrun 'U' and overrun 'O' messages from stdout
172 since we already report using the logging facility. Direct
173 everything else appropriately.
174 */
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100175static void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000176{
177 switch (type) {
178 case uhd::msg::status:
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100179 LOGC(DDEVDRV, INFO) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000180 break;
181 case uhd::msg::warning:
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100182 LOGC(DDEVDRV, NOTICE) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000183 break;
184 case uhd::msg::error:
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100185 LOGC(DDEVDRV, ERROR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000186 break;
187 case uhd::msg::fastpath:
188 break;
189 }
190}
Tom Tsou72bf7622017-03-07 14:16:46 -0800191#endif
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000192
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800193uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
Pau Espin Pedrolb0e54262020-01-13 16:00:04 +0100194 InterfaceType iface, size_t chan_num, double lo_offset,
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100195 const std::vector<std::string>& tx_paths,
196 const std::vector<std::string>& rx_paths)
Pau Espin Pedrolb0e54262020-01-13 16:00:04 +0100197 : RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
Harald Welte61707e82018-06-13 23:21:57 +0200198 tx_gain_min(0.0), tx_gain_max(0.0),
Thomas Tsou204a9f12013-10-29 18:34:16 -0400199 rx_gain_min(0.0), rx_gain_max(0.0),
200 tx_spp(0), rx_spp(0),
Pau Espin Pedrol7214fde2019-04-30 19:01:37 +0200201 started(false), aligned(false), drop_cnt(0),
Pau Espin Pedrolb7095c72018-02-09 16:19:31 +0100202 prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000203{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000204}
205
206uhd_device::~uhd_device()
207{
208 stop();
209
Thomas Tsou204a9f12013-10-29 18:34:16 -0400210 for (size_t i = 0; i < rx_buffers.size(); i++)
211 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000212}
213
kurtis.heimerl24481de2011-11-26 03:17:18 +0000214void uhd_device::init_gains()
215{
216 uhd::gain_range_t range;
217
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400218 if (dev_type == UMTRX) {
219 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
220 if (gain_stages[0] == "VGA") {
Harald Welte5cc88582018-08-17 19:55:38 +0200221 LOGC(DDEV, WARNING) << "Update your UHD version for a proper Tx gain support";
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400222 }
223 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
224 range = usrp_dev->get_tx_gain_range();
225 tx_gain_min = range.start();
226 tx_gain_max = range.stop();
227 } else {
228 range = usrp_dev->get_tx_gain_range("VGA2");
229 tx_gain_min = UMTRX_VGA1_DEF + range.start();
230 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
231 }
232 } else {
233 range = usrp_dev->get_tx_gain_range();
234 tx_gain_min = range.start();
235 tx_gain_max = range.stop();
236 }
Harald Welte5cc88582018-08-17 19:55:38 +0200237 LOGC(DDEV, INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000238
239 range = usrp_dev->get_rx_gain_range();
240 rx_gain_min = range.start();
241 rx_gain_max = range.stop();
Harald Welte5cc88582018-08-17 19:55:38 +0200242 LOGC(DDEV, INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000243
Thomas Tsou204a9f12013-10-29 18:34:16 -0400244 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400245 double gain = (tx_gain_min + tx_gain_max) / 2;
Harald Welte5cc88582018-08-17 19:55:38 +0200246 LOGC(DDEV, INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400247 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400248 tx_gains[i] = usrp_dev->get_tx_gain(i);
249 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000250
Thomas Tsou204a9f12013-10-29 18:34:16 -0400251 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400252 double gain = (rx_gain_min + rx_gain_max) / 2;
Harald Welte5cc88582018-08-17 19:55:38 +0200253 LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400254 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400255 rx_gains[i] = usrp_dev->get_rx_gain(i);
256 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000257
258 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400259
kurtis.heimerl24481de2011-11-26 03:17:18 +0000260}
261
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700262void uhd_device::set_rates()
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700263{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700264 dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
265 if (desc.mcr != 0.0)
266 usrp_dev->set_master_clock_rate(desc.mcr);
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700267
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700268 tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate;
269 rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate;
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700270
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700271 usrp_dev->set_tx_rate(tx_rate);
272 usrp_dev->set_rx_rate(rx_rate);
273 tx_rate = usrp_dev->get_tx_rate();
274 rx_rate = usrp_dev->get_rx_rate();
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700275
Tom Tsou1b6ab7d2017-06-15 15:31:08 -0700276 ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
Harald Welte5cc88582018-08-17 19:55:38 +0200277 LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000278}
279
Thomas Tsou204a9f12013-10-29 18:34:16 -0400280double uhd_device::setTxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000281{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400282 if (chan >= tx_gains.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200283 LOGC(DDEV, ALERT) << "Requested non-existent channel" << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400284 return 0.0f;
285 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000286
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400287 if (dev_type == UMTRX) {
288 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
289 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
290 usrp_dev->set_tx_gain(db, chan);
291 } else {
292 // New UHD versions support split configuration of
293 // Tx gain stages. We utilize this to set the gain
294 // configuration, optimal for the Tx signal quality.
295 // From our measurements, VGA1 must be 18dB plus-minus
296 // one and VGA2 is the best when 23dB or lower.
297 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
298 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
299 }
300 } else {
301 usrp_dev->set_tx_gain(db, chan);
302 }
303
Thomas Tsou204a9f12013-10-29 18:34:16 -0400304 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000305
Harald Welte5cc88582018-08-17 19:55:38 +0200306 LOGC(DDEV, INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400307
308 return tx_gains[chan];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000309}
310
Thomas Tsou204a9f12013-10-29 18:34:16 -0400311double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000312{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400313 if (chan >= rx_gains.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200314 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400315 return 0.0f;
316 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000317
Thomas Tsou204a9f12013-10-29 18:34:16 -0400318 usrp_dev->set_rx_gain(db, chan);
319 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000320
Harald Welte5cc88582018-08-17 19:55:38 +0200321 LOGC(DDEV, INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400322
323 return rx_gains[chan];
324}
325
326double uhd_device::getRxGain(size_t chan)
327{
328 if (chan >= rx_gains.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200329 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400330 return 0.0f;
331 }
332
333 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000334}
335
Pau Espin Pedrol2ab92182019-09-13 17:05:02 +0200336double uhd_device::getTxGain(size_t chan)
337{
Pau Espin Pedrol2ab92182019-09-13 17:05:02 +0200338 if (chan >= tx_gains.size()) {
339 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
340 return 0.0f;
341 }
342
343 return tx_gains[chan];
344}
345
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000346/*
347 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400348 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000349 deal with the transport latency. Reject the USRP1 because UHD doesn't
350 support timestamped samples with it.
351 */
352bool uhd_device::parse_dev_type()
353{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700354 uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree();
355 std::string devString = prop_tree->access<std::string>("/name").get();
356 std::string mboardString = usrp_dev->get_mboard_name();
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000357
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700358 const std::map<std::string, std::pair<uhd_dev_type, TxWindowType>> devStringMap {
359 { "B100", { B100, TX_WINDOW_USRP1 } },
360 { "B200", { B200, TX_WINDOW_USRP1 } },
361 { "B200mini", { B200, TX_WINDOW_USRP1 } },
Piotr Krysikaa60dda2017-12-04 00:29:16 +0700362 { "B205mini", { B200, TX_WINDOW_USRP1 } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700363 { "B210", { B210, TX_WINDOW_USRP1 } },
364 { "E100", { E1XX, TX_WINDOW_FIXED } },
365 { "E110", { E1XX, TX_WINDOW_FIXED } },
366 { "E310", { E3XX, TX_WINDOW_FIXED } },
367 { "E3XX", { E3XX, TX_WINDOW_FIXED } },
368 { "X300", { X3XX, TX_WINDOW_FIXED } },
369 { "X310", { X3XX, TX_WINDOW_FIXED } },
Tom Tsou988a4642017-06-15 09:16:53 -0700370 { "USRP2", { USRP2, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700371 { "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
ignasj87ed77b2017-06-13 23:37:46 +0300372 { "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } },
Keith6d496c82020-03-20 12:50:19 -0500373 { "OCR01", { OCR01, TX_WINDOW_USRP1 } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700374 };
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000375
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700376 // Compare UHD motherboard and device strings */
Tom Tsou988a4642017-06-15 09:16:53 -0700377 auto mapIter = devStringMap.begin();
378 while (mapIter != devStringMap.end()) {
379 if (devString.find(mapIter->first) != std::string::npos ||
380 mboardString.find(mapIter->first) != std::string::npos) {
381 dev_type = std::get<0>(mapIter->second);
382 tx_window = std::get<1>(mapIter->second);
383 return true;
384 }
385 mapIter++;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000386 }
387
Harald Welte5cc88582018-08-17 19:55:38 +0200388 LOGC(DDEV, ALERT) << "Unsupported device " << devString;
Tom Tsou988a4642017-06-15 09:16:53 -0700389 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000390}
391
Tom Tsou3b093bb2016-05-03 19:04:15 -0700392/*
393 * Check for UHD version > 3.9.0 for E3XX support
394 */
395static bool uhd_e3xx_version_chk()
396{
397 std::string ver = uhd::get_version_string();
398 std::string major_str(ver.begin(), ver.begin() + 3);
399 std::string minor_str(ver.begin() + 4, ver.begin() + 7);
400
401 int major_val = atoi(major_str.c_str());
402 int minor_val = atoi(minor_str.c_str());
403
404 if (major_val < 3)
405 return false;
406 if (minor_val < 9)
407 return false;
408
409 return true;
410}
411
Tom Tsou980525c2017-06-09 15:37:19 -0700412void uhd_device::set_channels(bool swap)
413{
414 if (iface == MULTI_ARFCN) {
Keith6d496c82020-03-20 12:50:19 -0500415 if (dev_type != B200 && dev_type != B210 && dev_type != OCR01)
Tom Tsou980525c2017-06-09 15:37:19 -0700416 throw std::invalid_argument("Device does not support MCBTS");
417 dev_type = B2XX_MCBTS;
Tom Tsou980525c2017-06-09 15:37:19 -0700418 }
419
Tom Tsouf6115692017-06-26 10:13:25 -0700420 if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
Tom Tsou980525c2017-06-09 15:37:19 -0700421 throw std::invalid_argument("Device does not support number of requested channels");
422
423 std::string subdev_string;
424 switch (dev_type) {
425 case B210:
426 case E3XX:
Keith6d496c82020-03-20 12:50:19 -0500427 case OCR01:
Tom Tsou980525c2017-06-09 15:37:19 -0700428 if (chans == 1)
429 subdev_string = swap ? "A:B" : "A:A";
430 else if (chans == 2)
431 subdev_string = swap ? "A:B A:A" : "A:A A:B";
432 break;
433 case X3XX:
434 case UMTRX:
435 if (chans == 1)
436 subdev_string = swap ? "B:0" : "A:0";
437 else if (chans == 2)
438 subdev_string = swap ? "B:0 A:0" : "A:0 B:0";
439 break;
440 default:
441 break;
442 }
443
444 if (!subdev_string.empty()) {
445 uhd::usrp::subdev_spec_t spec(subdev_string);
446 usrp_dev->set_tx_subdev_spec(spec);
447 usrp_dev->set_rx_subdev_spec(spec);
448 }
449}
450
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700451int uhd_device::open(const std::string &args, int ref, bool swap_channels)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000452{
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700453 const char *refstr;
454
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100455 /* Register msg handler. Different APIs depending on UHD version */
456#ifdef USE_UHD_3_11
457 uhd::log::add_logger("OsmoTRX", &uhd_log_handler);
458 uhd::log::set_log_level(uhd::log::debug);
459 uhd::log::set_console_level(uhd::log::off);
460 uhd::log::set_logger_level("OsmoTRX", uhd::log::debug);
461#else
462 uhd::msg::register_handler(&uhd_msg_handler);
463#endif
464
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000465 // Find UHD devices
ttsouf60dafa2012-10-22 00:07:14 +0000466 uhd::device_addr_t addr(args);
467 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000468 if (dev_addrs.size() == 0) {
Harald Welte5cc88582018-08-17 19:55:38 +0200469 LOGC(DDEV, ALERT) << "No UHD devices found with address '" << args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400470 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000471 }
472
473 // Use the first found device
Harald Welte5cc88582018-08-17 19:55:38 +0200474 LOGC(DDEV, INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000475 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700476 usrp_dev = uhd::usrp::multi_usrp::make(addr);
d0gtailebb37692018-12-01 17:02:15 +0100477 } catch(uhd::key_error::exception &e) {
478 LOGC(DDEV, ALERT) << "UHD make failed, device " << args << ", exception:\n" << e.what();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400479 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000480 }
481
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000482 // Check for a valid device type and set bus type
483 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400484 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000485
Tom Tsou3b093bb2016-05-03 19:04:15 -0700486 if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200487 LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater";
Tom Tsou3b093bb2016-05-03 19:04:15 -0700488 return -1;
489 }
490
Tom Tsou980525c2017-06-09 15:37:19 -0700491 try {
492 set_channels(swap_channels);
493 } catch (const std::exception &e) {
Harald Welte5cc88582018-08-17 19:55:38 +0200494 LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400495 return -1;
496 }
497
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100498 if (!set_antennas()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200499 LOGC(DDEV, ALERT) << "UHD antenna setting failed";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100500 return -1;
501 }
502
Thomas Tsou204a9f12013-10-29 18:34:16 -0400503 tx_freqs.resize(chans);
504 rx_freqs.resize(chans);
505 tx_gains.resize(chans);
506 rx_gains.resize(chans);
507 rx_buffers.resize(chans);
508
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700509 switch (ref) {
510 case REF_INTERNAL:
511 refstr = "internal";
512 break;
513 case REF_EXTERNAL:
514 refstr = "external";
515 break;
516 case REF_GPS:
517 refstr = "gpsdo";
518 break;
519 default:
Harald Welte5cc88582018-08-17 19:55:38 +0200520 LOGC(DDEV, ALERT) << "Invalid reference type";
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700521 return -1;
522 }
523
524 usrp_dev->set_clock_source(refstr);
Thomas Tsou010fff72013-10-16 00:31:18 -0400525
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700526 try {
527 set_rates();
528 } catch (const std::exception &e) {
Harald Welte5cc88582018-08-17 19:55:38 +0200529 LOGC(DDEV, ALERT) << "UHD rate setting failed - " << e.what();
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500530 return -1;
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700531 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000532
Alexander Chemerise1714252015-04-28 23:09:02 -0400533 // Set RF frontend bandwidth
534 if (dev_type == UMTRX) {
535 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
536 for (size_t i = 0; i < chans; i++) {
537 usrp_dev->set_tx_bandwidth(500*1000*2, i);
Tom Tsoud6ae8642017-03-30 17:22:58 -0700538 usrp_dev->set_rx_bandwidth(500*1000*2, i);
Alexander Chemerise1714252015-04-28 23:09:02 -0400539 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800540 } else if (dev_type == LIMESDR) {
541 for (size_t i = 0; i < chans; i++) {
Pau Espin Pedrol55df1e42018-05-08 20:49:00 +0200542 usrp_dev->set_tx_bandwidth(5.2e6, i);
543 usrp_dev->set_rx_bandwidth(1.4001e6, i);
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800544 }
Alexander Chemerise1714252015-04-28 23:09:02 -0400545 }
546
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400547 /* Create TX and RX streamers */
548 uhd::stream_args_t stream_args("sc16");
549 for (size_t i = 0; i < chans; i++)
550 stream_args.channels.push_back(i);
551
552 tx_stream = usrp_dev->get_tx_stream(stream_args);
553 rx_stream = usrp_dev->get_rx_stream(stream_args);
554
555 /* Number of samples per over-the-wire packet */
556 tx_spp = tx_stream->get_max_num_samps();
557 rx_spp = rx_stream->get_max_num_samps();
558
kurtis.heimerl965e7572011-11-26 03:16:54 +0000559 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400560 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400561 for (size_t i = 0; i < rx_buffers.size(); i++)
Pau Espin Pedrol580c48b2019-05-03 14:38:36 +0200562 rx_buffers[i] = new smpl_buf(buf_len);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000563
Pau Espin Pedrolef192d32019-04-30 18:51:00 +0200564 // Create vector buffer
565 pkt_bufs = std::vector<std::vector<short> >(chans, std::vector<short>(2 * rx_spp));
566 for (size_t i = 0; i < pkt_bufs.size(); i++)
567 pkt_ptrs.push_back(&pkt_bufs[i].front());
568
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100569 // Initialize and shadow gain values
kurtis.heimerl02d04052011-11-26 03:17:10 +0000570 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000571
kurtis.heimerl965e7572011-11-26 03:16:54 +0000572 // Print configuration
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +0100573 LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000574
Tom Tsou76764272016-06-24 14:25:39 -0700575 if (iface == MULTI_ARFCN)
576 return MULTI_ARFCN;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500577
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400578 switch (dev_type) {
579 case B100:
580 return RESAMP_64M;
581 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800582 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400583 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500584 case B200:
585 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100586 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800587 case E3XX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800588 case LIMESDR:
Keith6d496c82020-03-20 12:50:19 -0500589 case OCR01:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500590 default:
591 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400592 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400593
594 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000595}
596
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000597bool uhd_device::flush_recv(size_t num_pkts)
598{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000599 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000600 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800601 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000602
Thomas Tsou18d3b832014-02-13 14:55:23 -0500603 ts_initial = 0;
604 while (!ts_initial || (num_pkts-- > 0)) {
605 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400606 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000607 if (!num_smpls) {
608 switch (md.error_code) {
609 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Harald Welte5cc88582018-08-17 19:55:38 +0200610 LOGC(DDEV, ALERT) << "Device timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800611 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000612 default:
613 continue;
614 }
615 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500616
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700617 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000618 }
619
Harald Welte5cc88582018-08-17 19:55:38 +0200620 LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
Thomas Tsou18d3b832014-02-13 14:55:23 -0500621
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000622 return true;
623}
624
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800625bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000626{
Thomas Tsouc2839162014-03-27 13:52:58 -0400627 /* Allow 100 ms delay to align multi-channel streams */
628 double delay = 0.1;
629
kurtis.heimerl68292102011-11-26 03:17:28 +0000630 aligned = false;
631
Thomas Tsouc2839162014-03-27 13:52:58 -0400632 uhd::time_spec_t current = usrp_dev->get_time_now();
633
Thomas Tsou204a9f12013-10-29 18:34:16 -0400634 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400635 cmd.stream_now = false;
636 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400637
kurtis.heimerl68292102011-11-26 03:17:28 +0000638 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500639
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800640 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000641}
642
kurtis.heimerl965e7572011-11-26 03:16:54 +0000643bool uhd_device::start()
644{
Harald Welte5cc88582018-08-17 19:55:38 +0200645 LOGC(DDEV, INFO) << "Starting USRP...";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000646
647 if (started) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200648 LOGC(DDEV, ERROR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000649 return false;
650 }
651
kurtis.heimerl965e7572011-11-26 03:16:54 +0000652 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800653 async_event_thrd = new Thread();
654 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000655
656 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800657 if (!restart())
658 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000659
kurtis.heimerl965e7572011-11-26 03:16:54 +0000660 // Display usrp time
661 double time_now = usrp_dev->get_time_now().get_real_secs();
Harald Welte5cc88582018-08-17 19:55:38 +0200662 LOGC(DDEV, INFO) << "The current time is " << time_now << " seconds";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000663
664 started = true;
665 return true;
666}
667
668bool uhd_device::stop()
669{
Thomas Tsou2c791102013-11-15 23:00:28 -0500670 if (!started)
671 return false;
672
673 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000674 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
675
676 usrp_dev->issue_stream_cmd(stream_cmd);
677
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800678 async_event_thrd->cancel();
679 async_event_thrd->join();
680 delete async_event_thrd;
681
kurtis.heimerl965e7572011-11-26 03:16:54 +0000682 started = false;
683 return true;
684}
685
kurtis.heimerld4be0742011-11-26 03:17:46 +0000686int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000687{
kurtis.heimerld4be0742011-11-26 03:17:46 +0000688 if (!num_smpls) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200689 LOGC(DDEV, ERROR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000690
691 switch (md.error_code) {
692 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Harald Welte5cc88582018-08-17 19:55:38 +0200693 LOGC(DDEV, ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800694 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000695 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
696 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
697 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
698 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
699 default:
700 return ERROR_UNHANDLED;
701 }
702 }
703
kurtis.heimerl965e7572011-11-26 03:16:54 +0000704 // Missing timestamp
705 if (!md.has_time_spec) {
Harald Welte5cc88582018-08-17 19:55:38 +0200706 LOGC(DDEV, ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000707 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000708 }
709
710 // Monotonicity check
Tom Tsoua93f7892017-03-07 17:54:06 -0800711 if (md.time_spec < prev_ts) {
Harald Welte5cc88582018-08-17 19:55:38 +0200712 LOGC(DDEV, ALERT) << "UHD: Loss of monotonic time";
713 LOGC(DDEV, ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
ttsou724eb362012-03-13 02:20:01 +0000714 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000715 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000716 }
717
Tom Tsoua93f7892017-03-07 17:54:06 -0800718 // Workaround for UHD tick rounding bug
719 TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
720 if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
721 md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
722
723 prev_ts = md.time_spec;
724
kurtis.heimerl965e7572011-11-26 03:16:54 +0000725 return 0;
726}
727
Thomas Tsou204a9f12013-10-29 18:34:16 -0400728int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
Pau Espin Pedrolf8c0c462020-03-12 19:08:46 +0100729 TIMESTAMP timestamp, bool *underrun)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000730{
731 ssize_t rc;
732 uhd::time_spec_t ts;
733 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000734
Thomas Tsou204a9f12013-10-29 18:34:16 -0400735 if (bufs.size() != chans) {
Harald Welte5cc88582018-08-17 19:55:38 +0200736 LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400737 return -1;
738 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000739
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000740 *overrun = false;
741 *underrun = false;
742
kurtis.heimerl965e7572011-11-26 03:16:54 +0000743 // Shift read time with respect to transmit clock
744 timestamp += ts_offset;
745
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700746 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
Harald Welte5cc88582018-08-17 19:55:38 +0200747 LOGC(DDEV, DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000748
749 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400750 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000751 if (rc < 0) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200752 LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc);
753 LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000754 return 0;
755 }
756
757 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400758 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800759 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400760 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
761 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800762 thread_enable_cancel(true);
763
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100764 // Check for errors
kurtis.heimerld4be0742011-11-26 03:17:46 +0000765 rc = check_rx_md_err(metadata, num_smpls);
766 switch (rc) {
767 case ERROR_UNRECOVERABLE:
Harald Welte5cc88582018-08-17 19:55:38 +0200768 LOGC(DDEV, ALERT) << "UHD: Version " << uhd::get_version_string();
769 LOGC(DDEV, ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000770 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800771 case ERROR_TIMEOUT:
772 // Assume stopping condition
773 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000774 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -0400775 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000776 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000777 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000778 }
779
kurtis.heimerl965e7572011-11-26 03:16:54 +0000780 ts = metadata.time_spec;
Harald Welte5cc88582018-08-17 19:55:38 +0200781 LOGC(DDEV, DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000782
Thomas Tsou204a9f12013-10-29 18:34:16 -0400783 for (size_t i = 0; i < rx_buffers.size(); i++) {
784 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
785 num_smpls,
Pau Espin Pedrol0a2a40f2019-12-19 13:52:57 +0100786 ts.to_ticks(rx_rate));
kurtis.heimerl965e7572011-11-26 03:16:54 +0000787
Thomas Tsou204a9f12013-10-29 18:34:16 -0400788 // Continue on local overrun, exit on other errors
789 if ((rc < 0)) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200790 LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
791 LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400792 if (rc != smpl_buf::ERROR_OVERFLOW)
793 return 0;
794 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000795 }
796 }
797
798 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -0400799 for (size_t i = 0; i < rx_buffers.size(); i++) {
800 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
801 if ((rc < 0) || (rc != len)) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200802 LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
803 LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400804 return 0;
805 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000806 }
807
808 return len;
809}
810
Thomas Tsou204a9f12013-10-29 18:34:16 -0400811int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
Pau Espin Pedroldfc6e5f2020-03-12 19:35:33 +0100812 unsigned long long timestamp)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000813{
814 uhd::tx_metadata_t metadata;
815 metadata.has_time_spec = true;
816 metadata.start_of_burst = false;
817 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700818 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000819
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000820 *underrun = false;
821
Thomas Tsou204a9f12013-10-29 18:34:16 -0400822 if (bufs.size() != chans) {
Harald Welte5cc88582018-08-17 19:55:38 +0200823 LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400824 return -1;
825 }
826
kurtis.heimerl965e7572011-11-26 03:16:54 +0000827 // Drop a fixed number of packets (magic value)
828 if (!aligned) {
829 drop_cnt++;
830
831 if (drop_cnt == 1) {
Harald Welte5cc88582018-08-17 19:55:38 +0200832 LOGC(DDEV, DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000833 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000834 metadata.end_of_burst = true;
835 } else if (drop_cnt < 30) {
Harald Welte5cc88582018-08-17 19:55:38 +0200836 LOGC(DDEV, DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000837 return len;
838 } else {
Harald Welte5cc88582018-08-17 19:55:38 +0200839 LOGC(DDEV, DEBUG) << "Aligning transmitter: start burst";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000840 metadata.start_of_burst = true;
841 aligned = true;
842 drop_cnt = 0;
843 }
844 }
845
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800846 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400847 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800848 thread_enable_cancel(true);
849
ttsoub371ed52012-01-09 18:11:34 +0000850 if (num_smpls != (unsigned) len) {
Harald Welte5cc88582018-08-17 19:55:38 +0200851 LOGC(DDEV, ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +0000852 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000853
854 return num_smpls;
855}
856
857bool uhd_device::updateAlignment(TIMESTAMP timestamp)
858{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000859 return true;
860}
861
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500862uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
863{
864 double rf_spread, rf_freq;
865 std::vector<double> freqs;
866 uhd::tune_request_t treq(freq);
867
Alexander Chemeris90f7a012015-04-09 18:55:02 +0300868 if (dev_type == UMTRX) {
Harald Welte61707e82018-06-13 23:21:57 +0200869 if (lo_offset != 0.0)
870 return uhd::tune_request_t(freq, lo_offset);
Alexander Chemeris90f7a012015-04-09 18:55:02 +0300871
872 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
873 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
874 // only distort the signal (because cordic is not ideal).
875 treq.target_freq = freq;
876 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
877 treq.rf_freq = freq;
878 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
879 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +0300880 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +0300881 } else if (chans == 1) {
Harald Welte61707e82018-06-13 23:21:57 +0200882 if (lo_offset == 0.0)
Thomas Tsou8e17df72014-03-06 14:16:11 -0500883 return treq;
884
Harald Welte61707e82018-06-13 23:21:57 +0200885 return uhd::tune_request_t(freq, lo_offset);
Thomas Tsou8e17df72014-03-06 14:16:11 -0500886 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Harald Welte5cc88582018-08-17 19:55:38 +0200887 LOGC(DDEV, ALERT) << chans << " channels unsupported";
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500888 return treq;
889 }
890
891 if (tx)
892 freqs = tx_freqs;
893 else
894 freqs = rx_freqs;
895
896 /* Tune directly if other channel isn't tuned */
897 if (freqs[!chan] < 10.0)
898 return treq;
899
900 /* Find center frequency between channels */
901 rf_spread = fabs(freqs[!chan] - freq);
Tom Tsouf6115692017-06-26 10:13:25 -0700902 if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
Harald Welte5cc88582018-08-17 19:55:38 +0200903 LOGC(DDEV, ALERT) << rf_spread << "Hz tuning spread not supported\n";
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500904 return treq;
905 }
906
907 rf_freq = (freqs[!chan] + freq) / 2.0f;
908
909 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
910 treq.target_freq = freq;
911 treq.rf_freq = rf_freq;
912
913 return treq;
914}
915
916bool uhd_device::set_freq(double freq, size_t chan, bool tx)
917{
918 std::vector<double> freqs;
919 uhd::tune_result_t tres;
920 uhd::tune_request_t treq = select_freq(freq, chan, tx);
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +0100921 std::string str_dir;
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500922
923 if (tx) {
924 tres = usrp_dev->set_tx_freq(treq, chan);
925 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +0100926 str_dir = "Tx";
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500927 } else {
928 tres = usrp_dev->set_rx_freq(treq, chan);
929 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +0100930 str_dir = "Rx";
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500931 }
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +0100932 LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500933
Thomas Tsou8e17df72014-03-06 14:16:11 -0500934 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
935 return true;
936
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500937 /* Manual RF policy means we intentionally tuned with a baseband
938 * offset for dual-channel purposes. Now retune the other channel
939 * with the opposite corresponding frequency offset
940 */
941 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
942 if (tx) {
943 treq = select_freq(tx_freqs[!chan], !chan, true);
944 tres = usrp_dev->set_tx_freq(treq, !chan);
945 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
946 } else {
947 treq = select_freq(rx_freqs[!chan], !chan, false);
948 tres = usrp_dev->set_rx_freq(treq, !chan);
949 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
950
951 }
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +0100952 LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500953 }
954
955 return true;
956}
957
Thomas Tsou204a9f12013-10-29 18:34:16 -0400958bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000959{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400960 if (chan >= tx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200961 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400962 return false;
963 }
Tom Tsou93b7f372014-12-15 20:23:33 -0800964 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400965
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500966 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000967}
968
Thomas Tsou204a9f12013-10-29 18:34:16 -0400969bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000970{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400971 if (chan >= rx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200972 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400973 return false;
974 }
Tom Tsou93b7f372014-12-15 20:23:33 -0800975 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400976
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500977 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000978}
979
Thomas Tsou204a9f12013-10-29 18:34:16 -0400980double uhd_device::getTxFreq(size_t chan)
981{
982 if (chan >= tx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200983 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400984 return 0.0;
985 }
986
987 return tx_freqs[chan];
988}
989
990double uhd_device::getRxFreq(size_t chan)
991{
992 if (chan >= rx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200993 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400994 return 0.0;
995 }
996
997 return rx_freqs[chan];
998}
999
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001000bool uhd_device::setRxAntenna(const std::string &ant, size_t chan)
1001{
1002 std::vector<std::string> avail;
1003 if (chan >= rx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001004 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001005 return false;
1006 }
1007
1008 avail = usrp_dev->get_rx_antennas(chan);
1009 if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001010 LOGC(DDEV, ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan;
1011 LOGC(DDEV, INFO) << "Available Rx antennas: ";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001012 for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
Harald Welte5cc88582018-08-17 19:55:38 +02001013 LOGC(DDEV, INFO) << "- '" << *i << "'";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001014 return false;
1015 }
1016 usrp_dev->set_rx_antenna(ant, chan);
1017 rx_paths[chan] = usrp_dev->get_rx_antenna(chan);
1018
1019 if (ant != rx_paths[chan]) {
Harald Welte5cc88582018-08-17 19:55:38 +02001020 LOGC(DDEV, ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan];
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001021 return false;
1022 }
1023
1024 return true;
1025}
1026
1027std::string uhd_device::getRxAntenna(size_t chan)
1028{
1029 if (chan >= rx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001030 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001031 return "";
1032 }
1033 return usrp_dev->get_rx_antenna(chan);
1034}
1035
1036bool uhd_device::setTxAntenna(const std::string &ant, size_t chan)
1037{
1038 std::vector<std::string> avail;
1039 if (chan >= tx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001040 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001041 return false;
1042 }
1043
1044 avail = usrp_dev->get_tx_antennas(chan);
1045 if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001046 LOGC(DDEV, ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan;
1047 LOGC(DDEV, INFO) << "Available Tx antennas: ";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001048 for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
Harald Welte5cc88582018-08-17 19:55:38 +02001049 LOGC(DDEV, INFO) << "- '" << *i << "'";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001050 return false;
1051 }
1052 usrp_dev->set_tx_antenna(ant, chan);
1053 tx_paths[chan] = usrp_dev->get_tx_antenna(chan);
1054
1055 if (ant != tx_paths[chan]) {
Harald Welte5cc88582018-08-17 19:55:38 +02001056 LOGC(DDEV, ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan];
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001057 return false;
1058 }
1059
1060 return true;
1061}
1062
1063std::string uhd_device::getTxAntenna(size_t chan)
1064{
1065 if (chan >= tx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001066 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001067 return "";
1068 }
1069 return usrp_dev->get_tx_antenna(chan);
1070}
1071
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +02001072bool uhd_device::requiresRadioAlign()
1073{
1074 return false;
1075}
1076
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001077GSM::Time uhd_device::minLatency() {
1078 /* Empirical data from a handful of
1079 relatively recent machines shows that the B100 will underrun when
1080 the transmit threshold is reduced to a time of 6 and a half frames,
1081 so we set a minimum 7 frame threshold. */
1082 return GSM::Time(6,7);
1083}
1084
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001085/*
1086 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1087 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1088 * combination.
1089 */
1090TIMESTAMP uhd_device::initialWriteTimestamp()
1091{
Tom Tsou76764272016-06-24 14:25:39 -07001092 if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001093 return ts_initial;
1094 else
1095 return ts_initial * tx_sps;
1096}
1097
1098TIMESTAMP uhd_device::initialReadTimestamp()
1099{
1100 return ts_initial;
1101}
1102
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001103double uhd_device::fullScaleInputValue()
1104{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001105 if (dev_type == LIMESDR)
ignasj28d80812017-06-13 23:37:46 +03001106 return (double) SHRT_MAX * LIMESDR_TX_AMPL;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001107 if (dev_type == UMTRX)
1108 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1109 else
1110 return (double) SHRT_MAX * USRP_TX_AMPL;
1111}
1112
1113double uhd_device::fullScaleOutputValue()
1114{
1115 return (double) SHRT_MAX;
1116}
1117
kurtis.heimerl965e7572011-11-26 03:16:54 +00001118bool uhd_device::recv_async_msg()
1119{
ttsou60dc4c92012-08-08 23:30:23 +00001120 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001121
1122 thread_enable_cancel(false);
1123 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1124 thread_enable_cancel(true);
1125 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001126 return false;
1127
1128 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001129 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001130 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001131
1132 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1133 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +02001134 LOGC(DDEV, ERROR) << str_code(md);
ttsou60dc4c92012-08-08 23:30:23 +00001135 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001136 }
1137
1138 return true;
1139}
1140
1141std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1142{
1143 std::ostringstream ost("UHD: ");
1144
1145 switch (metadata.error_code) {
1146 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1147 ost << "No error";
1148 break;
1149 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1150 ost << "No packet received, implementation timed-out";
1151 break;
1152 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1153 ost << "A stream command was issued in the past";
1154 break;
1155 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1156 ost << "Expected another stream command";
1157 break;
1158 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1159 ost << "An internal receive buffer has filled";
1160 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001161 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1162 ost << "Multi-channel alignment failed";
1163 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001164 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1165 ost << "The packet could not be parsed";
1166 break;
1167 default:
1168 ost << "Unknown error " << metadata.error_code;
1169 }
1170
1171 if (metadata.has_time_spec)
1172 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1173
1174 return ost.str();
1175}
1176
1177std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1178{
1179 std::ostringstream ost("UHD: ");
1180
1181 switch (metadata.event_code) {
1182 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1183 ost << "A packet was successfully transmitted";
1184 break;
1185 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1186 ost << "An internal send buffer has emptied";
1187 break;
1188 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1189 ost << "Packet loss between host and device";
1190 break;
1191 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1192 ost << "Packet time was too late or too early";
1193 break;
1194 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1195 ost << "Underflow occurred inside a packet";
1196 break;
1197 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1198 ost << "Packet loss within a burst";
1199 break;
1200 default:
1201 ost << "Unknown error " << metadata.event_code;
1202 }
1203
1204 if (metadata.has_time_spec)
1205 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1206
1207 return ost.str();
1208}
1209
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001210RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
Harald Welte61707e82018-06-13 23:21:57 +02001211 InterfaceType iface, size_t chans, double lo_offset,
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001212 const std::vector<std::string>& tx_paths,
1213 const std::vector<std::string>& rx_paths)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001214{
Harald Welte61707e82018-06-13 23:21:57 +02001215 return new uhd_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001216}