blob: a0021dbde962eb84633793e0cca46de13a1bac5d [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
94/* Device Type, Tx-SPS, Rx-SPS */
95typedef std::tuple<uhd_dev_type, int, int> dev_key;
96
97/* Device parameter descriptor */
98struct dev_desc {
99 unsigned channels;
100 double mcr;
101 double rate;
102 double offset;
103 std::string str;
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800104};
kurtis.heimerl965e7572011-11-26 03:16:54 +0000105
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700106static const std::map<dev_key, dev_desc> dev_param_map {
107 { std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } },
108 { std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } },
109 { std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } },
110 { std::make_tuple(B100, 1, 1), { 1, 0.0, 400000, 1.2104e-4, "B100 1 SPS" } },
111 { std::make_tuple(B100, 4, 1), { 1, 0.0, 400000, 7.9307e-5, "B100 4/1 Tx/Rx SPS" } },
112 { std::make_tuple(B200, 1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS" } },
113 { std::make_tuple(B200, 4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } },
114 { std::make_tuple(B200, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
115 { std::make_tuple(B210, 1, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B210 1 SPS" } },
116 { std::make_tuple(B210, 4, 1), { 2, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B210 4/1 Tx/Rx SPS" } },
117 { std::make_tuple(B210, 4, 4), { 2, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B210 4 SPS" } },
118 { std::make_tuple(E1XX, 1, 1), { 1, 52e6, GSMRATE, 9.5192e-5, "E1XX 1 SPS" } },
119 { std::make_tuple(E1XX, 4, 1), { 1, 52e6, GSMRATE, 6.5571e-5, "E1XX 4/1 Tx/Rx SPS" } },
120 { std::make_tuple(E3XX, 1, 1), { 2, 26e6, GSMRATE, 1.8462e-4, "E3XX 1 SPS" } },
121 { std::make_tuple(E3XX, 4, 1), { 2, 26e6, GSMRATE, 1.2923e-4, "E3XX 4/1 Tx/Rx SPS" } },
122 { std::make_tuple(X3XX, 1, 1), { 2, 0.0, 390625, 1.5360e-4, "X3XX 1 SPS" } },
123 { std::make_tuple(X3XX, 4, 1), { 2, 0.0, 390625, 1.1264e-4, "X3XX 4/1 Tx/Rx SPS" } },
124 { std::make_tuple(X3XX, 4, 4), { 2, 0.0, 390625, 5.6567e-5, "X3XX 4 SPS" } },
125 { std::make_tuple(UMTRX, 1, 1), { 2, 0.0, GSMRATE, 9.9692e-5, "UmTRX 1 SPS" } },
126 { std::make_tuple(UMTRX, 4, 1), { 2, 0.0, GSMRATE, 7.3846e-5, "UmTRX 4/1 Tx/Rx SPS"} },
127 { std::make_tuple(UMTRX, 4, 4), { 2, 0.0, GSMRATE, 5.1503e-5, "UmTRX 4 SPS" } },
Tom Tsou4cafb0f2017-06-26 10:13:25 -0700128 { std::make_tuple(LIMESDR, 4, 4), { 1, GSMRATE*32, GSMRATE, 8.9e-5, "LimeSDR 4 SPS" } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700129 { std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
130};
Thomas Tsoucb69f082013-04-08 14:18:26 -0400131
Pau Espin Pedrol056ce132020-06-08 12:13:11 +0200132typedef std::tuple<uhd_dev_type, enum gsm_band> dev_band_key;
Pau Espin Pedrol056ce132020-06-08 12:13:11 +0200133typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
134static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200135 { std::make_tuple(B200, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
136 { std::make_tuple(B200, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
137 { std::make_tuple(B200, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
138 { std::make_tuple(B200, GSM_BAND_1900), { 89.75, 7.7, -11.0 } },
139 { std::make_tuple(B210, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
140 { std::make_tuple(B210, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
141 { std::make_tuple(B210, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
142 { std::make_tuple(B210, GSM_BAND_1900), { 89.75, 7.7, -11.0 } },
Pau Espin Pedrol056ce132020-06-08 12:13:11 +0200143};
144
kurtis.heimerl965e7572011-11-26 03:16:54 +0000145void *async_event_loop(uhd_device *dev)
146{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +0200147 set_selfthread_name("UHDAsyncEvent");
Eric Wild1e17c4f2020-03-24 17:19:27 +0100148 osmo_cpu_sched_vty_apply_localthread();
Thomas Tsou7553aa92013-11-08 12:50:03 -0500149
kurtis.heimerl965e7572011-11-26 03:16:54 +0000150 while (1) {
151 dev->recv_async_msg();
152 pthread_testcancel();
153 }
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500154
155 return NULL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000156}
157
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100158#ifdef USE_UHD_3_11
159static void uhd_log_handler(const uhd::log::logging_info &info)
160{
161 int level;
162
163 switch (info.verbosity)
164 {
165 case uhd::log::trace:
166 case uhd::log::debug:
167 level = LOGL_DEBUG;
168 break;
169 case uhd::log::info:
170 level = LOGL_INFO;
171 break;
172 case uhd::log::warning:
173 level = LOGL_NOTICE;
174 break;
175 case uhd::log::error:
176 level = LOGL_ERROR;
177 break;
178 case uhd::log::fatal:
179 level = LOGL_FATAL;
180 break;
181 default:
182 level = LOGL_NOTICE;
183 }
184
185 LOGSRC(DDEVDRV, level, info.file.c_str(), info.line) << "[" << info.component << "] " << info.message;
186}
187#else
Tom Tsou72bf7622017-03-07 14:16:46 -0800188/*
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000189 Catch and drop underrun 'U' and overrun 'O' messages from stdout
190 since we already report using the logging facility. Direct
191 everything else appropriately.
192 */
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100193static void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000194{
195 switch (type) {
196 case uhd::msg::status:
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100197 LOGC(DDEVDRV, INFO) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000198 break;
199 case uhd::msg::warning:
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100200 LOGC(DDEVDRV, NOTICE) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000201 break;
202 case uhd::msg::error:
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100203 LOGC(DDEVDRV, ERROR) << msg;
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000204 break;
205 case uhd::msg::fastpath:
206 break;
207 }
208}
Tom Tsou72bf7622017-03-07 14:16:46 -0800209#endif
kurtis.heimerl0803ad92011-11-26 03:18:51 +0000210
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200211/* So far measurements done for B210 show really close to linear relationship
212 * between gain and real output power, so we simply adjust the measured offset
213 */
214static double TxGain2TxPower(const dev_band_desc &desc, double tx_gain_db)
215{
216 return desc.nom_out_tx_power - (desc.nom_uhd_tx_gain - tx_gain_db);
217}
218static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
219{
220 return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
221}
222
Eric19e134a2023-05-10 23:50:38 +0200223uhd_device::uhd_device(InterfaceType iface, const struct trx_cfg *cfg)
224 : RadioDevice(iface, cfg), rx_gain_min(0.0), rx_gain_max(0.0), band_ass_curr_sess(false),
225 band((enum gsm_band)0), tx_spp(0), rx_spp(0), started(false), aligned(false), drop_cnt(0), prev_ts(0, 0),
226 ts_initial(0), ts_offset(0), async_event_thrd(NULL)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000227{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000228}
229
230uhd_device::~uhd_device()
231{
232 stop();
233
Thomas Tsou204a9f12013-10-29 18:34:16 -0400234 for (size_t i = 0; i < rx_buffers.size(); i++)
235 delete rx_buffers[i];
kurtis.heimerl965e7572011-11-26 03:16:54 +0000236}
237
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200238void uhd_device::assign_band_desc(enum gsm_band req_band)
Pau Espin Pedrol056ce132020-06-08 12:13:11 +0200239{
240 dev_band_map_it it;
Pau Espin Pedrol056ce132020-06-08 12:13:11 +0200241
Pau Espin Pedrol056ce132020-06-08 12:13:11 +0200242 it = dev_band_nom_power_param_map.find(dev_band_key(dev_type, req_band));
243 if (it == dev_band_nom_power_param_map.end()) {
244 dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200245 LOGC(DDEV, ERROR) << "No Power parameters exist for device "
Pau Espin Pedrol056ce132020-06-08 12:13:11 +0200246 << desc.str << " on band " << gsm_band_name(req_band)
247 << ", using B210 ones as fallback";
248 it = dev_band_nom_power_param_map.find(dev_band_key(B210, req_band));
249 }
250 OSMO_ASSERT(it != dev_band_nom_power_param_map.end())
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200251 band_desc = it->second;
252}
253
254bool uhd_device::set_band(enum gsm_band req_band)
255{
Pau Espin Pedrolbb2cb9d2021-09-21 13:52:46 +0200256 if (band_ass_curr_sess && req_band != band) {
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200257 LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
258 << " different from previous band " << gsm_band_name(band);
259 return false;
260 }
261
Pau Espin Pedrolbb2cb9d2021-09-21 13:52:46 +0200262 if (req_band != band) {
Pau Espin Pedrolb9423b22021-09-21 13:52:05 +0200263 band = req_band;
264 assign_band_desc(band);
265 }
Pau Espin Pedrolbb2cb9d2021-09-21 13:52:46 +0200266 band_ass_curr_sess = true;
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200267 return true;
268}
269
270void uhd_device::get_dev_band_desc(dev_band_desc& desc)
271{
272 if (band == 0) {
273 LOGC(DDEV, ERROR) << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
274 assign_band_desc(GSM_BAND_900);
275 }
276 desc = band_desc;
Pau Espin Pedrol056ce132020-06-08 12:13:11 +0200277}
278
kurtis.heimerl24481de2011-11-26 03:17:18 +0000279void uhd_device::init_gains()
280{
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200281 double tx_gain_min, tx_gain_max;
kurtis.heimerl24481de2011-11-26 03:17:18 +0000282 uhd::gain_range_t range;
283
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400284 if (dev_type == UMTRX) {
285 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
286 if (gain_stages[0] == "VGA") {
Harald Welte5cc88582018-08-17 19:55:38 +0200287 LOGC(DDEV, WARNING) << "Update your UHD version for a proper Tx gain support";
Alexander Chemeris4d029d82014-04-19 17:25:00 +0400288 }
289 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
290 range = usrp_dev->get_tx_gain_range();
291 tx_gain_min = range.start();
292 tx_gain_max = range.stop();
293 } else {
294 range = usrp_dev->get_tx_gain_range("VGA2");
295 tx_gain_min = UMTRX_VGA1_DEF + range.start();
296 tx_gain_max = UMTRX_VGA1_DEF + range.stop();
297 }
298 } else {
299 range = usrp_dev->get_tx_gain_range();
300 tx_gain_min = range.start();
301 tx_gain_max = range.stop();
302 }
Harald Welte5cc88582018-08-17 19:55:38 +0200303 LOGC(DDEV, INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000304
305 range = usrp_dev->get_rx_gain_range();
306 rx_gain_min = range.start();
307 rx_gain_max = range.stop();
Harald Welte5cc88582018-08-17 19:55:38 +0200308 LOGC(DDEV, INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
kurtis.heimerl24481de2011-11-26 03:17:18 +0000309
Thomas Tsou204a9f12013-10-29 18:34:16 -0400310 for (size_t i = 0; i < tx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400311 double gain = (tx_gain_min + tx_gain_max) / 2;
Harald Welte5cc88582018-08-17 19:55:38 +0200312 LOGC(DDEV, INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400313 usrp_dev->set_tx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400314 tx_gains[i] = usrp_dev->get_tx_gain(i);
315 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000316
Thomas Tsou204a9f12013-10-29 18:34:16 -0400317 for (size_t i = 0; i < rx_gains.size(); i++) {
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400318 double gain = (rx_gain_min + rx_gain_max) / 2;
Harald Welte5cc88582018-08-17 19:55:38 +0200319 LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
Alexander Chemerisc4eab872015-05-17 23:25:57 -0400320 usrp_dev->set_rx_gain(gain, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400321 rx_gains[i] = usrp_dev->get_rx_gain(i);
322 }
kurtis.heimerl24481de2011-11-26 03:17:18 +0000323
324 return;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400325
kurtis.heimerl24481de2011-11-26 03:17:18 +0000326}
327
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700328void uhd_device::set_rates()
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700329{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700330 dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
331 if (desc.mcr != 0.0)
332 usrp_dev->set_master_clock_rate(desc.mcr);
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700333
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700334 tx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * tx_sps : desc.rate;
335 rx_rate = (dev_type != B2XX_MCBTS) ? desc.rate * rx_sps : desc.rate;
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700336
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700337 usrp_dev->set_tx_rate(tx_rate);
338 usrp_dev->set_rx_rate(rx_rate);
339 tx_rate = usrp_dev->get_tx_rate();
340 rx_rate = usrp_dev->get_rx_rate();
Tom Tsou3f4a13f2016-05-03 19:02:24 -0700341
Tom Tsou1b6ab7d2017-06-15 15:31:08 -0700342 ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
Harald Welte5cc88582018-08-17 19:55:38 +0200343 LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000344}
345
Thomas Tsou204a9f12013-10-29 18:34:16 -0400346double uhd_device::setRxGain(double db, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000347{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400348 if (chan >= rx_gains.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200349 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400350 return 0.0f;
351 }
kurtis.heimerl02d04052011-11-26 03:17:10 +0000352
Thomas Tsou204a9f12013-10-29 18:34:16 -0400353 usrp_dev->set_rx_gain(db, chan);
354 rx_gains[chan] = usrp_dev->get_rx_gain(chan);
kurtis.heimerl02d04052011-11-26 03:17:10 +0000355
Harald Welte5cc88582018-08-17 19:55:38 +0200356 LOGC(DDEV, INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400357
358 return rx_gains[chan];
359}
360
361double uhd_device::getRxGain(size_t chan)
362{
363 if (chan >= rx_gains.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200364 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400365 return 0.0f;
366 }
367
368 return rx_gains[chan];
kurtis.heimerl02d04052011-11-26 03:17:10 +0000369}
370
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200371double uhd_device::rssiOffset(size_t chan)
372{
373 double rssiOffset;
374 dev_band_desc desc;
375
376 if (chan >= rx_gains.size()) {
377 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
378 return 0.0f;
379 }
380
381 get_dev_band_desc(desc);
382 rssiOffset = rx_gains[chan] + desc.rxgain2rssioffset_rel;
383 return rssiOffset;
384}
385
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200386double uhd_device::setPowerAttenuation(int atten, size_t chan) {
Pau Espin Pedrol58d80a02020-06-19 19:29:59 +0200387 double tx_power, db;
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200388 dev_band_desc desc;
389
390 if (chan >= tx_gains.size()) {
391 LOGC(DDEV, ALERT) << "Requested non-existent channel" << chan;
392 return 0.0f;
393 }
394
395 get_dev_band_desc(desc);
Pau Espin Pedrol58d80a02020-06-19 19:29:59 +0200396 tx_power = desc.nom_out_tx_power - atten;
397 db = TxPower2TxGain(desc, tx_power);
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200398
399 if (dev_type == UMTRX) {
400 std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
401 if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
402 usrp_dev->set_tx_gain(db, chan);
403 } else {
404 // New UHD versions support split configuration of
405 // Tx gain stages. We utilize this to set the gain
406 // configuration, optimal for the Tx signal quality.
407 // From our measurements, VGA1 must be 18dB plus-minus
408 // one and VGA2 is the best when 23dB or lower.
409 usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
410 usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
411 }
412 } else {
413 usrp_dev->set_tx_gain(db, chan);
414 }
415
416 tx_gains[chan] = usrp_dev->get_tx_gain(chan);
417
Pau Espin Pedrol58d80a02020-06-19 19:29:59 +0200418 LOGC(DDEV, INFO) << "Set TX gain to " << tx_gains[chan] << "dB, ~"
419 << TxGain2TxPower(desc, tx_gains[chan]) << " dBm "
420 << "(asked for " << db << " dB, ~" << tx_power << " dBm)";
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200421
422 return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
423}
424double uhd_device::getPowerAttenuation(size_t chan) {
425 dev_band_desc desc;
Pau Espin Pedrol2ab92182019-09-13 17:05:02 +0200426 if (chan >= tx_gains.size()) {
427 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
428 return 0.0f;
429 }
430
Pau Espin Pedrolb899c192020-06-08 14:49:33 +0200431 get_dev_band_desc(desc);
432 return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
Pau Espin Pedrol2ab92182019-09-13 17:05:02 +0200433}
434
Pau Espin Pedrol0e09e7c2020-05-29 16:39:07 +0200435int uhd_device::getNominalTxPower(size_t chan)
436{
Pau Espin Pedrol056ce132020-06-08 12:13:11 +0200437 dev_band_desc desc;
438 get_dev_band_desc(desc);
439
440 return desc.nom_out_tx_power;
Pau Espin Pedrol0e09e7c2020-05-29 16:39:07 +0200441}
442
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000443/*
444 Parse the UHD device tree and mboard name to find out what device we're
Thomas Tsou02d88d12013-04-05 15:36:30 -0400445 dealing with. We need the window type so that the transceiver knows how to
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000446 deal with the transport latency. Reject the USRP1 because UHD doesn't
447 support timestamped samples with it.
448 */
449bool uhd_device::parse_dev_type()
450{
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700451 uhd::property_tree::sptr prop_tree = usrp_dev->get_device()->get_tree();
452 std::string devString = prop_tree->access<std::string>("/name").get();
453 std::string mboardString = usrp_dev->get_mboard_name();
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000454
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700455 const std::map<std::string, std::pair<uhd_dev_type, TxWindowType>> devStringMap {
456 { "B100", { B100, TX_WINDOW_USRP1 } },
457 { "B200", { B200, TX_WINDOW_USRP1 } },
458 { "B200mini", { B200, TX_WINDOW_USRP1 } },
Piotr Krysikaa60dda2017-12-04 00:29:16 +0700459 { "B205mini", { B200, TX_WINDOW_USRP1 } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700460 { "B210", { B210, TX_WINDOW_USRP1 } },
461 { "E100", { E1XX, TX_WINDOW_FIXED } },
462 { "E110", { E1XX, TX_WINDOW_FIXED } },
463 { "E310", { E3XX, TX_WINDOW_FIXED } },
464 { "E3XX", { E3XX, TX_WINDOW_FIXED } },
465 { "X300", { X3XX, TX_WINDOW_FIXED } },
466 { "X310", { X3XX, TX_WINDOW_FIXED } },
Tom Tsou988a4642017-06-15 09:16:53 -0700467 { "USRP2", { USRP2, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700468 { "UmTRX", { UMTRX, TX_WINDOW_FIXED } },
ignasj87ed77b2017-06-13 23:37:46 +0300469 { "LimeSDR", { LIMESDR, TX_WINDOW_FIXED } },
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700470 };
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000471
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700472 // Compare UHD motherboard and device strings */
Tom Tsou988a4642017-06-15 09:16:53 -0700473 auto mapIter = devStringMap.begin();
474 while (mapIter != devStringMap.end()) {
475 if (devString.find(mapIter->first) != std::string::npos ||
476 mboardString.find(mapIter->first) != std::string::npos) {
477 dev_type = std::get<0>(mapIter->second);
478 tx_window = std::get<1>(mapIter->second);
479 return true;
480 }
481 mapIter++;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000482 }
483
Harald Welte5cc88582018-08-17 19:55:38 +0200484 LOGC(DDEV, ALERT) << "Unsupported device " << devString;
Tom Tsou988a4642017-06-15 09:16:53 -0700485 return false;
kurtis.heimerlb7358ce2011-11-26 03:18:57 +0000486}
487
Tom Tsou3b093bb2016-05-03 19:04:15 -0700488/*
489 * Check for UHD version > 3.9.0 for E3XX support
490 */
491static bool uhd_e3xx_version_chk()
492{
493 std::string ver = uhd::get_version_string();
494 std::string major_str(ver.begin(), ver.begin() + 3);
495 std::string minor_str(ver.begin() + 4, ver.begin() + 7);
496
497 int major_val = atoi(major_str.c_str());
498 int minor_val = atoi(minor_str.c_str());
499
500 if (major_val < 3)
501 return false;
502 if (minor_val < 9)
503 return false;
504
505 return true;
506}
507
Tom Tsou980525c2017-06-09 15:37:19 -0700508void uhd_device::set_channels(bool swap)
509{
510 if (iface == MULTI_ARFCN) {
511 if (dev_type != B200 && dev_type != B210)
512 throw std::invalid_argument("Device does not support MCBTS");
513 dev_type = B2XX_MCBTS;
Tom Tsou980525c2017-06-09 15:37:19 -0700514 }
515
Tom Tsouf6115692017-06-26 10:13:25 -0700516 if (chans > dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps)).channels)
Tom Tsou980525c2017-06-09 15:37:19 -0700517 throw std::invalid_argument("Device does not support number of requested channels");
518
519 std::string subdev_string;
520 switch (dev_type) {
521 case B210:
522 case E3XX:
523 if (chans == 1)
524 subdev_string = swap ? "A:B" : "A:A";
525 else if (chans == 2)
526 subdev_string = swap ? "A:B A:A" : "A:A A:B";
527 break;
528 case X3XX:
529 case UMTRX:
530 if (chans == 1)
531 subdev_string = swap ? "B:0" : "A:0";
532 else if (chans == 2)
533 subdev_string = swap ? "B:0 A:0" : "A:0 B:0";
534 break;
535 default:
536 break;
537 }
538
539 if (!subdev_string.empty()) {
540 uhd::usrp::subdev_spec_t spec(subdev_string);
541 usrp_dev->set_tx_subdev_spec(spec);
542 usrp_dev->set_rx_subdev_spec(spec);
543 }
544}
545
Eric19e134a2023-05-10 23:50:38 +0200546int uhd_device::open()
kurtis.heimerl965e7572011-11-26 03:16:54 +0000547{
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700548 const char *refstr;
Eric5e6b10c2021-05-28 21:20:19 +0200549 int clock_lock_attempts = 15;
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700550
Pau Espin Pedrol84231bd2019-12-20 22:47:06 +0100551 /* Register msg handler. Different APIs depending on UHD version */
552#ifdef USE_UHD_3_11
553 uhd::log::add_logger("OsmoTRX", &uhd_log_handler);
554 uhd::log::set_log_level(uhd::log::debug);
555 uhd::log::set_console_level(uhd::log::off);
556 uhd::log::set_logger_level("OsmoTRX", uhd::log::debug);
557#else
558 uhd::msg::register_handler(&uhd_msg_handler);
559#endif
560
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000561 // Find UHD devices
Eric19e134a2023-05-10 23:50:38 +0200562 uhd::device_addr_t addr(cfg->dev_args);
ttsouf60dafa2012-10-22 00:07:14 +0000563 uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000564 if (dev_addrs.size() == 0) {
Eric19e134a2023-05-10 23:50:38 +0200565 LOGC(DDEV, ALERT) << "No UHD devices found with address '" << cfg->dev_args << "'";
Thomas Tsoucb69f082013-04-08 14:18:26 -0400566 return -1;
kurtis.heimerl4403d4f2011-11-28 06:26:04 +0000567 }
568
569 // Use the first found device
Harald Welte5cc88582018-08-17 19:55:38 +0200570 LOGC(DDEV, INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000571 try {
Tom Tsou5c7c1782015-05-18 16:51:44 -0700572 usrp_dev = uhd::usrp::multi_usrp::make(addr);
d0gtailebb37692018-12-01 17:02:15 +0100573 } catch(uhd::key_error::exception &e) {
Eric19e134a2023-05-10 23:50:38 +0200574 LOGC(DDEV, ALERT) << "UHD make failed, device " << cfg->dev_args << ", exception:\n" << e.what();
Thomas Tsoucb69f082013-04-08 14:18:26 -0400575 return -1;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000576 }
577
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000578 // Check for a valid device type and set bus type
579 if (!parse_dev_type())
Thomas Tsoucb69f082013-04-08 14:18:26 -0400580 return -1;
kurtis.heimerl523ae5a2011-11-28 06:26:01 +0000581
Tom Tsou3b093bb2016-05-03 19:04:15 -0700582 if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200583 LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater";
Tom Tsou3b093bb2016-05-03 19:04:15 -0700584 return -1;
585 }
586
Tom Tsou980525c2017-06-09 15:37:19 -0700587 try {
Eric19e134a2023-05-10 23:50:38 +0200588 set_channels(cfg->swap_channels);
589 } catch (const std::exception &e) {
Harald Welte5cc88582018-08-17 19:55:38 +0200590 LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400591 return -1;
592 }
593
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100594 if (!set_antennas()) {
Harald Welte5cc88582018-08-17 19:55:38 +0200595 LOGC(DDEV, ALERT) << "UHD antenna setting failed";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +0100596 return -1;
597 }
598
Thomas Tsou204a9f12013-10-29 18:34:16 -0400599 tx_freqs.resize(chans);
600 rx_freqs.resize(chans);
601 tx_gains.resize(chans);
602 rx_gains.resize(chans);
603 rx_buffers.resize(chans);
604
Eric19e134a2023-05-10 23:50:38 +0200605 switch (cfg->clock_ref) {
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700606 case REF_INTERNAL:
607 refstr = "internal";
608 break;
609 case REF_EXTERNAL:
610 refstr = "external";
611 break;
612 case REF_GPS:
613 refstr = "gpsdo";
614 break;
615 default:
Harald Welte5cc88582018-08-17 19:55:38 +0200616 LOGC(DDEV, ALERT) << "Invalid reference type";
Tom Tsou2f3e60b2016-07-17 19:29:08 -0700617 return -1;
618 }
619
620 usrp_dev->set_clock_source(refstr);
Thomas Tsou010fff72013-10-16 00:31:18 -0400621
Eric5e6b10c2021-05-28 21:20:19 +0200622 std::vector<std::string> sensor_names = usrp_dev->get_mboard_sensor_names();
623 if (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end()) {
624 LOGC(DDEV, INFO) << "Waiting for clock reference lock (max " << clock_lock_attempts << "s)..." << std::flush;
625 while (!usrp_dev->get_mboard_sensor("ref_locked", 0).to_bool() && clock_lock_attempts--)
626 sleep(1);
627
628 if (!clock_lock_attempts) {
629 LOGC(DDEV, ALERT) << "Locking to external 10Mhz failed!";
630 return -1;
631 }
632 }
633 LOGC(DDEV, INFO) << "Selected clock source is " << usrp_dev->get_clock_source(0);
634
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700635 try {
636 set_rates();
637 } catch (const std::exception &e) {
Harald Welte5cc88582018-08-17 19:55:38 +0200638 LOGC(DDEV, ALERT) << "UHD rate setting failed - " << e.what();
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500639 return -1;
Tom Tsou1fb0ce62017-06-08 19:41:48 -0700640 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000641
Alexander Chemerise1714252015-04-28 23:09:02 -0400642 // Set RF frontend bandwidth
643 if (dev_type == UMTRX) {
644 // Setting LMS6002D LPF to 500kHz gives us the best signal quality
645 for (size_t i = 0; i < chans; i++) {
646 usrp_dev->set_tx_bandwidth(500*1000*2, i);
Tom Tsoud6ae8642017-03-30 17:22:58 -0700647 usrp_dev->set_rx_bandwidth(500*1000*2, i);
Alexander Chemerise1714252015-04-28 23:09:02 -0400648 }
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800649 } else if (dev_type == LIMESDR) {
650 for (size_t i = 0; i < chans; i++) {
Pau Espin Pedrol55df1e42018-05-08 20:49:00 +0200651 usrp_dev->set_tx_bandwidth(5.2e6, i);
652 usrp_dev->set_rx_bandwidth(1.4001e6, i);
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800653 }
Alexander Chemerise1714252015-04-28 23:09:02 -0400654 }
655
Thomas Tsoucecfb2e2014-03-27 13:44:09 -0400656 /* Create TX and RX streamers */
657 uhd::stream_args_t stream_args("sc16");
658 for (size_t i = 0; i < chans; i++)
659 stream_args.channels.push_back(i);
660
661 tx_stream = usrp_dev->get_tx_stream(stream_args);
662 rx_stream = usrp_dev->get_rx_stream(stream_args);
663
664 /* Number of samples per over-the-wire packet */
665 tx_spp = tx_stream->get_max_num_samps();
666 rx_spp = rx_stream->get_max_num_samps();
667
kurtis.heimerl965e7572011-11-26 03:16:54 +0000668 // Create receive buffer
Thomas Tsoue3e88142013-04-05 20:42:41 -0400669 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400670 for (size_t i = 0; i < rx_buffers.size(); i++)
Pau Espin Pedrol580c48b2019-05-03 14:38:36 +0200671 rx_buffers[i] = new smpl_buf(buf_len);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000672
Pau Espin Pedrolef192d32019-04-30 18:51:00 +0200673 // Create vector buffer
674 pkt_bufs = std::vector<std::vector<short> >(chans, std::vector<short>(2 * rx_spp));
675 for (size_t i = 0; i < pkt_bufs.size(); i++)
676 pkt_ptrs.push_back(&pkt_bufs[i].front());
677
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100678 // Initialize and shadow gain values
kurtis.heimerl02d04052011-11-26 03:17:10 +0000679 init_gains();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000680
kurtis.heimerl965e7572011-11-26 03:16:54 +0000681 // Print configuration
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +0100682 LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000683
Tom Tsou76764272016-06-24 14:25:39 -0700684 if (iface == MULTI_ARFCN)
685 return MULTI_ARFCN;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500686
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400687 switch (dev_type) {
688 case B100:
689 return RESAMP_64M;
690 case USRP2:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800691 case X3XX:
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400692 return RESAMP_100M;
Thomas Tsoue7882392014-02-13 14:46:23 -0500693 case B200:
694 case B210:
Thomas Tsoua5c83ae2014-04-02 03:31:44 +0100695 case E1XX:
Tom Tsou4ad9ea62014-12-03 18:47:20 -0800696 case E3XX:
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -0800697 case LIMESDR:
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500698 default:
699 break;
Thomas Tsoufe269fe2013-10-14 23:56:51 -0400700 }
Thomas Tsoucb69f082013-04-08 14:18:26 -0400701
702 return NORMAL;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000703}
704
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000705bool uhd_device::flush_recv(size_t num_pkts)
706{
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000707 uhd::rx_metadata_t md;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000708 size_t num_smpls;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800709 float timeout = UHD_RESTART_TIMEOUT;
kurtis.heimerl187b03d2011-11-26 03:17:40 +0000710
Thomas Tsou18d3b832014-02-13 14:55:23 -0500711 ts_initial = 0;
712 while (!ts_initial || (num_pkts-- > 0)) {
713 num_smpls = rx_stream->recv(pkt_ptrs, rx_spp, md,
Thomas Tsoub4cb4e22013-04-05 15:11:35 -0400714 timeout, true);
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000715 if (!num_smpls) {
716 switch (md.error_code) {
717 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Harald Welte5cc88582018-08-17 19:55:38 +0200718 LOGC(DDEV, ALERT) << "Device timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800719 return false;
kurtis.heimerl495d3c82011-11-26 03:18:02 +0000720 default:
721 continue;
722 }
723 }
Thomas Tsou18d3b832014-02-13 14:55:23 -0500724
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700725 ts_initial = md.time_spec.to_ticks(rx_rate);
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000726 }
727
Harald Welte5cc88582018-08-17 19:55:38 +0200728 LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
Thomas Tsou18d3b832014-02-13 14:55:23 -0500729
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000730 return true;
731}
732
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800733bool uhd_device::restart()
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000734{
Thomas Tsouc2839162014-03-27 13:52:58 -0400735 /* Allow 100 ms delay to align multi-channel streams */
736 double delay = 0.1;
737
kurtis.heimerl68292102011-11-26 03:17:28 +0000738 aligned = false;
739
Thomas Tsouc2839162014-03-27 13:52:58 -0400740 uhd::time_spec_t current = usrp_dev->get_time_now();
741
Thomas Tsou204a9f12013-10-29 18:34:16 -0400742 uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
Thomas Tsouc2839162014-03-27 13:52:58 -0400743 cmd.stream_now = false;
744 cmd.time_spec = uhd::time_spec_t(current.get_real_secs() + delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400745
kurtis.heimerl68292102011-11-26 03:17:28 +0000746 usrp_dev->issue_stream_cmd(cmd);
Thomas Tsou18d3b832014-02-13 14:55:23 -0500747
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800748 return flush_recv(10);
kurtis.heimerla14d4be2011-11-26 03:17:23 +0000749}
750
kurtis.heimerl965e7572011-11-26 03:16:54 +0000751bool uhd_device::start()
752{
Harald Welte5cc88582018-08-17 19:55:38 +0200753 LOGC(DDEV, INFO) << "Starting USRP...";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000754
755 if (started) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200756 LOGC(DDEV, ERROR) << "Device already started";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000757 return false;
758 }
759
kurtis.heimerl965e7572011-11-26 03:16:54 +0000760 // Start asynchronous event (underrun check) loop
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800761 async_event_thrd = new Thread();
762 async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000763
764 // Start streaming
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800765 if (!restart())
766 return false;
kurtis.heimerl33f748f2011-11-26 03:16:57 +0000767
kurtis.heimerl965e7572011-11-26 03:16:54 +0000768 // Display usrp time
769 double time_now = usrp_dev->get_time_now().get_real_secs();
Harald Welte5cc88582018-08-17 19:55:38 +0200770 LOGC(DDEV, INFO) << "The current time is " << time_now << " seconds";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000771
772 started = true;
773 return true;
774}
775
776bool uhd_device::stop()
777{
Thomas Tsou2c791102013-11-15 23:00:28 -0500778 if (!started)
779 return false;
780
781 uhd::stream_cmd_t stream_cmd =
kurtis.heimerl965e7572011-11-26 03:16:54 +0000782 uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
783
784 usrp_dev->issue_stream_cmd(stream_cmd);
785
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800786 async_event_thrd->cancel();
787 async_event_thrd->join();
788 delete async_event_thrd;
789
Eric4080eb72020-07-16 18:08:30 +0200790 /* reset internal buffer timestamps */
791 for (size_t i = 0; i < rx_buffers.size(); i++)
792 rx_buffers[i]->reset();
793
Pau Espin Pedrolbb2cb9d2021-09-21 13:52:46 +0200794 band_ass_curr_sess = false;
795
kurtis.heimerl965e7572011-11-26 03:16:54 +0000796 started = false;
797 return true;
798}
799
kurtis.heimerld4be0742011-11-26 03:17:46 +0000800int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000801{
kurtis.heimerld4be0742011-11-26 03:17:46 +0000802 if (!num_smpls) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200803 LOGC(DDEV, ERROR) << str_code(md);
kurtis.heimerld4be0742011-11-26 03:17:46 +0000804
805 switch (md.error_code) {
806 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
Harald Welte5cc88582018-08-17 19:55:38 +0200807 LOGC(DDEV, ALERT) << "UHD: Receive timed out";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800808 return ERROR_TIMEOUT;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000809 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
810 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
811 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
812 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
813 default:
814 return ERROR_UNHANDLED;
815 }
816 }
817
kurtis.heimerl965e7572011-11-26 03:16:54 +0000818 // Missing timestamp
819 if (!md.has_time_spec) {
Harald Welte5cc88582018-08-17 19:55:38 +0200820 LOGC(DDEV, ALERT) << "UHD: Received packet missing timestamp";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000821 return ERROR_UNRECOVERABLE;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000822 }
823
824 // Monotonicity check
Tom Tsoua93f7892017-03-07 17:54:06 -0800825 if (md.time_spec < prev_ts) {
Harald Welte5cc88582018-08-17 19:55:38 +0200826 LOGC(DDEV, ALERT) << "UHD: Loss of monotonic time";
827 LOGC(DDEV, ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", "
ttsou724eb362012-03-13 02:20:01 +0000828 << "Previous time: " << prev_ts.get_real_secs();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000829 return ERROR_TIMING;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000830 }
831
Tom Tsoua93f7892017-03-07 17:54:06 -0800832 // Workaround for UHD tick rounding bug
833 TIMESTAMP ticks = md.time_spec.to_ticks(rx_rate);
834 if (ticks - prev_ts.to_ticks(rx_rate) == rx_spp - 1)
835 md.time_spec = uhd::time_spec_t::from_ticks(++ticks, rx_rate);
836
837 prev_ts = md.time_spec;
838
kurtis.heimerl965e7572011-11-26 03:16:54 +0000839 return 0;
840}
841
Thomas Tsou204a9f12013-10-29 18:34:16 -0400842int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
Pau Espin Pedrolf8c0c462020-03-12 19:08:46 +0100843 TIMESTAMP timestamp, bool *underrun)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000844{
845 ssize_t rc;
846 uhd::time_spec_t ts;
847 uhd::rx_metadata_t metadata;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000848
Thomas Tsou204a9f12013-10-29 18:34:16 -0400849 if (bufs.size() != chans) {
Harald Welte5cc88582018-08-17 19:55:38 +0200850 LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400851 return -1;
852 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000853
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000854 *overrun = false;
855 *underrun = false;
856
kurtis.heimerl965e7572011-11-26 03:16:54 +0000857 // Shift read time with respect to transmit clock
858 timestamp += ts_offset;
859
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700860 ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
Harald Welte5cc88582018-08-17 19:55:38 +0200861 LOGC(DDEV, DEBUG) << "Requested timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000862
863 // Check that timestamp is valid
Thomas Tsou204a9f12013-10-29 18:34:16 -0400864 rc = rx_buffers[0]->avail_smpls(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000865 if (rc < 0) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200866 LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc);
867 LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000868 return 0;
869 }
870
871 // Receive samples from the usrp until we have enough
Thomas Tsou204a9f12013-10-29 18:34:16 -0400872 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800873 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400874 size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
875 metadata, 0.1, true);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800876 thread_enable_cancel(true);
877
Pau Espin Pedrolf58cd8a2018-02-05 13:04:41 +0100878 // Check for errors
kurtis.heimerld4be0742011-11-26 03:17:46 +0000879 rc = check_rx_md_err(metadata, num_smpls);
880 switch (rc) {
881 case ERROR_UNRECOVERABLE:
Harald Welte5cc88582018-08-17 19:55:38 +0200882 LOGC(DDEV, ALERT) << "UHD: Version " << uhd::get_version_string();
883 LOGC(DDEV, ALERT) << "UHD: Unrecoverable error, exiting...";
kurtis.heimerld4be0742011-11-26 03:17:46 +0000884 exit(-1);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800885 case ERROR_TIMEOUT:
886 // Assume stopping condition
887 return 0;
kurtis.heimerld4be0742011-11-26 03:17:46 +0000888 case ERROR_TIMING:
Thomas Tsouc2839162014-03-27 13:52:58 -0400889 restart();
kurtis.heimerld4be0742011-11-26 03:17:46 +0000890 case ERROR_UNHANDLED:
kurtis.heimerl513a6292011-11-26 03:18:36 +0000891 continue;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000892 }
893
kurtis.heimerl965e7572011-11-26 03:16:54 +0000894 ts = metadata.time_spec;
Harald Welte5cc88582018-08-17 19:55:38 +0200895 LOGC(DDEV, DEBUG) << "Received timestamp = " << ts.get_real_secs();
kurtis.heimerl965e7572011-11-26 03:16:54 +0000896
Thomas Tsou204a9f12013-10-29 18:34:16 -0400897 for (size_t i = 0; i < rx_buffers.size(); i++) {
898 rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
899 num_smpls,
Pau Espin Pedrol0a2a40f2019-12-19 13:52:57 +0100900 ts.to_ticks(rx_rate));
kurtis.heimerl965e7572011-11-26 03:16:54 +0000901
Thomas Tsou204a9f12013-10-29 18:34:16 -0400902 // Continue on local overrun, exit on other errors
903 if ((rc < 0)) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200904 LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
905 LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400906 if (rc != smpl_buf::ERROR_OVERFLOW)
907 return 0;
908 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000909 }
910 }
911
912 // We have enough samples
Thomas Tsou204a9f12013-10-29 18:34:16 -0400913 for (size_t i = 0; i < rx_buffers.size(); i++) {
914 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
915 if ((rc < 0) || (rc != len)) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +0200916 LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
917 LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400918 return 0;
919 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000920 }
921
922 return len;
923}
924
Thomas Tsou204a9f12013-10-29 18:34:16 -0400925int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
Pau Espin Pedroldfc6e5f2020-03-12 19:35:33 +0100926 unsigned long long timestamp)
kurtis.heimerl965e7572011-11-26 03:16:54 +0000927{
928 uhd::tx_metadata_t metadata;
929 metadata.has_time_spec = true;
930 metadata.start_of_burst = false;
931 metadata.end_of_burst = false;
Tom Tsoud4d3daa2015-08-21 19:21:28 -0700932 metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
kurtis.heimerl965e7572011-11-26 03:16:54 +0000933
Thomas Tsoucf910dc2013-10-25 14:42:00 +0000934 *underrun = false;
935
Thomas Tsou204a9f12013-10-29 18:34:16 -0400936 if (bufs.size() != chans) {
Harald Welte5cc88582018-08-17 19:55:38 +0200937 LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
Thomas Tsou204a9f12013-10-29 18:34:16 -0400938 return -1;
939 }
940
kurtis.heimerl965e7572011-11-26 03:16:54 +0000941 // Drop a fixed number of packets (magic value)
942 if (!aligned) {
943 drop_cnt++;
944
945 if (drop_cnt == 1) {
Harald Welte5cc88582018-08-17 19:55:38 +0200946 LOGC(DDEV, DEBUG) << "Aligning transmitter: stop burst";
ttsou221f23e2012-08-08 00:51:34 +0000947 *underrun = true;
kurtis.heimerl965e7572011-11-26 03:16:54 +0000948 metadata.end_of_burst = true;
949 } else if (drop_cnt < 30) {
Harald Welte5cc88582018-08-17 19:55:38 +0200950 LOGC(DDEV, DEBUG) << "Aligning transmitter: packet advance";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000951 return len;
952 } else {
Harald Welte5cc88582018-08-17 19:55:38 +0200953 LOGC(DDEV, DEBUG) << "Aligning transmitter: start burst";
kurtis.heimerl965e7572011-11-26 03:16:54 +0000954 metadata.start_of_burst = true;
955 aligned = true;
956 drop_cnt = 0;
957 }
958 }
959
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800960 thread_enable_cancel(false);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400961 size_t num_smpls = tx_stream->send(bufs, len, metadata);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800962 thread_enable_cancel(true);
963
ttsoub371ed52012-01-09 18:11:34 +0000964 if (num_smpls != (unsigned) len) {
Harald Welte5cc88582018-08-17 19:55:38 +0200965 LOGC(DDEV, ALERT) << "UHD: Device send timed out";
ttsoub371ed52012-01-09 18:11:34 +0000966 }
kurtis.heimerl965e7572011-11-26 03:16:54 +0000967
968 return num_smpls;
969}
970
971bool uhd_device::updateAlignment(TIMESTAMP timestamp)
972{
kurtis.heimerl965e7572011-11-26 03:16:54 +0000973 return true;
974}
975
Thomas Tsou3ebc7722014-02-13 15:09:00 -0500976uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
977{
978 double rf_spread, rf_freq;
979 std::vector<double> freqs;
980 uhd::tune_request_t treq(freq);
981
Alexander Chemeris90f7a012015-04-09 18:55:02 +0300982 if (dev_type == UMTRX) {
Harald Welte61707e82018-06-13 23:21:57 +0200983 if (lo_offset != 0.0)
984 return uhd::tune_request_t(freq, lo_offset);
Alexander Chemeris90f7a012015-04-09 18:55:02 +0300985
986 // Don't use DSP tuning, because LMS6002D PLL steps are small enough.
987 // We end up with DSP tuning just for 2-3Hz, which is meaningless and
988 // only distort the signal (because cordic is not ideal).
989 treq.target_freq = freq;
990 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
991 treq.rf_freq = freq;
992 treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
993 treq.dsp_freq = 0.0;
Alexander Chemerisf3b9af62015-06-20 00:05:51 +0300994 return treq;
Alexander Chemeris90f7a012015-04-09 18:55:02 +0300995 } else if (chans == 1) {
Harald Welte61707e82018-06-13 23:21:57 +0200996 if (lo_offset == 0.0)
Thomas Tsou8e17df72014-03-06 14:16:11 -0500997 return treq;
998
Harald Welte61707e82018-06-13 23:21:57 +0200999 return uhd::tune_request_t(freq, lo_offset);
Thomas Tsou8e17df72014-03-06 14:16:11 -05001000 } else if ((dev_type != B210) || (chans > 2) || (chan > 1)) {
Harald Welte5cc88582018-08-17 19:55:38 +02001001 LOGC(DDEV, ALERT) << chans << " channels unsupported";
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001002 return treq;
1003 }
1004
1005 if (tx)
1006 freqs = tx_freqs;
1007 else
1008 freqs = rx_freqs;
1009
1010 /* Tune directly if other channel isn't tuned */
1011 if (freqs[!chan] < 10.0)
1012 return treq;
1013
1014 /* Find center frequency between channels */
1015 rf_spread = fabs(freqs[!chan] - freq);
Tom Tsouf6115692017-06-26 10:13:25 -07001016 if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) {
Harald Welte5cc88582018-08-17 19:55:38 +02001017 LOGC(DDEV, ALERT) << rf_spread << "Hz tuning spread not supported\n";
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001018 return treq;
1019 }
1020
1021 rf_freq = (freqs[!chan] + freq) / 2.0f;
1022
1023 treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
1024 treq.target_freq = freq;
1025 treq.rf_freq = rf_freq;
1026
1027 return treq;
1028}
1029
1030bool uhd_device::set_freq(double freq, size_t chan, bool tx)
1031{
1032 std::vector<double> freqs;
1033 uhd::tune_result_t tres;
1034 uhd::tune_request_t treq = select_freq(freq, chan, tx);
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +01001035 std::string str_dir;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001036
1037 if (tx) {
1038 tres = usrp_dev->set_tx_freq(treq, chan);
1039 tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +01001040 str_dir = "Tx";
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001041 } else {
1042 tres = usrp_dev->set_rx_freq(treq, chan);
1043 rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +01001044 str_dir = "Rx";
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001045 }
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +01001046 LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001047
Thomas Tsou8e17df72014-03-06 14:16:11 -05001048 if ((chans == 1) || ((chans == 2) && dev_type == UMTRX))
1049 return true;
1050
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001051 /* Manual RF policy means we intentionally tuned with a baseband
1052 * offset for dual-channel purposes. Now retune the other channel
1053 * with the opposite corresponding frequency offset
1054 */
1055 if (treq.rf_freq_policy == uhd::tune_request_t::POLICY_MANUAL) {
1056 if (tx) {
1057 treq = select_freq(tx_freqs[!chan], !chan, true);
1058 tres = usrp_dev->set_tx_freq(treq, !chan);
1059 tx_freqs[!chan] = usrp_dev->get_tx_freq(!chan);
1060 } else {
1061 treq = select_freq(rx_freqs[!chan], !chan, false);
1062 tres = usrp_dev->set_rx_freq(treq, !chan);
1063 rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan);
1064
1065 }
Pau Espin Pedrol9279e0e2019-12-20 23:02:53 +01001066 LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001067 }
1068
1069 return true;
1070}
1071
Thomas Tsou204a9f12013-10-29 18:34:16 -04001072bool uhd_device::setTxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001073{
Pau Espin Pedrol056ce132020-06-08 12:13:11 +02001074 uint16_t req_arfcn;
1075 enum gsm_band req_band;
Pau Espin Pedrol056ce132020-06-08 12:13:11 +02001076
Thomas Tsou204a9f12013-10-29 18:34:16 -04001077 if (chan >= tx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001078 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001079 return false;
1080 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001081 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001082
Pau Espin Pedrol056ce132020-06-08 12:13:11 +02001083 req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
1084 if (req_arfcn == 0xffff) {
1085 LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
1086 return false;
1087 }
1088 if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
1089 LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
1090 << " Hz (ARFCN " << req_arfcn << " )";
1091 return false;
1092 }
1093
Pau Espin Pedrole91544d2020-10-13 17:03:37 +02001094 if (!set_band(req_band))
Pau Espin Pedrol056ce132020-06-08 12:13:11 +02001095 return false;
Pau Espin Pedrol056ce132020-06-08 12:13:11 +02001096
1097 if (!set_freq(wFreq, chan, true))
1098 return false;
1099
Pau Espin Pedrol056ce132020-06-08 12:13:11 +02001100 return true;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001101}
1102
Thomas Tsou204a9f12013-10-29 18:34:16 -04001103bool uhd_device::setRxFreq(double wFreq, size_t chan)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001104{
Pau Espin Pedrol069f5cd2021-09-21 13:50:38 +02001105 uint16_t req_arfcn;
1106 enum gsm_band req_band;
1107
Thomas Tsou204a9f12013-10-29 18:34:16 -04001108 if (chan >= rx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001109 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001110 return false;
1111 }
Tom Tsou93b7f372014-12-15 20:23:33 -08001112 ScopedLock lock(tune_lock);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001113
Pau Espin Pedrol069f5cd2021-09-21 13:50:38 +02001114 req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
1115 if (req_arfcn == 0xffff) {
1116 LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
1117 return false;
1118 }
1119 if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
1120 LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq
1121 << " Hz (ARFCN " << req_arfcn << " )";
1122 return false;
1123 }
1124
1125 if (!set_band(req_band))
1126 return false;
1127
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001128 return set_freq(wFreq, chan, false);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001129}
1130
Thomas Tsou204a9f12013-10-29 18:34:16 -04001131double uhd_device::getTxFreq(size_t chan)
1132{
1133 if (chan >= tx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001134 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001135 return 0.0;
1136 }
1137
1138 return tx_freqs[chan];
1139}
1140
1141double uhd_device::getRxFreq(size_t chan)
1142{
1143 if (chan >= rx_freqs.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001144 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001145 return 0.0;
1146 }
1147
1148 return rx_freqs[chan];
1149}
1150
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001151bool uhd_device::setRxAntenna(const std::string &ant, size_t chan)
1152{
1153 std::vector<std::string> avail;
1154 if (chan >= rx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001155 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001156 return false;
1157 }
1158
Vadim Yanitskiy68d8db42020-06-29 17:39:35 +07001159 /* UHD may throw a LookupError/IndexError here (see OS#4636) */
1160 try {
1161 avail = usrp_dev->get_rx_antennas(chan);
1162 } catch (const uhd::index_error &e) {
1163 LOGC(DDEV, ALERT) << "UHD Error: " << e.what();
1164 return false;
1165 }
1166
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001167 if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001168 LOGC(DDEV, ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan;
1169 LOGC(DDEV, INFO) << "Available Rx antennas: ";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001170 for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
Harald Welte5cc88582018-08-17 19:55:38 +02001171 LOGC(DDEV, INFO) << "- '" << *i << "'";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001172 return false;
1173 }
1174 usrp_dev->set_rx_antenna(ant, chan);
1175 rx_paths[chan] = usrp_dev->get_rx_antenna(chan);
1176
1177 if (ant != rx_paths[chan]) {
Harald Welte5cc88582018-08-17 19:55:38 +02001178 LOGC(DDEV, ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan];
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001179 return false;
1180 }
1181
1182 return true;
1183}
1184
1185std::string uhd_device::getRxAntenna(size_t chan)
1186{
1187 if (chan >= rx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001188 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001189 return "";
1190 }
1191 return usrp_dev->get_rx_antenna(chan);
1192}
1193
1194bool uhd_device::setTxAntenna(const std::string &ant, size_t chan)
1195{
1196 std::vector<std::string> avail;
1197 if (chan >= tx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001198 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001199 return false;
1200 }
1201
Vadim Yanitskiy68d8db42020-06-29 17:39:35 +07001202 /* UHD may throw a LookupError/IndexError here (see OS#4636) */
1203 try {
1204 avail = usrp_dev->get_tx_antennas(chan);
1205 } catch (const uhd::index_error &e) {
1206 LOGC(DDEV, ALERT) << "UHD Error: " << e.what();
1207 return false;
1208 }
1209
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001210 if (std::find(avail.begin(), avail.end(), ant) == avail.end()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001211 LOGC(DDEV, ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan;
1212 LOGC(DDEV, INFO) << "Available Tx antennas: ";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001213 for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i)
Harald Welte5cc88582018-08-17 19:55:38 +02001214 LOGC(DDEV, INFO) << "- '" << *i << "'";
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001215 return false;
1216 }
1217 usrp_dev->set_tx_antenna(ant, chan);
1218 tx_paths[chan] = usrp_dev->get_tx_antenna(chan);
1219
1220 if (ant != tx_paths[chan]) {
Harald Welte5cc88582018-08-17 19:55:38 +02001221 LOGC(DDEV, ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan];
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001222 return false;
1223 }
1224
1225 return true;
1226}
1227
1228std::string uhd_device::getTxAntenna(size_t chan)
1229{
1230 if (chan >= tx_paths.size()) {
Harald Welte5cc88582018-08-17 19:55:38 +02001231 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
Pau Espin Pedrol77ce99a2018-02-05 13:05:06 +01001232 return "";
1233 }
1234 return usrp_dev->get_tx_antenna(chan);
1235}
1236
Pau Espin Pedrol0fc20d12018-04-24 17:48:52 +02001237bool uhd_device::requiresRadioAlign()
1238{
1239 return false;
1240}
1241
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001242GSM::Time uhd_device::minLatency() {
1243 /* Empirical data from a handful of
1244 relatively recent machines shows that the B100 will underrun when
1245 the transmit threshold is reduced to a time of 6 and a half frames,
1246 so we set a minimum 7 frame threshold. */
1247 return GSM::Time(6,7);
1248}
1249
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001250/*
1251 * Only allow sampling the Rx path lower than Tx and not vice-versa.
1252 * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
1253 * combination.
1254 */
1255TIMESTAMP uhd_device::initialWriteTimestamp()
1256{
Tom Tsou76764272016-06-24 14:25:39 -07001257 if ((iface == MULTI_ARFCN) || (rx_sps == tx_sps))
Tom Tsou5cd70dc2016-03-06 01:28:40 -08001258 return ts_initial;
1259 else
1260 return ts_initial * tx_sps;
1261}
1262
1263TIMESTAMP uhd_device::initialReadTimestamp()
1264{
1265 return ts_initial;
1266}
1267
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001268double uhd_device::fullScaleInputValue()
1269{
Alexander Chemeriscbfef6e2016-02-05 00:52:49 -08001270 if (dev_type == LIMESDR)
ignasj28d80812017-06-13 23:37:46 +03001271 return (double) SHRT_MAX * LIMESDR_TX_AMPL;
Alexander Chemeris88bbf1a2015-03-25 14:22:37 +03001272 if (dev_type == UMTRX)
1273 return (double) SHRT_MAX * UMTRX_TX_AMPL;
1274 else
1275 return (double) SHRT_MAX * USRP_TX_AMPL;
1276}
1277
1278double uhd_device::fullScaleOutputValue()
1279{
1280 return (double) SHRT_MAX;
1281}
1282
kurtis.heimerl965e7572011-11-26 03:16:54 +00001283bool uhd_device::recv_async_msg()
1284{
ttsou60dc4c92012-08-08 23:30:23 +00001285 uhd::async_metadata_t md;
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001286
1287 thread_enable_cancel(false);
1288 bool rc = usrp_dev->get_device()->recv_async_msg(md);
1289 thread_enable_cancel(true);
1290 if (!rc)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001291 return false;
1292
1293 // Assume that any error requires resynchronization
ttsou60dc4c92012-08-08 23:30:23 +00001294 if (md.event_code != uhd::async_metadata_t::EVENT_CODE_BURST_ACK) {
kurtis.heimerl965e7572011-11-26 03:16:54 +00001295 aligned = false;
ttsou60dc4c92012-08-08 23:30:23 +00001296
1297 if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) &&
1298 (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) {
Pau Espin Pedrolac927b22019-04-30 18:19:54 +02001299 LOGC(DDEV, ERROR) << str_code(md);
ttsou60dc4c92012-08-08 23:30:23 +00001300 }
kurtis.heimerl965e7572011-11-26 03:16:54 +00001301 }
1302
1303 return true;
1304}
1305
1306std::string uhd_device::str_code(uhd::rx_metadata_t metadata)
1307{
1308 std::ostringstream ost("UHD: ");
1309
1310 switch (metadata.error_code) {
1311 case uhd::rx_metadata_t::ERROR_CODE_NONE:
1312 ost << "No error";
1313 break;
1314 case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
1315 ost << "No packet received, implementation timed-out";
1316 break;
1317 case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
1318 ost << "A stream command was issued in the past";
1319 break;
1320 case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
1321 ost << "Expected another stream command";
1322 break;
1323 case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
1324 ost << "An internal receive buffer has filled";
1325 break;
Thomas Tsou3ebc7722014-02-13 15:09:00 -05001326 case uhd::rx_metadata_t::ERROR_CODE_ALIGNMENT:
1327 ost << "Multi-channel alignment failed";
1328 break;
kurtis.heimerl965e7572011-11-26 03:16:54 +00001329 case uhd::rx_metadata_t::ERROR_CODE_BAD_PACKET:
1330 ost << "The packet could not be parsed";
1331 break;
1332 default:
1333 ost << "Unknown error " << metadata.error_code;
1334 }
1335
1336 if (metadata.has_time_spec)
1337 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1338
1339 return ost.str();
1340}
1341
1342std::string uhd_device::str_code(uhd::async_metadata_t metadata)
1343{
1344 std::ostringstream ost("UHD: ");
1345
1346 switch (metadata.event_code) {
1347 case uhd::async_metadata_t::EVENT_CODE_BURST_ACK:
1348 ost << "A packet was successfully transmitted";
1349 break;
1350 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW:
1351 ost << "An internal send buffer has emptied";
1352 break;
1353 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR:
1354 ost << "Packet loss between host and device";
1355 break;
1356 case uhd::async_metadata_t::EVENT_CODE_TIME_ERROR:
1357 ost << "Packet time was too late or too early";
1358 break;
1359 case uhd::async_metadata_t::EVENT_CODE_UNDERFLOW_IN_PACKET:
1360 ost << "Underflow occurred inside a packet";
1361 break;
1362 case uhd::async_metadata_t::EVENT_CODE_SEQ_ERROR_IN_BURST:
1363 ost << "Packet loss within a burst";
1364 break;
1365 default:
1366 ost << "Unknown error " << metadata.event_code;
1367 }
1368
1369 if (metadata.has_time_spec)
1370 ost << " at " << metadata.time_spec.get_real_secs() << " sec.";
1371
1372 return ost.str();
1373}
1374
Eric Wild1e17c4f2020-03-24 17:19:27 +01001375#ifndef IPCMAGIC
Eric19e134a2023-05-10 23:50:38 +02001376RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
kurtis.heimerl965e7572011-11-26 03:16:54 +00001377{
Eric19e134a2023-05-10 23:50:38 +02001378 return new uhd_device(type, cfg);
kurtis.heimerl965e7572011-11-26 03:16:54 +00001379}
Eric Wild1e17c4f2020-03-24 17:19:27 +01001380#endif