blob: 95ea8e71ddfa3e719cda95406ff5b35b89fca88d [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 Pedrol056ce132020-06-08 12:13:11 +020036extern "C" {
Pau Espin Pedrol553a2502020-07-29 18:05:25 +020037#include <osmocom/core/utils.h>
Pau Espin Pedrol056ce132020-06-08 12:13:11 +020038#include <osmocom/gsm/gsm_utils.h>
Pau Espin Pedrol553a2502020-07-29 18:05:25 +020039#include <osmocom/vty/cpu_sched_vty.h>
Pau Espin Pedrol056ce132020-06-08 12:13:11 +020040}
41
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +010042#ifdef USE_UHD_3_11
43#include <uhd/utils/log_add.hpp>
44#include <uhd/utils/thread.hpp>
45#else
Tom Tsou72bf7622017-03-07 14:16:46 -080046#include <uhd/utils/msg.hpp>
Pau Espin Pedrol1f4a0092018-08-30 17:24:49 +020047#include <uhd/utils/thread_priority.hpp>
Tom Tsou72bf7622017-03-07 14:16:46 -080048#endif
49
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +030050#define USRP_TX_AMPL 0.3
51#define UMTRX_TX_AMPL 0.7
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -080052#define LIMESDR_TX_AMPL 0.3
Thomas Tsoue3e88142013-04-05 20:42:41 -040053#define SAMPLE_BUF_SZ (1 << 20)
Thomas Tsou02d88d12013-04-05 15:36:30 -040054
Tom Tsoueb54bdd2014-11-25 16:06:32 -080055/*
56 * UHD timeout value on streaming (re)start
57 *
58 * Allow some time for streaming to commence after the start command is issued,
59 * but consider a wait beyond one second to be a definite error condition.
60 */
61#define UHD_RESTART_TIMEOUT 1.0
62
Alexander Chemeris4d029d82014-04-19 17:25:00 +040063/*
64 * UmTRX specific settings
65 */
66#define UMTRX_VGA1_DEF -18
67
kurtis.heimerl965e7572011-11-26 03:16:54 +000068/*
Tom Tsouc3129052015-08-21 18:28:52 -070069 * USRP version dependent device timings
70 */
Tom Tsou72bf7622017-03-07 14:16:46 -080071#if defined(USE_UHD_3_9) || defined(USE_UHD_3_11)
Tom Tsouc3129052015-08-21 18:28:52 -070072#define B2XX_TIMING_1SPS 1.7153e-4
73#define B2XX_TIMING_4SPS 1.1696e-4
Tom Tsoud2b07032016-04-26 19:28:59 -070074#define B2XX_TIMING_4_4SPS 6.18462e-5
Tom Tsoua93f7892017-03-07 17:54:06 -080075#define B2XX_TIMING_MCBTS 7e-5
Tom Tsou80cb0802017-01-19 13:44:02 -080076#else
77#define B2XX_TIMING_1SPS 9.9692e-5
78#define B2XX_TIMING_4SPS 6.9248e-5
79#define B2XX_TIMING_4_4SPS 4.52308e-5
Tom Tsoua93f7892017-03-07 17:54:06 -080080#define B2XX_TIMING_MCBTS 6.42452e-5
Tom Tsou80cb0802017-01-19 13:44:02 -080081#endif
Tom Tsouc3129052015-08-21 18:28:52 -070082
83/*
Thomas Tsoue3e88142013-04-05 20:42:41 -040084 * Tx / Rx sample offset values. In a perfect world, there is no group delay
85 * though analog components, and behaviour through digital filters exactly
86 * matches calculated values. In reality, there are unaccounted factors,
87 * which are captured in these empirically measured (using a loopback test)
88 * timing correction values.
89 *
90 * Notes:
91 * USRP1 with timestamps is not supported by UHD.
92 */
Tom Tsou1fb0ce62017-06-08 19:41:48 -070093
Ericc0f78a32023-05-12 13:00:14 +020094static const dev_map_t dev_param_map {
Tom Tsou1fb0ce62017-06-08 19:41:48 -070095 { std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } },
96 { std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } },
97 { std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } },
98 { std::make_tuple(B100, 1, 1), { 1, 0.0, 400000, 1.2104e-4, "B100 1 SPS" } },
99 { std::make_tuple(B100, 4, 1), { 1, 0.0, 400000, 7.9307e-5, "B100 4/1 Tx/Rx SPS" } },
100 { std::make_tuple(B200, 1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS" } },
101 { std::make_tuple(B200, 4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } },
102 { std::make_tuple(B200, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
103 { std::make_tuple(B210, 1, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B210 1 SPS" } },
104 { std::make_tuple(B210, 4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" } },
105 { std::make_tuple(B210, 4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B210 4 SPS" } },
106 { std::make_tuple(E1XX, 1, 1), { 1, 52e6, GSMRATE, 9.5192e-5, "E1XX 1 SPS" } },
107 { std::make_tuple(E1XX, 4, 1), { 1, 52e6, GSMRATE, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" } },
108 { std::make_tuple(E3XX, 1, 1), { 2, 26e6, GSMRATE, 1.8462e-4, "E3XX 1 SPS" } },
109 { std::make_tuple(E3XX, 4, 1), { 2, 26e6, GSMRATE, 1.2923e-4, "E3XX 4/1 Tx/Rx SPS" } },
110 { std::make_tuple(X3XX, 1, 1), { 2, 0.0, 390625, 1.5360e-4, "X3XX 1 SPS" } },
111 { std::make_tuple(X3XX, 4, 1), { 2, 0.0, 390625, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS" } },
112 { std::make_tuple(X3XX, 4, 4), { 2, 0.0, 390625, 5.6567e-5, "X3XX 4 SPS" } },
113 { std::make_tuple(UMTRX, 1, 1), { 2, 0.0, GSMRATE, 9.9692e-5, "UmTRX 1 SPS" } },
114 { std::make_tuple(UMTRX, 4, 1), { 2, 0.0, GSMRATE, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS"} },
115 { std::make_tuple(UMTRX, 4, 4), { 2, 0.0, GSMRATE, 5.1503e-5, "UmTRX 4 SPS" } },
Tom Tsou4cafb0f2017-06-26 10:13:25 -0700116 { std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS" } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700117 { std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
118};
Thomas Tsoucb69f082013-04-08 14:18:26 -0400119
Ericc0f78a32023-05-12 13:00:14 +0200120static const power_map_t dev_band_nom_power_param_map {
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200121 { std::make_tuple(B200, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
122 { std::make_tuple(B200, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
123 { std::make_tuple(B200, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
124 { std::make_tuple(B200, GSM_BAND_1900), { 89.75, 7.7, -11.0 } },
125 { std::make_tuple(B210, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
126 { std::make_tuple(B210, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
127 { std::make_tuple(B210, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
128 { std::make_tuple(B210, GSM_BAND_1900), { 89.75, 7.7, -11.0 } },
Pau Espin Pedrol056ce132020-06-08 12:13:11 +0200129};
130
kurtis.heimerl965e7572011-11-26 03:16:54 +0000131void *async_event_loop(uhd_device *dev)
132{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +0200133 set_selfthread_name("UHDAsyncEvent");
Eric Wild1e17c4f2020-03-24 17:19:27 +0100134 osmo_cpu_sched_vty_apply_localthread();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500135
kurtis.heimerl965e7572011-11-26 03:16:54 +0000136 while (1) {
137 dev->recv_async_msg();
138 pthread_testcancel();
139 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500140
141 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000142}
143
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100144#ifdef USE_UHD_3_11
145static void uhd_log_handler(const uhd::log::logging_info &info)
146{
147 int level;
148
149 switch (info.verbosity)
150 {
151 case uhd::log::trace:
152 case uhd::log::debug:
153 level = LOGL_DEBUG;
154 break;
155 case uhd::log::info:
156 level = LOGL_INFO;
157 break;
158 case uhd::log::warning:
159 level = LOGL_NOTICE;
160 break;
161 case uhd::log::error:
162 level = LOGL_ERROR;
163 break;
164 case uhd::log::fatal:
165 level = LOGL_FATAL;
166 break;
167 default:
168 level = LOGL_NOTICE;
169 }
170
171 LOGSRC(DDEVDRV, level, info.file.c_str(), info.line) << "[" << info.component << "] " << info.message;
172}
173#else
Tom Tsou72bf7622017-03-07 14:16:46 -0800174/*
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000175 Catch and drop underrun 'U' and overrun 'O' messages from stdout
176 since we already report using the logging facility. Direct
177 everything else appropriately.
178 */
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100179static void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000180{
181 switch (type) {
182 case uhd::msg::status:
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100183 LOGC(DDEVDRV, INFO) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000184 break;
185 case uhd::msg::warning:
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100186 LOGC(DDEVDRV, NOTICE) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000187 break;
188 case uhd::msg::error:
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100189 LOGC(DDEVDRV, ERROR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000190 break;
191 case uhd::msg::fastpath:
192 break;
193 }
194}
Tom Tsou72bf7622017-03-07 14:16:46 -0800195#endif
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000196
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200197/* So far measurements done for B210 show really close to linear relationship
198 * between gain and real output power, so we simply adjust the measured offset
199 */
200static double TxGain2TxPower(const dev_band_desc &desc, double tx_gain_db)
201{
202 return desc.nom_out_tx_power - (desc.nom_uhd_tx_gain - tx_gain_db);
203}
204static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
205{
206 return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
207}
208
Eric19e134a2023-05-10 23:50:38 +0200209uhd_device::uhd_device(InterfaceType iface, const struct trx_cfg *cfg)
Ericc0f78a32023-05-12 13:00:14 +0200210 : RadioDevice(iface, cfg), band_manager(dev_band_nom_power_param_map, dev_param_map), rx_gain_min(0.0),
211 rx_gain_max(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), drop_cnt(0), prev_ts(0, 0),
Eric19e134a2023-05-10 23:50:38 +0200212 ts_initial(0), ts_offset(0), async_event_thrd(NULL)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000213{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000214}
215
216uhd_device::~uhd_device()
217{
218 stop();
219
Thomas Tsou204a9f12013-10-29 18:34:16 -0400220 for (size_t i = 0; i < rx_buffers.size(); i++)
221 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000222}
223
kurtis.heimerl24481de2011-11-26 03:17:18 +0000224void uhd_device::init_gains()
225{
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200226 double tx_gain_min, tx_gain_max;
kurtis.heimerl24481de2011-11-26 03:17:18 +0000227 uhd::gain_range_t range;
228
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400229 if (dev_type == UMTRX) {
230 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
231 if (gain_stages[0] == "VGA") {
Harald Welte5cc88582018-08-17 19:55:38 +0200232 LOGC(DDEV, WARNING) << "Update your UHD version for a proper Tx gain support";
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400233 }
234 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
235 range = usrp_dev->get_tx_gain_range();
236 tx_gain_min = range.start();
237 tx_gain_max = range.stop();
238 } else {
239 range = usrp_dev->get_tx_gain_range("VGA2");
240 tx_gain_min = UMTRX_VGA1_DEF + range.start();
241 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
242 }
243 } else {
244 range = usrp_dev->get_tx_gain_range();
245 tx_gain_min = range.start();
246 tx_gain_max = range.stop();
247 }
Harald Welte5cc88582018-08-17 19:55:38 +0200248 LOGC(DDEV, INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000249
250 range = usrp_dev->get_rx_gain_range();
251 rx_gain_min = range.start();
252 rx_gain_max = range.stop();
Harald Welte5cc88582018-08-17 19:55:38 +0200253 LOGC(DDEV, INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000254
Thomas Tsou204a9f12013-10-29 18:34:16 -0400255 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400256 double gain = (tx_gain_min + tx_gain_max) / 2;
Harald Welte5cc88582018-08-17 19:55:38 +0200257 LOGC(DDEV, INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400258 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400259 tx_gains[i] = usrp_dev->get_tx_gain(i);
260 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000261
Thomas Tsou204a9f12013-10-29 18:34:16 -0400262 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400263 double gain = (rx_gain_min + rx_gain_max) / 2;
Harald Welte5cc88582018-08-17 19:55:38 +0200264 LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400265 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400266 rx_gains[i] = usrp_dev->get_rx_gain(i);
267 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000268
269 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400270
kurtis.heimerl24481de2011-11-26 03:17:18 +0000271}
272
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700273void uhd_device::set_rates()
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700274{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700275 dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
276 if (desc.mcr != 0.0)
277 usrp_dev->set_master_clock_rate(desc.mcr);
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700278
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700279 tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate;
280 rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate;
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700281
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700282 usrp_dev->set_tx_rate(tx_rate);
283 usrp_dev->set_rx_rate(rx_rate);
284 tx_rate = usrp_dev->get_tx_rate();
285 rx_rate = usrp_dev->get_rx_rate();
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700286
Tom Tsou1b6ab7d2017-06-15 15:31:08 -0700287 ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
Ericc0f78a32023-05-12 13:00:14 +0200288 LOGC(DDEV, INFO) << "Rates configured for " << desc.desc_str;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000289}
290
Thomas Tsou204a9f12013-10-29 18:34:16 -0400291double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000292{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400293 if (chan >= rx_gains.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200294 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400295 return 0.0f;
296 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000297
Thomas Tsou204a9f12013-10-29 18:34:16 -0400298 usrp_dev->set_rx_gain(db, chan);
299 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000300
Harald Welte5cc88582018-08-17 19:55:38 +0200301 LOGC(DDEV, INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400302
303 return rx_gains[chan];
304}
305
306double uhd_device::getRxGain(size_t chan)
307{
308 if (chan >= rx_gains.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200309 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400310 return 0.0f;
311 }
312
313 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000314}
315
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200316double uhd_device::rssiOffset(size_t chan)
317{
318 double rssiOffset;
319 dev_band_desc desc;
320
321 if (chan >= rx_gains.size()) {
322 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
323 return 0.0f;
324 }
325
326 get_dev_band_desc(desc);
327 rssiOffset = rx_gains[chan] + desc.rxgain2rssioffset_rel;
328 return rssiOffset;
329}
330
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200331double uhd_device::setPowerAttenuation(int atten, size_t chan) {
Pau Espin Pedrol58d80a02020-06-19 19:29:59 +0200332 double tx_power, db;
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200333 dev_band_desc desc;
334
335 if (chan >= tx_gains.size()) {
336 LOGC(DDEV, ALERT) << "Requested non-existent channel" << chan;
337 return 0.0f;
338 }
339
340 get_dev_band_desc(desc);
Pau Espin Pedrol58d80a02020-06-19 19:29:59 +0200341 tx_power = desc.nom_out_tx_power - atten;
342 db = TxPower2TxGain(desc, tx_power);
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200343
344 if (dev_type == UMTRX) {
345 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
346 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
347 usrp_dev->set_tx_gain(db, chan);
348 } else {
349 // New UHD versions support split configuration of
350 // Tx gain stages. We utilize this to set the gain
351 // configuration, optimal for the Tx signal quality.
352 // From our measurements, VGA1 must be 18dB plus-minus
353 // one and VGA2 is the best when 23dB or lower.
354 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
355 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
356 }
357 } else {
358 usrp_dev->set_tx_gain(db, chan);
359 }
360
361 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
362
Pau Espin Pedrol58d80a02020-06-19 19:29:59 +0200363 LOGC(DDEV, INFO) << "Set TX gain to " << tx_gains[chan] << "dB, ~"
364 << TxGain2TxPower(desc, tx_gains[chan]) << " dBm "
365 << "(asked for " << db << " dB, ~" << tx_power << " dBm)";
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200366
367 return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
368}
369double uhd_device::getPowerAttenuation(size_t chan) {
370 dev_band_desc desc;
Pau Espin Pedrol2ab92182019-09-13 17:05:02 +0200371 if (chan >= tx_gains.size()) {
372 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
373 return 0.0f;
374 }
375
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200376 get_dev_band_desc(desc);
377 return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
Pau Espin Pedrol2ab92182019-09-13 17:05:02 +0200378}
379
Pau Espin Pedrol0e09e7c2020-05-29 16:39:07 +0200380int uhd_device::getNominalTxPower(size_t chan)
381{
Pau Espin Pedrol056ce132020-06-08 12:13:11 +0200382 dev_band_desc desc;
383 get_dev_band_desc(desc);
384
385 return desc.nom_out_tx_power;
Pau Espin Pedrol0e09e7c2020-05-29 16:39:07 +0200386}
387
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000388/*
389 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400390 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000391 deal with the transport latency. Reject the USRP1 because UHD doesn't
392 support timestamped samples with it.
393 */
394bool uhd_device::parse_dev_type()
395{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700396 uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree();
397 std::string devString = prop_tree->access<std::string>("/name").get();
398 std::string mboardString = usrp_dev->get_mboard_name();
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000399
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700400 const std::map<std::string, std::pair<uhd_dev_type, TxWindowType>> devStringMap {
401 { "B100", { B100, TX_WINDOW_USRP1 } },
402 { "B200", { B200, TX_WINDOW_USRP1 } },
403 { "B200mini", { B200, TX_WINDOW_USRP1 } },
Piotr Krysikaa60dda2017-12-04 00:29:16 +0700404 { "B205mini", { B200, TX_WINDOW_USRP1 } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700405 { "B210", { B210, TX_WINDOW_USRP1 } },
406 { "E100", { E1XX, TX_WINDOW_FIXED } },
407 { "E110", { E1XX, TX_WINDOW_FIXED } },
408 { "E310", { E3XX, TX_WINDOW_FIXED } },
409 { "E3XX", { E3XX, TX_WINDOW_FIXED } },
410 { "X300", { X3XX, TX_WINDOW_FIXED } },
411 { "X310", { X3XX, TX_WINDOW_FIXED } },
Tom Tsou988a4642017-06-15 09:16:53 -0700412 { "USRP2", { USRP2, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700413 { "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
ignasj87ed77b2017-06-13 23:37:46 +0300414 { "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700415 };
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000416
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700417 // Compare UHD motherboard and device strings */
Tom Tsou988a4642017-06-15 09:16:53 -0700418 auto mapIter = devStringMap.begin();
419 while (mapIter != devStringMap.end()) {
420 if (devString.find(mapIter->first) != std::string::npos ||
421 mboardString.find(mapIter->first) != std::string::npos) {
422 dev_type = std::get<0>(mapIter->second);
423 tx_window = std::get<1>(mapIter->second);
424 return true;
425 }
426 mapIter++;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000427 }
428
Harald Welte5cc88582018-08-17 19:55:38 +0200429 LOGC(DDEV, ALERT) << "Unsupported device " << devString;
Tom Tsou988a4642017-06-15 09:16:53 -0700430 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000431}
432
Tom Tsou3b093bb2016-05-03 19:04:15 -0700433/*
434 * Check for UHD version > 3.9.0 for E3XX support
435 */
436static bool uhd_e3xx_version_chk()
437{
438 std::string ver = uhd::get_version_string();
439 std::string major_str(ver.begin(), ver.begin() + 3);
440 std::string minor_str(ver.begin() + 4, ver.begin() + 7);
441
442 int major_val = atoi(major_str.c_str());
443 int minor_val = atoi(minor_str.c_str());
444
445 if (major_val < 3)
446 return false;
447 if (minor_val < 9)
448 return false;
449
450 return true;
451}
452
Tom Tsou980525c2017-06-09 15:37:19 -0700453void uhd_device::set_channels(bool swap)
454{
455 if (iface == MULTI_ARFCN) {
456 if (dev_type != B200 && dev_type != B210)
457 throw std::invalid_argument("Device does not support MCBTS");
458 dev_type = B2XX_MCBTS;
Tom Tsou980525c2017-06-09 15:37:19 -0700459 }
460
Tom Tsouf6115692017-06-26 10:13:25 -0700461 if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
Tom Tsou980525c2017-06-09 15:37:19 -0700462 throw std::invalid_argument("Device does not support number of requested channels");
463
464 std::string subdev_string;
465 switch (dev_type) {
466 case B210:
467 case E3XX:
468 if (chans == 1)
469 subdev_string = swap ? "A:B" : "A:A";
470 else if (chans == 2)
471 subdev_string = swap ? "A:B A:A" : "A:A A:B";
472 break;
473 case X3XX:
474 case UMTRX:
475 if (chans == 1)
476 subdev_string = swap ? "B:0" : "A:0";
477 else if (chans == 2)
478 subdev_string = swap ? "B:0 A:0" : "A:0 B:0";
479 break;
480 default:
481 break;
482 }
483
484 if (!subdev_string.empty()) {
485 uhd::usrp::subdev_spec_t spec(subdev_string);
486 usrp_dev->set_tx_subdev_spec(spec);
487 usrp_dev->set_rx_subdev_spec(spec);
488 }
489}
490
Eric19e134a2023-05-10 23:50:38 +0200491int uhd_device::open()
kurtis.heimerl965e7572011-11-26 03:16:54 +0000492{
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700493 const char *refstr;
Eric5e6b10c2021-05-28 21:20:19 +0200494 int clock_lock_attempts = 15;
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700495
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100496 /* Register msg handler. Different APIs depending on UHD version */
497#ifdef USE_UHD_3_11
498 uhd::log::add_logger("OsmoTRX", &uhd_log_handler);
499 uhd::log::set_log_level(uhd::log::debug);
500 uhd::log::set_console_level(uhd::log::off);
501 uhd::log::set_logger_level("OsmoTRX", uhd::log::debug);
502#else
503 uhd::msg::register_handler(&uhd_msg_handler);
504#endif
505
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000506 // Find UHD devices
Eric19e134a2023-05-10 23:50:38 +0200507 uhd::device_addr_t addr(cfg->dev_args);
ttsouf60dafa2012-10-22 00:07:14 +0000508 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000509 if (dev_addrs.size() == 0) {
Eric19e134a2023-05-10 23:50:38 +0200510 LOGC(DDEV, ALERT) << "No UHD devices found with address '" << cfg->dev_args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400511 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000512 }
513
514 // Use the first found device
Harald Welte5cc88582018-08-17 19:55:38 +0200515 LOGC(DDEV, INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000516 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700517 usrp_dev = uhd::usrp::multi_usrp::make(addr);
d0gtailebb37692018-12-01 17:02:15 +0100518 } catch(uhd::key_error::exception &e) {
Eric19e134a2023-05-10 23:50:38 +0200519 LOGC(DDEV, ALERT) << "UHD make failed, device " << cfg->dev_args << ", exception:\n" << e.what();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400520 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000521 }
522
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000523 // Check for a valid device type and set bus type
524 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400525 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000526
Tom Tsou3b093bb2016-05-03 19:04:15 -0700527 if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200528 LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater";
Tom Tsou3b093bb2016-05-03 19:04:15 -0700529 return -1;
530 }
531
Tom Tsou980525c2017-06-09 15:37:19 -0700532 try {
Eric19e134a2023-05-10 23:50:38 +0200533 set_channels(cfg->swap_channels);
534 } catch (const std::exception &e) {
Harald Welte5cc88582018-08-17 19:55:38 +0200535 LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400536 return -1;
537 }
538
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100539 if (!set_antennas()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200540 LOGC(DDEV, ALERT) << "UHD antenna setting failed";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100541 return -1;
542 }
543
Thomas Tsou204a9f12013-10-29 18:34:16 -0400544 tx_freqs.resize(chans);
545 rx_freqs.resize(chans);
546 tx_gains.resize(chans);
547 rx_gains.resize(chans);
548 rx_buffers.resize(chans);
549
Eric19e134a2023-05-10 23:50:38 +0200550 switch (cfg->clock_ref) {
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700551 case REF_INTERNAL:
552 refstr = "internal";
553 break;
554 case REF_EXTERNAL:
555 refstr = "external";
556 break;
557 case REF_GPS:
558 refstr = "gpsdo";
559 break;
560 default:
Harald Welte5cc88582018-08-17 19:55:38 +0200561 LOGC(DDEV, ALERT) << "Invalid reference type";
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700562 return -1;
563 }
564
565 usrp_dev->set_clock_source(refstr);
Thomas Tsou010fff72013-10-16 00:31:18 -0400566
Eric5e6b10c2021-05-28 21:20:19 +0200567 std::vector<std::string> sensor_names = usrp_dev->get_mboard_sensor_names();
568 if (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end()) {
569 LOGC(DDEV, INFO) << "Waiting for clock reference lock (max " << clock_lock_attempts << "s)..." << std::flush;
570 while (!usrp_dev->get_mboard_sensor("ref_locked", 0).to_bool() && clock_lock_attempts--)
571 sleep(1);
572
573 if (!clock_lock_attempts) {
574 LOGC(DDEV, ALERT) << "Locking to external 10Mhz failed!";
575 return -1;
576 }
577 }
578 LOGC(DDEV, INFO) << "Selected clock source is " << usrp_dev->get_clock_source(0);
579
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700580 try {
581 set_rates();
582 } catch (const std::exception &e) {
Harald Welte5cc88582018-08-17 19:55:38 +0200583 LOGC(DDEV, ALERT) << "UHD rate setting failed - " << e.what();
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500584 return -1;
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700585 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000586
Alexander Chemerise1714252015-04-28 23:09:02 -0400587 // Set RF frontend bandwidth
588 if (dev_type == UMTRX) {
589 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
590 for (size_t i = 0; i < chans; i++) {
591 usrp_dev->set_tx_bandwidth(500*1000*2, i);
Tom Tsoud6ae8642017-03-30 17:22:58 -0700592 usrp_dev->set_rx_bandwidth(500*1000*2, i);
Alexander Chemerise1714252015-04-28 23:09:02 -0400593 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800594 } else if (dev_type == LIMESDR) {
595 for (size_t i = 0; i < chans; i++) {
Pau Espin Pedrol55df1e42018-05-08 20:49:00 +0200596 usrp_dev->set_tx_bandwidth(5.2e6, i);
597 usrp_dev->set_rx_bandwidth(1.4001e6, i);
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800598 }
Alexander Chemerise1714252015-04-28 23:09:02 -0400599 }
600
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400601 /* Create TX and RX streamers */
602 uhd::stream_args_t stream_args("sc16");
603 for (size_t i = 0; i < chans; i++)
604 stream_args.channels.push_back(i);
605
606 tx_stream = usrp_dev->get_tx_stream(stream_args);
607 rx_stream = usrp_dev->get_rx_stream(stream_args);
608
609 /* Number of samples per over-the-wire packet */
610 tx_spp = tx_stream->get_max_num_samps();
611 rx_spp = rx_stream->get_max_num_samps();
612
kurtis.heimerl965e7572011-11-26 03:16:54 +0000613 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400614 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400615 for (size_t i = 0; i < rx_buffers.size(); i++)
Pau Espin Pedrol580c48b2019-05-03 14:38:36 +0200616 rx_buffers[i] = new smpl_buf(buf_len);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000617
Pau Espin Pedrolef192d32019-04-30 18:51:00 +0200618 // Create vector buffer
619 pkt_bufs = std::vector<std::vector<short> >(chans, std::vector<short>(2 * rx_spp));
620 for (size_t i = 0; i < pkt_bufs.size(); i++)
621 pkt_ptrs.push_back(&pkt_bufs[i].front());
622
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100623 // Initialize and shadow gain values
kurtis.heimerl02d04052011-11-26 03:17:10 +0000624 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000625
kurtis.heimerl965e7572011-11-26 03:16:54 +0000626 // Print configuration
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +0100627 LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000628
Tom Tsou76764272016-06-24 14:25:39 -0700629 if (iface == MULTI_ARFCN)
630 return MULTI_ARFCN;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500631
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400632 switch (dev_type) {
633 case B100:
634 return RESAMP_64M;
635 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800636 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400637 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500638 case B200:
639 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100640 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800641 case E3XX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800642 case LIMESDR:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500643 default:
644 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400645 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400646
647 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000648}
649
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000650bool uhd_device::flush_recv(size_t num_pkts)
651{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000652 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000653 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800654 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000655
Thomas Tsou18d3b832014-02-13 14:55:23 -0500656 ts_initial = 0;
657 while (!ts_initial || (num_pkts-- > 0)) {
658 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400659 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000660 if (!num_smpls) {
661 switch (md.error_code) {
662 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Harald Welte5cc88582018-08-17 19:55:38 +0200663 LOGC(DDEV, ALERT) << "Device timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800664 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000665 default:
666 continue;
667 }
668 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500669
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700670 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000671 }
672
Harald Welte5cc88582018-08-17 19:55:38 +0200673 LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
Thomas Tsou18d3b832014-02-13 14:55:23 -0500674
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000675 return true;
676}
677
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800678bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000679{
Thomas Tsouc2839162014-03-27 13:52:58 -0400680 /* Allow 100 ms delay to align multi-channel streams */
681 double delay = 0.1;
682
kurtis.heimerl68292102011-11-26 03:17:28 +0000683 aligned = false;
684
Thomas Tsouc2839162014-03-27 13:52:58 -0400685 uhd::time_spec_t current = usrp_dev->get_time_now();
686
Thomas Tsou204a9f12013-10-29 18:34:16 -0400687 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400688 cmd.stream_now = false;
689 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400690
kurtis.heimerl68292102011-11-26 03:17:28 +0000691 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500692
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800693 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000694}
695
kurtis.heimerl965e7572011-11-26 03:16:54 +0000696bool uhd_device::start()
697{
Harald Welte5cc88582018-08-17 19:55:38 +0200698 LOGC(DDEV, INFO) << "Starting USRP...";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000699
700 if (started) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200701 LOGC(DDEV, ERROR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000702 return false;
703 }
704
kurtis.heimerl965e7572011-11-26 03:16:54 +0000705 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800706 async_event_thrd = new Thread();
707 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000708
709 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800710 if (!restart())
711 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000712
kurtis.heimerl965e7572011-11-26 03:16:54 +0000713 // Display usrp time
714 double time_now = usrp_dev->get_time_now().get_real_secs();
Harald Welte5cc88582018-08-17 19:55:38 +0200715 LOGC(DDEV, INFO) << "The current time is " << time_now << " seconds";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000716
717 started = true;
718 return true;
719}
720
721bool uhd_device::stop()
722{
Thomas Tsou2c791102013-11-15 23:00:28 -0500723 if (!started)
724 return false;
725
726 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000727 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
728
729 usrp_dev->issue_stream_cmd(stream_cmd);
730
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800731 async_event_thrd->cancel();
732 async_event_thrd->join();
733 delete async_event_thrd;
734
Eric4080eb72020-07-16 18:08:30 +0200735 /* reset internal buffer timestamps */
736 for (size_t i = 0; i < rx_buffers.size(); i++)
737 rx_buffers[i]->reset();
738
Ericc0f78a32023-05-12 13:00:14 +0200739 band_reset();
Pau Espin Pedrolbb2cb9d2021-09-21 13:52:46 +0200740
kurtis.heimerl965e7572011-11-26 03:16:54 +0000741 started = false;
742 return true;
743}
744
kurtis.heimerld4be0742011-11-26 03:17:46 +0000745int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000746{
kurtis.heimerld4be0742011-11-26 03:17:46 +0000747 if (!num_smpls) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200748 LOGC(DDEV, ERROR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000749
750 switch (md.error_code) {
751 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Harald Welte5cc88582018-08-17 19:55:38 +0200752 LOGC(DDEV, ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800753 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000754 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
755 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
756 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
757 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
758 default:
759 return ERROR_UNHANDLED;
760 }
761 }
762
kurtis.heimerl965e7572011-11-26 03:16:54 +0000763 // Missing timestamp
764 if (!md.has_time_spec) {
Harald Welte5cc88582018-08-17 19:55:38 +0200765 LOGC(DDEV, ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000766 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000767 }
768
769 // Monotonicity check
Tom Tsoua93f7892017-03-07 17:54:06 -0800770 if (md.time_spec < prev_ts) {
Harald Welte5cc88582018-08-17 19:55:38 +0200771 LOGC(DDEV, ALERT) << "UHD: Loss of monotonic time";
772 LOGC(DDEV, ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
ttsou724eb362012-03-13 02:20:01 +0000773 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000774 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000775 }
776
Tom Tsoua93f7892017-03-07 17:54:06 -0800777 // Workaround for UHD tick rounding bug
778 TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
779 if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
780 md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
781
782 prev_ts = md.time_spec;
783
kurtis.heimerl965e7572011-11-26 03:16:54 +0000784 return 0;
785}
786
Thomas Tsou204a9f12013-10-29 18:34:16 -0400787int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
Pau Espin Pedrolf8c0c462020-03-12 19:08:46 +0100788 TIMESTAMP timestamp, bool *underrun)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000789{
790 ssize_t rc;
791 uhd::time_spec_t ts;
792 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000793
Thomas Tsou204a9f12013-10-29 18:34:16 -0400794 if (bufs.size() != chans) {
Harald Welte5cc88582018-08-17 19:55:38 +0200795 LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400796 return -1;
797 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000798
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000799 *overrun = false;
800 *underrun = false;
801
kurtis.heimerl965e7572011-11-26 03:16:54 +0000802 // Shift read time with respect to transmit clock
803 timestamp += ts_offset;
804
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700805 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
Harald Welte5cc88582018-08-17 19:55:38 +0200806 LOGC(DDEV, DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000807
808 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400809 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000810 if (rc < 0) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200811 LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc);
812 LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000813 return 0;
814 }
815
816 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400817 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800818 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400819 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
820 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800821 thread_enable_cancel(true);
822
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100823 // Check for errors
kurtis.heimerld4be0742011-11-26 03:17:46 +0000824 rc = check_rx_md_err(metadata, num_smpls);
825 switch (rc) {
826 case ERROR_UNRECOVERABLE:
Harald Welte5cc88582018-08-17 19:55:38 +0200827 LOGC(DDEV, ALERT) << "UHD: Version " << uhd::get_version_string();
828 LOGC(DDEV, ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000829 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800830 case ERROR_TIMEOUT:
831 // Assume stopping condition
832 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000833 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -0400834 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000835 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000836 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000837 }
838
kurtis.heimerl965e7572011-11-26 03:16:54 +0000839 ts = metadata.time_spec;
Harald Welte5cc88582018-08-17 19:55:38 +0200840 LOGC(DDEV, DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000841
Thomas Tsou204a9f12013-10-29 18:34:16 -0400842 for (size_t i = 0; i < rx_buffers.size(); i++) {
843 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
844 num_smpls,
Pau Espin Pedrol0a2a40f2019-12-19 13:52:57 +0100845 ts.to_ticks(rx_rate));
kurtis.heimerl965e7572011-11-26 03:16:54 +0000846
Thomas Tsou204a9f12013-10-29 18:34:16 -0400847 // Continue on local overrun, exit on other errors
848 if ((rc < 0)) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200849 LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
850 LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400851 if (rc != smpl_buf::ERROR_OVERFLOW)
852 return 0;
853 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000854 }
855 }
856
857 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -0400858 for (size_t i = 0; i < rx_buffers.size(); i++) {
859 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
860 if ((rc < 0) || (rc != len)) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200861 LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
862 LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400863 return 0;
864 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000865 }
866
867 return len;
868}
869
Thomas Tsou204a9f12013-10-29 18:34:16 -0400870int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
Pau Espin Pedroldfc6e5f2020-03-12 19:35:33 +0100871 unsigned long long timestamp)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000872{
873 uhd::tx_metadata_t metadata;
874 metadata.has_time_spec = true;
875 metadata.start_of_burst = false;
876 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700877 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000878
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000879 *underrun = false;
880
Thomas Tsou204a9f12013-10-29 18:34:16 -0400881 if (bufs.size() != chans) {
Harald Welte5cc88582018-08-17 19:55:38 +0200882 LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400883 return -1;
884 }
885
kurtis.heimerl965e7572011-11-26 03:16:54 +0000886 // Drop a fixed number of packets (magic value)
887 if (!aligned) {
888 drop_cnt++;
889
890 if (drop_cnt == 1) {
Harald Welte5cc88582018-08-17 19:55:38 +0200891 LOGC(DDEV, DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000892 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000893 metadata.end_of_burst = true;
894 } else if (drop_cnt < 30) {
Harald Welte5cc88582018-08-17 19:55:38 +0200895 LOGC(DDEV, DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000896 return len;
897 } else {
Harald Welte5cc88582018-08-17 19:55:38 +0200898 LOGC(DDEV, DEBUG) << "Aligning transmitter: start burst";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000899 metadata.start_of_burst = true;
900 aligned = true;
901 drop_cnt = 0;
902 }
903 }
904
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800905 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400906 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800907 thread_enable_cancel(true);
908
ttsoub371ed52012-01-09 18:11:34 +0000909 if (num_smpls != (unsigned) len) {
Harald Welte5cc88582018-08-17 19:55:38 +0200910 LOGC(DDEV, ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +0000911 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000912
913 return num_smpls;
914}
915
916bool uhd_device::updateAlignment(TIMESTAMP timestamp)
917{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000918 return true;
919}
920
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500921uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
922{
923 double rf_spread, rf_freq;
924 std::vector<double> freqs;
925 uhd::tune_request_t treq(freq);
926
Alexander Chemeris90f7a012015-04-09 18:55:02 +0300927 if (dev_type == UMTRX) {
Harald Welte61707e82018-06-13 23:21:57 +0200928 if (lo_offset != 0.0)
929 return uhd::tune_request_t(freq, lo_offset);
Alexander Chemeris90f7a012015-04-09 18:55:02 +0300930
931 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
932 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
933 // only distort the signal (because cordic is not ideal).
934 treq.target_freq = freq;
935 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
936 treq.rf_freq = freq;
937 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
938 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +0300939 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +0300940 } else if (chans == 1) {
Harald Welte61707e82018-06-13 23:21:57 +0200941 if (lo_offset == 0.0)
Thomas Tsou8e17df72014-03-06 14:16:11 -0500942 return treq;
943
Harald Welte61707e82018-06-13 23:21:57 +0200944 return uhd::tune_request_t(freq, lo_offset);
Thomas Tsou8e17df72014-03-06 14:16:11 -0500945 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Harald Welte5cc88582018-08-17 19:55:38 +0200946 LOGC(DDEV, ALERT) << chans << " channels unsupported";
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500947 return treq;
948 }
949
950 if (tx)
951 freqs = tx_freqs;
952 else
953 freqs = rx_freqs;
954
955 /* Tune directly if other channel isn't tuned */
956 if (freqs[!chan] < 10.0)
957 return treq;
958
959 /* Find center frequency between channels */
960 rf_spread = fabs(freqs[!chan] - freq);
Tom Tsouf6115692017-06-26 10:13:25 -0700961 if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
Harald Welte5cc88582018-08-17 19:55:38 +0200962 LOGC(DDEV, ALERT) << rf_spread << "Hz tuning spread not supported\n";
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500963 return treq;
964 }
965
966 rf_freq = (freqs[!chan] + freq) / 2.0f;
967
968 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
969 treq.target_freq = freq;
970 treq.rf_freq = rf_freq;
971
972 return treq;
973}
974
975bool uhd_device::set_freq(double freq, size_t chan, bool tx)
976{
977 std::vector<double> freqs;
978 uhd::tune_result_t tres;
Ericc0f78a32023-05-12 13:00:14 +0200979 std::string str_dir = tx ? "Tx" : "Rx";
980
981 if (!update_band_from_freq(freq, chan, tx))
982 return false;
983
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500984 uhd::tune_request_t treq = select_freq(freq, chan, tx);
985
986 if (tx) {
987 tres = usrp_dev->set_tx_freq(treq, chan);
988 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
989 } else {
990 tres = usrp_dev->set_rx_freq(treq, chan);
991 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
992 }
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +0100993 LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500994
Thomas Tsou8e17df72014-03-06 14:16:11 -0500995 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
996 return true;
997
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500998 /* Manual RF policy means we intentionally tuned with a baseband
999 * offset for dual-channel purposes. Now retune the other channel
1000 * with the opposite corresponding frequency offset
1001 */
1002 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1003 if (tx) {
1004 treq = select_freq(tx_freqs[!chan], !chan, true);
1005 tres = usrp_dev->set_tx_freq(treq, !chan);
1006 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1007 } else {
1008 treq = select_freq(rx_freqs[!chan], !chan, false);
1009 tres = usrp_dev->set_rx_freq(treq, !chan);
1010 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1011
1012 }
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +01001013 LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001014 }
1015
1016 return true;
1017}
1018
Thomas Tsou204a9f12013-10-29 18:34:16 -04001019bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001020{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001021 if (chan >= tx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001022 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001023 return false;
1024 }
1025
Ericc0f78a32023-05-12 13:00:14 +02001026 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001027}
1028
Thomas Tsou204a9f12013-10-29 18:34:16 -04001029bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001030{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001031 if (chan >= rx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001032 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001033 return false;
1034 }
Pau Espin Pedrol069f5cd2021-09-21 13:50:38 +02001035
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001036 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001037}
1038
Thomas Tsou204a9f12013-10-29 18:34:16 -04001039double uhd_device::getTxFreq(size_t chan)
1040{
1041 if (chan >= tx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001042 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001043 return 0.0;
1044 }
1045
1046 return tx_freqs[chan];
1047}
1048
1049double uhd_device::getRxFreq(size_t chan)
1050{
1051 if (chan >= rx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001052 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001053 return 0.0;
1054 }
1055
1056 return rx_freqs[chan];
1057}
1058
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001059bool uhd_device::setRxAntenna(const std::string &ant, size_t chan)
1060{
1061 std::vector<std::string> avail;
1062 if (chan >= rx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001063 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001064 return false;
1065 }
1066
Vadim Yanitskiy68d8db42020-06-29 17:39:35 +07001067 /* UHD may throw a LookupError/IndexError here (see OS#4636) */
1068 try {
1069 avail = usrp_dev->get_rx_antennas(chan);
1070 } catch (const uhd::index_error &e) {
1071 LOGC(DDEV, ALERT) << "UHD Error: " << e.what();
1072 return false;
1073 }
1074
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001075 if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001076 LOGC(DDEV, ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan;
1077 LOGC(DDEV, INFO) << "Available Rx antennas: ";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001078 for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
Harald Welte5cc88582018-08-17 19:55:38 +02001079 LOGC(DDEV, INFO) << "- '" << *i << "'";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001080 return false;
1081 }
1082 usrp_dev->set_rx_antenna(ant, chan);
1083 rx_paths[chan] = usrp_dev->get_rx_antenna(chan);
1084
1085 if (ant != rx_paths[chan]) {
Harald Welte5cc88582018-08-17 19:55:38 +02001086 LOGC(DDEV, ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan];
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001087 return false;
1088 }
1089
1090 return true;
1091}
1092
1093std::string uhd_device::getRxAntenna(size_t chan)
1094{
1095 if (chan >= rx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001096 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001097 return "";
1098 }
1099 return usrp_dev->get_rx_antenna(chan);
1100}
1101
1102bool uhd_device::setTxAntenna(const std::string &ant, size_t chan)
1103{
1104 std::vector<std::string> avail;
1105 if (chan >= tx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001106 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001107 return false;
1108 }
1109
Vadim Yanitskiy68d8db42020-06-29 17:39:35 +07001110 /* UHD may throw a LookupError/IndexError here (see OS#4636) */
1111 try {
1112 avail = usrp_dev->get_tx_antennas(chan);
1113 } catch (const uhd::index_error &e) {
1114 LOGC(DDEV, ALERT) << "UHD Error: " << e.what();
1115 return false;
1116 }
1117
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001118 if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001119 LOGC(DDEV, ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan;
1120 LOGC(DDEV, INFO) << "Available Tx antennas: ";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001121 for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
Harald Welte5cc88582018-08-17 19:55:38 +02001122 LOGC(DDEV, INFO) << "- '" << *i << "'";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001123 return false;
1124 }
1125 usrp_dev->set_tx_antenna(ant, chan);
1126 tx_paths[chan] = usrp_dev->get_tx_antenna(chan);
1127
1128 if (ant != tx_paths[chan]) {
Harald Welte5cc88582018-08-17 19:55:38 +02001129 LOGC(DDEV, ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan];
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001130 return false;
1131 }
1132
1133 return true;
1134}
1135
1136std::string uhd_device::getTxAntenna(size_t chan)
1137{
1138 if (chan >= tx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001139 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001140 return "";
1141 }
1142 return usrp_dev->get_tx_antenna(chan);
1143}
1144
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +02001145bool uhd_device::requiresRadioAlign()
1146{
1147 return false;
1148}
1149
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001150GSM::Time uhd_device::minLatency() {
1151 /* Empirical data from a handful of
1152 relatively recent machines shows that the B100 will underrun when
1153 the transmit threshold is reduced to a time of 6 and a half frames,
1154 so we set a minimum 7 frame threshold. */
1155 return GSM::Time(6,7);
1156}
1157
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001158/*
1159 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1160 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1161 * combination.
1162 */
1163TIMESTAMP uhd_device::initialWriteTimestamp()
1164{
Tom Tsou76764272016-06-24 14:25:39 -07001165 if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001166 return ts_initial;
1167 else
1168 return ts_initial * tx_sps;
1169}
1170
1171TIMESTAMP uhd_device::initialReadTimestamp()
1172{
1173 return ts_initial;
1174}
1175
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001176double uhd_device::fullScaleInputValue()
1177{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001178 if (dev_type == LIMESDR)
ignasj28d80812017-06-13 23:37:46 +03001179 return (double) SHRT_MAX * LIMESDR_TX_AMPL;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001180 if (dev_type == UMTRX)
1181 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1182 else
1183 return (double) SHRT_MAX * USRP_TX_AMPL;
1184}
1185
1186double uhd_device::fullScaleOutputValue()
1187{
1188 return (double) SHRT_MAX;
1189}
1190
kurtis.heimerl965e7572011-11-26 03:16:54 +00001191bool uhd_device::recv_async_msg()
1192{
ttsou60dc4c92012-08-08 23:30:23 +00001193 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001194
1195 thread_enable_cancel(false);
1196 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1197 thread_enable_cancel(true);
1198 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001199 return false;
1200
1201 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001202 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001203 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001204
1205 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1206 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +02001207 LOGC(DDEV, ERROR) << str_code(md);
ttsou60dc4c92012-08-08 23:30:23 +00001208 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001209 }
1210
1211 return true;
1212}
1213
1214std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1215{
1216 std::ostringstream ost("UHD: ");
1217
1218 switch (metadata.error_code) {
1219 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1220 ost << "No error";
1221 break;
1222 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1223 ost << "No packet received, implementation timed-out";
1224 break;
1225 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1226 ost << "A stream command was issued in the past";
1227 break;
1228 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1229 ost << "Expected another stream command";
1230 break;
1231 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1232 ost << "An internal receive buffer has filled";
1233 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001234 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1235 ost << "Multi-channel alignment failed";
1236 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001237 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1238 ost << "The packet could not be parsed";
1239 break;
1240 default:
1241 ost << "Unknown error " << metadata.error_code;
1242 }
1243
1244 if (metadata.has_time_spec)
1245 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1246
1247 return ost.str();
1248}
1249
1250std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1251{
1252 std::ostringstream ost("UHD: ");
1253
1254 switch (metadata.event_code) {
1255 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1256 ost << "A packet was successfully transmitted";
1257 break;
1258 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1259 ost << "An internal send buffer has emptied";
1260 break;
1261 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1262 ost << "Packet loss between host and device";
1263 break;
1264 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1265 ost << "Packet time was too late or too early";
1266 break;
1267 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1268 ost << "Underflow occurred inside a packet";
1269 break;
1270 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1271 ost << "Packet loss within a burst";
1272 break;
1273 default:
1274 ost << "Unknown error " << metadata.event_code;
1275 }
1276
1277 if (metadata.has_time_spec)
1278 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1279
1280 return ost.str();
1281}
1282
Eric Wild1e17c4f2020-03-24 17:19:27 +01001283#ifndef IPCMAGIC
Eric19e134a2023-05-10 23:50:38 +02001284RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001285{
Eric19e134a2023-05-10 23:50:38 +02001286 return new uhd_device(type, cfg);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001287}
Eric Wild1e17c4f2020-03-24 17:19:27 +01001288#endif