blob: ec772aa5d2ec4bc80ec14c356f3c81f1eb243848 [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
Eric Wildf8c7a522023-05-17 17:44:08 +0200298 if (cfg->overrides.ul_gain_override)
299 return rx_gains[chan];
300
Thomas Tsou204a9f12013-10-29 18:34:16 -0400301 usrp_dev->set_rx_gain(db, chan);
302 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000303
Harald Welte5cc88582018-08-17 19:55:38 +0200304 LOGC(DDEV, INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400305
306 return rx_gains[chan];
307}
308
309double uhd_device::getRxGain(size_t chan)
310{
311 if (chan >= rx_gains.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200312 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400313 return 0.0f;
314 }
315
316 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000317}
318
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200319double uhd_device::rssiOffset(size_t chan)
320{
321 double rssiOffset;
322 dev_band_desc desc;
323
324 if (chan >= rx_gains.size()) {
325 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
326 return 0.0f;
327 }
328
329 get_dev_band_desc(desc);
330 rssiOffset = rx_gains[chan] + desc.rxgain2rssioffset_rel;
331 return rssiOffset;
332}
333
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200334double uhd_device::setPowerAttenuation(int atten, size_t chan) {
Pau Espin Pedrol58d80a02020-06-19 19:29:59 +0200335 double tx_power, db;
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200336 dev_band_desc desc;
337
338 if (chan >= tx_gains.size()) {
339 LOGC(DDEV, ALERT) << "Requested non-existent channel" << chan;
340 return 0.0f;
341 }
342
Eric Wildf8c7a522023-05-17 17:44:08 +0200343 if (cfg->overrides.dl_gain_override)
344 return atten; // ensures caller does not apply digital attenuation
345
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200346 get_dev_band_desc(desc);
Pau Espin Pedrol58d80a02020-06-19 19:29:59 +0200347 tx_power = desc.nom_out_tx_power - atten;
348 db = TxPower2TxGain(desc, tx_power);
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200349
350 if (dev_type == UMTRX) {
351 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
352 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
353 usrp_dev->set_tx_gain(db, chan);
354 } else {
355 // New UHD versions support split configuration of
356 // Tx gain stages. We utilize this to set the gain
357 // configuration, optimal for the Tx signal quality.
358 // From our measurements, VGA1 must be 18dB plus-minus
359 // one and VGA2 is the best when 23dB or lower.
360 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
361 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
362 }
363 } else {
364 usrp_dev->set_tx_gain(db, chan);
365 }
366
367 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
368
Pau Espin Pedrol58d80a02020-06-19 19:29:59 +0200369 LOGC(DDEV, INFO) << "Set TX gain to " << tx_gains[chan] << "dB, ~"
370 << TxGain2TxPower(desc, tx_gains[chan]) << " dBm "
371 << "(asked for " << db << " dB, ~" << tx_power << " dBm)";
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200372
373 return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
374}
375double uhd_device::getPowerAttenuation(size_t chan) {
376 dev_band_desc desc;
Pau Espin Pedrol2ab92182019-09-13 17:05:02 +0200377 if (chan >= tx_gains.size()) {
378 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
379 return 0.0f;
380 }
381
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200382 get_dev_band_desc(desc);
383 return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
Pau Espin Pedrol2ab92182019-09-13 17:05:02 +0200384}
385
Pau Espin Pedrol0e09e7c2020-05-29 16:39:07 +0200386int uhd_device::getNominalTxPower(size_t chan)
387{
Pau Espin Pedrol056ce132020-06-08 12:13:11 +0200388 dev_band_desc desc;
389 get_dev_band_desc(desc);
390
391 return desc.nom_out_tx_power;
Pau Espin Pedrol0e09e7c2020-05-29 16:39:07 +0200392}
393
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000394/*
395 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400396 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000397 deal with the transport latency. Reject the USRP1 because UHD doesn't
398 support timestamped samples with it.
399 */
400bool uhd_device::parse_dev_type()
401{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700402 uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree();
403 std::string devString = prop_tree->access<std::string>("/name").get();
404 std::string mboardString = usrp_dev->get_mboard_name();
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000405
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700406 const std::map<std::string, std::pair<uhd_dev_type, TxWindowType>> devStringMap {
407 { "B100", { B100, TX_WINDOW_USRP1 } },
408 { "B200", { B200, TX_WINDOW_USRP1 } },
409 { "B200mini", { B200, TX_WINDOW_USRP1 } },
Piotr Krysikaa60dda2017-12-04 00:29:16 +0700410 { "B205mini", { B200, TX_WINDOW_USRP1 } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700411 { "B210", { B210, TX_WINDOW_USRP1 } },
412 { "E100", { E1XX, TX_WINDOW_FIXED } },
413 { "E110", { E1XX, TX_WINDOW_FIXED } },
414 { "E310", { E3XX, TX_WINDOW_FIXED } },
415 { "E3XX", { E3XX, TX_WINDOW_FIXED } },
416 { "X300", { X3XX, TX_WINDOW_FIXED } },
417 { "X310", { X3XX, TX_WINDOW_FIXED } },
Tom Tsou988a4642017-06-15 09:16:53 -0700418 { "USRP2", { USRP2, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700419 { "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
ignasj87ed77b2017-06-13 23:37:46 +0300420 { "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700421 };
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000422
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700423 // Compare UHD motherboard and device strings */
Tom Tsou988a4642017-06-15 09:16:53 -0700424 auto mapIter = devStringMap.begin();
425 while (mapIter != devStringMap.end()) {
426 if (devString.find(mapIter->first) != std::string::npos ||
427 mboardString.find(mapIter->first) != std::string::npos) {
428 dev_type = std::get<0>(mapIter->second);
429 tx_window = std::get<1>(mapIter->second);
430 return true;
431 }
432 mapIter++;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000433 }
434
Harald Welte5cc88582018-08-17 19:55:38 +0200435 LOGC(DDEV, ALERT) << "Unsupported device " << devString;
Tom Tsou988a4642017-06-15 09:16:53 -0700436 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000437}
438
Tom Tsou3b093bb2016-05-03 19:04:15 -0700439/*
440 * Check for UHD version > 3.9.0 for E3XX support
441 */
442static bool uhd_e3xx_version_chk()
443{
444 std::string ver = uhd::get_version_string();
445 std::string major_str(ver.begin(), ver.begin() + 3);
446 std::string minor_str(ver.begin() + 4, ver.begin() + 7);
447
448 int major_val = atoi(major_str.c_str());
449 int minor_val = atoi(minor_str.c_str());
450
451 if (major_val < 3)
452 return false;
453 if (minor_val < 9)
454 return false;
455
456 return true;
457}
458
Tom Tsou980525c2017-06-09 15:37:19 -0700459void uhd_device::set_channels(bool swap)
460{
461 if (iface == MULTI_ARFCN) {
462 if (dev_type != B200 && dev_type != B210)
463 throw std::invalid_argument("Device does not support MCBTS");
464 dev_type = B2XX_MCBTS;
Tom Tsou980525c2017-06-09 15:37:19 -0700465 }
466
Tom Tsouf6115692017-06-26 10:13:25 -0700467 if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
Tom Tsou980525c2017-06-09 15:37:19 -0700468 throw std::invalid_argument("Device does not support number of requested channels");
469
470 std::string subdev_string;
471 switch (dev_type) {
472 case B210:
473 case E3XX:
474 if (chans == 1)
475 subdev_string = swap ? "A:B" : "A:A";
476 else if (chans == 2)
477 subdev_string = swap ? "A:B A:A" : "A:A A:B";
478 break;
479 case X3XX:
480 case UMTRX:
481 if (chans == 1)
482 subdev_string = swap ? "B:0" : "A:0";
483 else if (chans == 2)
484 subdev_string = swap ? "B:0 A:0" : "A:0 B:0";
485 break;
486 default:
487 break;
488 }
489
490 if (!subdev_string.empty()) {
491 uhd::usrp::subdev_spec_t spec(subdev_string);
492 usrp_dev->set_tx_subdev_spec(spec);
493 usrp_dev->set_rx_subdev_spec(spec);
494 }
495}
496
Eric19e134a2023-05-10 23:50:38 +0200497int uhd_device::open()
kurtis.heimerl965e7572011-11-26 03:16:54 +0000498{
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700499 const char *refstr;
Eric5e6b10c2021-05-28 21:20:19 +0200500 int clock_lock_attempts = 15;
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700501
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100502 /* Register msg handler. Different APIs depending on UHD version */
503#ifdef USE_UHD_3_11
504 uhd::log::add_logger("OsmoTRX", &uhd_log_handler);
505 uhd::log::set_log_level(uhd::log::debug);
506 uhd::log::set_console_level(uhd::log::off);
507 uhd::log::set_logger_level("OsmoTRX", uhd::log::debug);
508#else
509 uhd::msg::register_handler(&uhd_msg_handler);
510#endif
511
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000512 // Find UHD devices
Eric19e134a2023-05-10 23:50:38 +0200513 uhd::device_addr_t addr(cfg->dev_args);
ttsouf60dafa2012-10-22 00:07:14 +0000514 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000515 if (dev_addrs.size() == 0) {
Eric19e134a2023-05-10 23:50:38 +0200516 LOGC(DDEV, ALERT) << "No UHD devices found with address '" << cfg->dev_args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400517 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000518 }
519
520 // Use the first found device
Harald Welte5cc88582018-08-17 19:55:38 +0200521 LOGC(DDEV, INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000522 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700523 usrp_dev = uhd::usrp::multi_usrp::make(addr);
d0gtailebb37692018-12-01 17:02:15 +0100524 } catch(uhd::key_error::exception &e) {
Eric19e134a2023-05-10 23:50:38 +0200525 LOGC(DDEV, ALERT) << "UHD make failed, device " << cfg->dev_args << ", exception:\n" << e.what();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400526 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000527 }
528
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000529 // Check for a valid device type and set bus type
530 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400531 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000532
Tom Tsou3b093bb2016-05-03 19:04:15 -0700533 if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200534 LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater";
Tom Tsou3b093bb2016-05-03 19:04:15 -0700535 return -1;
536 }
537
Tom Tsou980525c2017-06-09 15:37:19 -0700538 try {
Eric19e134a2023-05-10 23:50:38 +0200539 set_channels(cfg->swap_channels);
540 } catch (const std::exception &e) {
Harald Welte5cc88582018-08-17 19:55:38 +0200541 LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400542 return -1;
543 }
544
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100545 if (!set_antennas()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200546 LOGC(DDEV, ALERT) << "UHD antenna setting failed";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100547 return -1;
548 }
549
Thomas Tsou204a9f12013-10-29 18:34:16 -0400550 tx_freqs.resize(chans);
551 rx_freqs.resize(chans);
552 tx_gains.resize(chans);
553 rx_gains.resize(chans);
554 rx_buffers.resize(chans);
555
Eric19e134a2023-05-10 23:50:38 +0200556 switch (cfg->clock_ref) {
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700557 case REF_INTERNAL:
558 refstr = "internal";
559 break;
560 case REF_EXTERNAL:
561 refstr = "external";
562 break;
563 case REF_GPS:
564 refstr = "gpsdo";
565 break;
566 default:
Harald Welte5cc88582018-08-17 19:55:38 +0200567 LOGC(DDEV, ALERT) << "Invalid reference type";
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700568 return -1;
569 }
570
571 usrp_dev->set_clock_source(refstr);
Thomas Tsou010fff72013-10-16 00:31:18 -0400572
Eric5e6b10c2021-05-28 21:20:19 +0200573 std::vector<std::string> sensor_names = usrp_dev->get_mboard_sensor_names();
574 if (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end()) {
575 LOGC(DDEV, INFO) << "Waiting for clock reference lock (max " << clock_lock_attempts << "s)..." << std::flush;
576 while (!usrp_dev->get_mboard_sensor("ref_locked", 0).to_bool() && clock_lock_attempts--)
577 sleep(1);
578
579 if (!clock_lock_attempts) {
580 LOGC(DDEV, ALERT) << "Locking to external 10Mhz failed!";
581 return -1;
582 }
583 }
584 LOGC(DDEV, INFO) << "Selected clock source is " << usrp_dev->get_clock_source(0);
585
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700586 try {
587 set_rates();
588 } catch (const std::exception &e) {
Harald Welte5cc88582018-08-17 19:55:38 +0200589 LOGC(DDEV, ALERT) << "UHD rate setting failed - " << e.what();
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500590 return -1;
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700591 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000592
Alexander Chemerise1714252015-04-28 23:09:02 -0400593 // Set RF frontend bandwidth
594 if (dev_type == UMTRX) {
595 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
596 for (size_t i = 0; i < chans; i++) {
597 usrp_dev->set_tx_bandwidth(500*1000*2, i);
Tom Tsoud6ae8642017-03-30 17:22:58 -0700598 usrp_dev->set_rx_bandwidth(500*1000*2, i);
Alexander Chemerise1714252015-04-28 23:09:02 -0400599 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800600 } else if (dev_type == LIMESDR) {
601 for (size_t i = 0; i < chans; i++) {
Pau Espin Pedrol55df1e42018-05-08 20:49:00 +0200602 usrp_dev->set_tx_bandwidth(5.2e6, i);
603 usrp_dev->set_rx_bandwidth(1.4001e6, i);
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800604 }
Alexander Chemerise1714252015-04-28 23:09:02 -0400605 }
606
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400607 /* Create TX and RX streamers */
608 uhd::stream_args_t stream_args("sc16");
609 for (size_t i = 0; i < chans; i++)
610 stream_args.channels.push_back(i);
611
612 tx_stream = usrp_dev->get_tx_stream(stream_args);
613 rx_stream = usrp_dev->get_rx_stream(stream_args);
614
615 /* Number of samples per over-the-wire packet */
616 tx_spp = tx_stream->get_max_num_samps();
617 rx_spp = rx_stream->get_max_num_samps();
618
kurtis.heimerl965e7572011-11-26 03:16:54 +0000619 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400620 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400621 for (size_t i = 0; i < rx_buffers.size(); i++)
Pau Espin Pedrol580c48b2019-05-03 14:38:36 +0200622 rx_buffers[i] = new smpl_buf(buf_len);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000623
Pau Espin Pedrolef192d32019-04-30 18:51:00 +0200624 // Create vector buffer
625 pkt_bufs = std::vector<std::vector<short> >(chans, std::vector<short>(2 * rx_spp));
626 for (size_t i = 0; i < pkt_bufs.size(); i++)
627 pkt_ptrs.push_back(&pkt_bufs[i].front());
628
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100629 // Initialize and shadow gain values
kurtis.heimerl02d04052011-11-26 03:17:10 +0000630 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000631
kurtis.heimerl965e7572011-11-26 03:16:54 +0000632 // Print configuration
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +0100633 LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000634
Eric Wildf8c7a522023-05-17 17:44:08 +0200635 if (cfg->overrides.dl_freq_override) {
636 uhd::tune_request_t treq_tx = uhd::tune_request_t(cfg->overrides.dl_freq, 0);
637 auto tres = usrp_dev->set_tx_freq(treq_tx, 0);
638 tx_freqs[0] = usrp_dev->get_tx_freq(0);
639 LOGCHAN(0, DDEV, INFO) << "OVERRIDE set_freq(" << tx_freqs[0] << ", TX): " << tres.to_pp_string() << std::endl;
640 }
641
642 if (cfg->overrides.ul_freq_override) {
643 uhd::tune_request_t treq_rx = uhd::tune_request_t(cfg->overrides.ul_freq, 0);
644 auto tres = usrp_dev->set_rx_freq(treq_rx, 0);
645 rx_freqs[0] = usrp_dev->get_rx_freq(0);
646 LOGCHAN(0, DDEV, INFO) << "OVERRIDE set_freq(" << rx_freqs[0] << ", RX): " << tres.to_pp_string() << std::endl;
647 }
648
649 if (cfg->overrides.ul_gain_override) {
650 usrp_dev->set_rx_gain(cfg->overrides.ul_gain, 0);
651 rx_gains[0] = usrp_dev->get_rx_gain(0);
652 LOGCHAN(0, DDEV, INFO) << " OVERRIDE RX gain:" << rx_gains[0] << std::endl;
653 }
654
655 if (cfg->overrides.dl_gain_override) {
656 usrp_dev->set_tx_gain(cfg->overrides.dl_gain, 0);
657 tx_gains[0] = usrp_dev->get_tx_gain(0);
658 LOGCHAN(0, DDEV, INFO) << " OVERRIDE TX gain:" << tx_gains[0] << std::endl;
659 }
660
Tom Tsou76764272016-06-24 14:25:39 -0700661 if (iface == MULTI_ARFCN)
662 return MULTI_ARFCN;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500663
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400664 switch (dev_type) {
665 case B100:
666 return RESAMP_64M;
667 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800668 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400669 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500670 case B200:
671 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100672 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800673 case E3XX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800674 case LIMESDR:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500675 default:
676 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400677 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400678
679 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000680}
681
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000682bool uhd_device::flush_recv(size_t num_pkts)
683{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000684 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000685 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800686 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000687
Thomas Tsou18d3b832014-02-13 14:55:23 -0500688 ts_initial = 0;
689 while (!ts_initial || (num_pkts-- > 0)) {
690 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400691 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000692 if (!num_smpls) {
693 switch (md.error_code) {
694 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Harald Welte5cc88582018-08-17 19:55:38 +0200695 LOGC(DDEV, ALERT) << "Device timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800696 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000697 default:
698 continue;
699 }
700 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500701
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700702 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000703 }
704
Harald Welte5cc88582018-08-17 19:55:38 +0200705 LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
Thomas Tsou18d3b832014-02-13 14:55:23 -0500706
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000707 return true;
708}
709
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800710bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000711{
Thomas Tsouc2839162014-03-27 13:52:58 -0400712 /* Allow 100 ms delay to align multi-channel streams */
713 double delay = 0.1;
714
kurtis.heimerl68292102011-11-26 03:17:28 +0000715 aligned = false;
716
Thomas Tsouc2839162014-03-27 13:52:58 -0400717 uhd::time_spec_t current = usrp_dev->get_time_now();
718
Thomas Tsou204a9f12013-10-29 18:34:16 -0400719 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400720 cmd.stream_now = false;
721 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400722
kurtis.heimerl68292102011-11-26 03:17:28 +0000723 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500724
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800725 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000726}
727
kurtis.heimerl965e7572011-11-26 03:16:54 +0000728bool uhd_device::start()
729{
Harald Welte5cc88582018-08-17 19:55:38 +0200730 LOGC(DDEV, INFO) << "Starting USRP...";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000731
732 if (started) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200733 LOGC(DDEV, ERROR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000734 return false;
735 }
736
kurtis.heimerl965e7572011-11-26 03:16:54 +0000737 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800738 async_event_thrd = new Thread();
739 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000740
741 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800742 if (!restart())
743 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000744
kurtis.heimerl965e7572011-11-26 03:16:54 +0000745 // Display usrp time
746 double time_now = usrp_dev->get_time_now().get_real_secs();
Harald Welte5cc88582018-08-17 19:55:38 +0200747 LOGC(DDEV, INFO) << "The current time is " << time_now << " seconds";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000748
749 started = true;
750 return true;
751}
752
753bool uhd_device::stop()
754{
Thomas Tsou2c791102013-11-15 23:00:28 -0500755 if (!started)
756 return false;
757
758 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000759 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
760
761 usrp_dev->issue_stream_cmd(stream_cmd);
762
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800763 async_event_thrd->cancel();
764 async_event_thrd->join();
765 delete async_event_thrd;
766
Eric4080eb72020-07-16 18:08:30 +0200767 /* reset internal buffer timestamps */
768 for (size_t i = 0; i < rx_buffers.size(); i++)
769 rx_buffers[i]->reset();
770
Ericc0f78a32023-05-12 13:00:14 +0200771 band_reset();
Pau Espin Pedrolbb2cb9d2021-09-21 13:52:46 +0200772
kurtis.heimerl965e7572011-11-26 03:16:54 +0000773 started = false;
774 return true;
775}
776
kurtis.heimerld4be0742011-11-26 03:17:46 +0000777int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000778{
kurtis.heimerld4be0742011-11-26 03:17:46 +0000779 if (!num_smpls) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200780 LOGC(DDEV, ERROR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000781
782 switch (md.error_code) {
783 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Harald Welte5cc88582018-08-17 19:55:38 +0200784 LOGC(DDEV, ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800785 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000786 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
787 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
788 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
789 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
790 default:
791 return ERROR_UNHANDLED;
792 }
793 }
794
kurtis.heimerl965e7572011-11-26 03:16:54 +0000795 // Missing timestamp
796 if (!md.has_time_spec) {
Harald Welte5cc88582018-08-17 19:55:38 +0200797 LOGC(DDEV, ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000798 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000799 }
800
801 // Monotonicity check
Tom Tsoua93f7892017-03-07 17:54:06 -0800802 if (md.time_spec < prev_ts) {
Harald Welte5cc88582018-08-17 19:55:38 +0200803 LOGC(DDEV, ALERT) << "UHD: Loss of monotonic time";
804 LOGC(DDEV, ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
ttsou724eb362012-03-13 02:20:01 +0000805 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000806 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000807 }
808
Tom Tsoua93f7892017-03-07 17:54:06 -0800809 // Workaround for UHD tick rounding bug
810 TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
811 if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
812 md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
813
814 prev_ts = md.time_spec;
815
kurtis.heimerl965e7572011-11-26 03:16:54 +0000816 return 0;
817}
818
Thomas Tsou204a9f12013-10-29 18:34:16 -0400819int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
Pau Espin Pedrolf8c0c462020-03-12 19:08:46 +0100820 TIMESTAMP timestamp, bool *underrun)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000821{
822 ssize_t rc;
823 uhd::time_spec_t ts;
824 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000825
Thomas Tsou204a9f12013-10-29 18:34:16 -0400826 if (bufs.size() != chans) {
Harald Welte5cc88582018-08-17 19:55:38 +0200827 LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400828 return -1;
829 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000830
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000831 *overrun = false;
832 *underrun = false;
833
kurtis.heimerl965e7572011-11-26 03:16:54 +0000834 // Shift read time with respect to transmit clock
835 timestamp += ts_offset;
836
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700837 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
Harald Welte5cc88582018-08-17 19:55:38 +0200838 LOGC(DDEV, DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000839
840 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400841 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000842 if (rc < 0) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200843 LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc);
844 LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000845 return 0;
846 }
847
848 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400849 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800850 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400851 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
852 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800853 thread_enable_cancel(true);
854
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100855 // Check for errors
kurtis.heimerld4be0742011-11-26 03:17:46 +0000856 rc = check_rx_md_err(metadata, num_smpls);
857 switch (rc) {
858 case ERROR_UNRECOVERABLE:
Harald Welte5cc88582018-08-17 19:55:38 +0200859 LOGC(DDEV, ALERT) << "UHD: Version " << uhd::get_version_string();
860 LOGC(DDEV, ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000861 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800862 case ERROR_TIMEOUT:
863 // Assume stopping condition
864 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000865 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -0400866 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000867 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000868 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000869 }
870
kurtis.heimerl965e7572011-11-26 03:16:54 +0000871 ts = metadata.time_spec;
Harald Welte5cc88582018-08-17 19:55:38 +0200872 LOGC(DDEV, DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000873
Thomas Tsou204a9f12013-10-29 18:34:16 -0400874 for (size_t i = 0; i < rx_buffers.size(); i++) {
875 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
876 num_smpls,
Pau Espin Pedrol0a2a40f2019-12-19 13:52:57 +0100877 ts.to_ticks(rx_rate));
kurtis.heimerl965e7572011-11-26 03:16:54 +0000878
Thomas Tsou204a9f12013-10-29 18:34:16 -0400879 // Continue on local overrun, exit on other errors
880 if ((rc < 0)) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200881 LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
882 LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400883 if (rc != smpl_buf::ERROR_OVERFLOW)
884 return 0;
885 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000886 }
887 }
888
889 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -0400890 for (size_t i = 0; i < rx_buffers.size(); i++) {
891 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
892 if ((rc < 0) || (rc != len)) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200893 LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
894 LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400895 return 0;
896 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000897 }
898
899 return len;
900}
901
Thomas Tsou204a9f12013-10-29 18:34:16 -0400902int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
Pau Espin Pedroldfc6e5f2020-03-12 19:35:33 +0100903 unsigned long long timestamp)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000904{
905 uhd::tx_metadata_t metadata;
906 metadata.has_time_spec = true;
907 metadata.start_of_burst = false;
908 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700909 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000910
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000911 *underrun = false;
912
Thomas Tsou204a9f12013-10-29 18:34:16 -0400913 if (bufs.size() != chans) {
Harald Welte5cc88582018-08-17 19:55:38 +0200914 LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400915 return -1;
916 }
917
kurtis.heimerl965e7572011-11-26 03:16:54 +0000918 // Drop a fixed number of packets (magic value)
919 if (!aligned) {
920 drop_cnt++;
921
922 if (drop_cnt == 1) {
Harald Welte5cc88582018-08-17 19:55:38 +0200923 LOGC(DDEV, DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000924 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000925 metadata.end_of_burst = true;
926 } else if (drop_cnt < 30) {
Harald Welte5cc88582018-08-17 19:55:38 +0200927 LOGC(DDEV, DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000928 return len;
929 } else {
Harald Welte5cc88582018-08-17 19:55:38 +0200930 LOGC(DDEV, DEBUG) << "Aligning transmitter: start burst";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000931 metadata.start_of_burst = true;
932 aligned = true;
933 drop_cnt = 0;
934 }
935 }
936
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800937 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400938 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800939 thread_enable_cancel(true);
940
ttsoub371ed52012-01-09 18:11:34 +0000941 if (num_smpls != (unsigned) len) {
Harald Welte5cc88582018-08-17 19:55:38 +0200942 LOGC(DDEV, ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +0000943 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000944
945 return num_smpls;
946}
947
948bool uhd_device::updateAlignment(TIMESTAMP timestamp)
949{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000950 return true;
951}
952
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500953uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
954{
955 double rf_spread, rf_freq;
956 std::vector<double> freqs;
957 uhd::tune_request_t treq(freq);
958
Alexander Chemeris90f7a012015-04-09 18:55:02 +0300959 if (dev_type == UMTRX) {
Harald Welte61707e82018-06-13 23:21:57 +0200960 if (lo_offset != 0.0)
961 return uhd::tune_request_t(freq, lo_offset);
Alexander Chemeris90f7a012015-04-09 18:55:02 +0300962
963 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
964 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
965 // only distort the signal (because cordic is not ideal).
966 treq.target_freq = freq;
967 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
968 treq.rf_freq = freq;
969 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
970 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +0300971 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +0300972 } else if (chans == 1) {
Harald Welte61707e82018-06-13 23:21:57 +0200973 if (lo_offset == 0.0)
Thomas Tsou8e17df72014-03-06 14:16:11 -0500974 return treq;
975
Harald Welte61707e82018-06-13 23:21:57 +0200976 return uhd::tune_request_t(freq, lo_offset);
Thomas Tsou8e17df72014-03-06 14:16:11 -0500977 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Harald Welte5cc88582018-08-17 19:55:38 +0200978 LOGC(DDEV, ALERT) << chans << " channels unsupported";
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500979 return treq;
980 }
981
982 if (tx)
983 freqs = tx_freqs;
984 else
985 freqs = rx_freqs;
986
987 /* Tune directly if other channel isn't tuned */
988 if (freqs[!chan] < 10.0)
989 return treq;
990
991 /* Find center frequency between channels */
992 rf_spread = fabs(freqs[!chan] - freq);
Tom Tsouf6115692017-06-26 10:13:25 -0700993 if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
Harald Welte5cc88582018-08-17 19:55:38 +0200994 LOGC(DDEV, ALERT) << rf_spread << "Hz tuning spread not supported\n";
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500995 return treq;
996 }
997
998 rf_freq = (freqs[!chan] + freq) / 2.0f;
999
1000 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1001 treq.target_freq = freq;
1002 treq.rf_freq = rf_freq;
1003
1004 return treq;
1005}
1006
1007bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1008{
1009 std::vector<double> freqs;
1010 uhd::tune_result_t tres;
Ericc0f78a32023-05-12 13:00:14 +02001011 std::string str_dir = tx ? "Tx" : "Rx";
1012
Eric Wildf8c7a522023-05-17 17:44:08 +02001013 if (cfg->overrides.dl_freq_override || cfg->overrides.ul_freq_override)
1014 return true;
1015
Ericc0f78a32023-05-12 13:00:14 +02001016 if (!update_band_from_freq(freq, chan, tx))
1017 return false;
1018
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001019 uhd::tune_request_t treq = select_freq(freq, chan, tx);
1020
1021 if (tx) {
1022 tres = usrp_dev->set_tx_freq(treq, chan);
1023 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
1024 } else {
1025 tres = usrp_dev->set_rx_freq(treq, chan);
1026 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
1027 }
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +01001028 LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001029
Thomas Tsou8e17df72014-03-06 14:16:11 -05001030 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1031 return true;
1032
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001033 /* Manual RF policy means we intentionally tuned with a baseband
1034 * offset for dual-channel purposes. Now retune the other channel
1035 * with the opposite corresponding frequency offset
1036 */
1037 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1038 if (tx) {
1039 treq = select_freq(tx_freqs[!chan], !chan, true);
1040 tres = usrp_dev->set_tx_freq(treq, !chan);
1041 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1042 } else {
1043 treq = select_freq(rx_freqs[!chan], !chan, false);
1044 tres = usrp_dev->set_rx_freq(treq, !chan);
1045 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1046
1047 }
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +01001048 LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001049 }
1050
1051 return true;
1052}
1053
Thomas Tsou204a9f12013-10-29 18:34:16 -04001054bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001055{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001056 if (chan >= tx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001057 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001058 return false;
1059 }
1060
Ericc0f78a32023-05-12 13:00:14 +02001061 return set_freq(wFreq, chan, true);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001062}
1063
Thomas Tsou204a9f12013-10-29 18:34:16 -04001064bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001065{
Thomas Tsou204a9f12013-10-29 18:34:16 -04001066 if (chan >= rx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001067 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001068 return false;
1069 }
Pau Espin Pedrol069f5cd2021-09-21 13:50:38 +02001070
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001071 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001072}
1073
Thomas Tsou204a9f12013-10-29 18:34:16 -04001074double uhd_device::getTxFreq(size_t chan)
1075{
1076 if (chan >= tx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001077 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001078 return 0.0;
1079 }
1080
1081 return tx_freqs[chan];
1082}
1083
1084double uhd_device::getRxFreq(size_t chan)
1085{
1086 if (chan >= rx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001087 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001088 return 0.0;
1089 }
1090
1091 return rx_freqs[chan];
1092}
1093
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001094bool uhd_device::setRxAntenna(const std::string &ant, size_t chan)
1095{
1096 std::vector<std::string> avail;
1097 if (chan >= rx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001098 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001099 return false;
1100 }
1101
Vadim Yanitskiy68d8db42020-06-29 17:39:35 +07001102 /* UHD may throw a LookupError/IndexError here (see OS#4636) */
1103 try {
1104 avail = usrp_dev->get_rx_antennas(chan);
1105 } catch (const uhd::index_error &e) {
1106 LOGC(DDEV, ALERT) << "UHD Error: " << e.what();
1107 return false;
1108 }
1109
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001110 if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001111 LOGC(DDEV, ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan;
1112 LOGC(DDEV, INFO) << "Available Rx antennas: ";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001113 for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
Harald Welte5cc88582018-08-17 19:55:38 +02001114 LOGC(DDEV, INFO) << "- '" << *i << "'";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001115 return false;
1116 }
1117 usrp_dev->set_rx_antenna(ant, chan);
1118 rx_paths[chan] = usrp_dev->get_rx_antenna(chan);
1119
1120 if (ant != rx_paths[chan]) {
Harald Welte5cc88582018-08-17 19:55:38 +02001121 LOGC(DDEV, ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan];
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001122 return false;
1123 }
1124
1125 return true;
1126}
1127
1128std::string uhd_device::getRxAntenna(size_t chan)
1129{
1130 if (chan >= rx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001131 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001132 return "";
1133 }
1134 return usrp_dev->get_rx_antenna(chan);
1135}
1136
1137bool uhd_device::setTxAntenna(const std::string &ant, size_t chan)
1138{
1139 std::vector<std::string> avail;
1140 if (chan >= tx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001141 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001142 return false;
1143 }
1144
Vadim Yanitskiy68d8db42020-06-29 17:39:35 +07001145 /* UHD may throw a LookupError/IndexError here (see OS#4636) */
1146 try {
1147 avail = usrp_dev->get_tx_antennas(chan);
1148 } catch (const uhd::index_error &e) {
1149 LOGC(DDEV, ALERT) << "UHD Error: " << e.what();
1150 return false;
1151 }
1152
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001153 if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001154 LOGC(DDEV, ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan;
1155 LOGC(DDEV, INFO) << "Available Tx antennas: ";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001156 for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
Harald Welte5cc88582018-08-17 19:55:38 +02001157 LOGC(DDEV, INFO) << "- '" << *i << "'";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001158 return false;
1159 }
1160 usrp_dev->set_tx_antenna(ant, chan);
1161 tx_paths[chan] = usrp_dev->get_tx_antenna(chan);
1162
1163 if (ant != tx_paths[chan]) {
Harald Welte5cc88582018-08-17 19:55:38 +02001164 LOGC(DDEV, ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan];
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001165 return false;
1166 }
1167
1168 return true;
1169}
1170
1171std::string uhd_device::getTxAntenna(size_t chan)
1172{
1173 if (chan >= tx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001174 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001175 return "";
1176 }
1177 return usrp_dev->get_tx_antenna(chan);
1178}
1179
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +02001180bool uhd_device::requiresRadioAlign()
1181{
1182 return false;
1183}
1184
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001185GSM::Time uhd_device::minLatency() {
1186 /* Empirical data from a handful of
1187 relatively recent machines shows that the B100 will underrun when
1188 the transmit threshold is reduced to a time of 6 and a half frames,
1189 so we set a minimum 7 frame threshold. */
1190 return GSM::Time(6,7);
1191}
1192
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001193/*
1194 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1195 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1196 * combination.
1197 */
1198TIMESTAMP uhd_device::initialWriteTimestamp()
1199{
Tom Tsou76764272016-06-24 14:25:39 -07001200 if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001201 return ts_initial;
1202 else
1203 return ts_initial * tx_sps;
1204}
1205
1206TIMESTAMP uhd_device::initialReadTimestamp()
1207{
1208 return ts_initial;
1209}
1210
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001211double uhd_device::fullScaleInputValue()
1212{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001213 if (dev_type == LIMESDR)
ignasj28d80812017-06-13 23:37:46 +03001214 return (double) SHRT_MAX * LIMESDR_TX_AMPL;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001215 if (dev_type == UMTRX)
1216 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1217 else
1218 return (double) SHRT_MAX * USRP_TX_AMPL;
1219}
1220
1221double uhd_device::fullScaleOutputValue()
1222{
1223 return (double) SHRT_MAX;
1224}
1225
kurtis.heimerl965e7572011-11-26 03:16:54 +00001226bool uhd_device::recv_async_msg()
1227{
ttsou60dc4c92012-08-08 23:30:23 +00001228 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001229
1230 thread_enable_cancel(false);
1231 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1232 thread_enable_cancel(true);
1233 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001234 return false;
1235
1236 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001237 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001238 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001239
1240 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1241 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +02001242 LOGC(DDEV, ERROR) << str_code(md);
ttsou60dc4c92012-08-08 23:30:23 +00001243 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001244 }
1245
1246 return true;
1247}
1248
1249std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1250{
1251 std::ostringstream ost("UHD: ");
1252
1253 switch (metadata.error_code) {
1254 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1255 ost << "No error";
1256 break;
1257 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1258 ost << "No packet received, implementation timed-out";
1259 break;
1260 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1261 ost << "A stream command was issued in the past";
1262 break;
1263 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1264 ost << "Expected another stream command";
1265 break;
1266 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1267 ost << "An internal receive buffer has filled";
1268 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001269 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1270 ost << "Multi-channel alignment failed";
1271 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001272 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1273 ost << "The packet could not be parsed";
1274 break;
1275 default:
1276 ost << "Unknown error " << metadata.error_code;
1277 }
1278
1279 if (metadata.has_time_spec)
1280 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1281
1282 return ost.str();
1283}
1284
1285std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1286{
1287 std::ostringstream ost("UHD: ");
1288
1289 switch (metadata.event_code) {
1290 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1291 ost << "A packet was successfully transmitted";
1292 break;
1293 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1294 ost << "An internal send buffer has emptied";
1295 break;
1296 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1297 ost << "Packet loss between host and device";
1298 break;
1299 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1300 ost << "Packet time was too late or too early";
1301 break;
1302 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1303 ost << "Underflow occurred inside a packet";
1304 break;
1305 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1306 ost << "Packet loss within a burst";
1307 break;
1308 default:
1309 ost << "Unknown error " << metadata.event_code;
1310 }
1311
1312 if (metadata.has_time_spec)
1313 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1314
1315 return ost.str();
1316}
1317
Eric Wild1e17c4f2020-03-24 17:19:27 +01001318#ifndef IPCMAGIC
Eric19e134a2023-05-10 23:50:38 +02001319RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001320{
Eric19e134a2023-05-10 23:50:38 +02001321 return new uhd_device(type, cfg);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001322}
Eric Wild1e17c4f2020-03-24 17:19:27 +01001323#endif