blob: 4f0110b91435806b37e95c724bcc0fa03bec3d94 [file] [log] [blame]
Eric7d897cb2022-11-28 19:20:32 +01001/*
2 * Copyright 2022 sysmocom - s.f.m.c. GmbH
3 *
4 * Author: Eric Wild <ewild@sysmocom.de>
5 *
6 * SPDX-License-Identifier: AGPL-3.0+
7 *
8 * This program is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU Affero General Public License as published by
10 * the Free Software Foundation, either version 3 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU Affero General Public License for more details.
17 *
18 * You should have received a copy of the GNU Affero General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>.
20 * See the COPYING file in the main directory for details.
21 */
22
23#include <map>
24#include <libbladeRF.h>
25#include "radioDevice.h"
26#include "bladerf.h"
27#include "Threads.h"
28#include "Logger.h"
29
30#ifdef HAVE_CONFIG_H
31#include "config.h"
32#endif
33
34extern "C" {
35#include <osmocom/core/utils.h>
36#include <osmocom/gsm/gsm_utils.h>
37#include <osmocom/vty/cpu_sched_vty.h>
38}
39
40#define SAMPLE_BUF_SZ (1 << 20)
41
42#define B2XX_TIMING_4_4SPS 6.18462e-5
43
44#define CHKRET() \
45 { \
46 if (status != 0) \
47 LOGC(DDEV, ERROR) << bladerf_strerror(status); \
48 }
49
50/* Device Type, Tx-SPS, Rx-SPS */
51typedef std::tuple<blade_dev_type, int, int> dev_key;
52
53/* Device parameter descriptor */
54struct dev_desc {
55 unsigned channels;
56 double mcr;
57 double rate;
58 double offset;
59 std::string str;
60};
61
62static const std::map<dev_key, dev_desc> dev_param_map{
63 { std::make_tuple(blade_dev_type::BLADE2, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
64};
65
66typedef std::tuple<blade_dev_type, enum gsm_band> dev_band_key;
67typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
68static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map{
69 { std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
70 { std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
71 { std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
72 { std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_1900), { 89.75, 7.7, -11.0 } },
73};
74
75/* So far measurements done for B210 show really close to linear relationship
76 * between gain and real output power, so we simply adjust the measured offset
77 */
78static double TxGain2TxPower(const dev_band_desc &desc, double tx_gain_db)
79{
80 return desc.nom_out_tx_power - (desc.nom_uhd_tx_gain - tx_gain_db);
81}
82static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
83{
84 return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
85}
86
87blade_device::blade_device(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
88 const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
89 : RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths), dev(nullptr), rx_gain_min(0.0),
90 rx_gain_max(0.0), band_ass_curr_sess(false), band((enum gsm_band)0), tx_spp(0), rx_spp(0), started(false),
91 aligned(false), drop_cnt(0), prev_ts(0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
92{
93}
94
95blade_device::~blade_device()
96{
97 if (dev) {
98 bladerf_enable_module(dev, BLADERF_CHANNEL_RX(0), false);
99 bladerf_enable_module(dev, BLADERF_CHANNEL_TX(0), false);
100 }
101
102 stop();
103
104 for (size_t i = 0; i < rx_buffers.size(); i++)
105 delete rx_buffers[i];
106}
107
108void blade_device::assign_band_desc(enum gsm_band req_band)
109{
110 dev_band_map_it it;
111
112 it = dev_band_nom_power_param_map.find(dev_band_key(dev_type, req_band));
113 if (it == dev_band_nom_power_param_map.end()) {
114 dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
115 LOGC(DDEV, ERROR) << "No Power parameters exist for device " << desc.str << " on band "
116 << gsm_band_name(req_band) << ", using B210 ones as fallback";
117 it = dev_band_nom_power_param_map.find(dev_band_key(blade_dev_type::BLADE2, req_band));
118 }
119 OSMO_ASSERT(it != dev_band_nom_power_param_map.end())
120 band_desc = it->second;
121}
122
123bool blade_device::set_band(enum gsm_band req_band)
124{
125 if (band_ass_curr_sess && req_band != band) {
126 LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band) << " different from previous band "
127 << gsm_band_name(band);
128 return false;
129 }
130
131 if (req_band != band) {
132 band = req_band;
133 assign_band_desc(band);
134 }
135 band_ass_curr_sess = true;
136 return true;
137}
138
139void blade_device::get_dev_band_desc(dev_band_desc &desc)
140{
141 if (band == 0) {
142 LOGC(DDEV, ERROR)
143 << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
144 assign_band_desc(GSM_BAND_900);
145 }
146 desc = band_desc;
147}
148
149void blade_device::init_gains()
150{
151 double tx_gain_min, tx_gain_max;
152 int status;
153
154 const struct bladerf_range *r;
155 bladerf_get_gain_range(dev, BLADERF_RX, &r);
156
157 rx_gain_min = r->min;
158 rx_gain_max = r->max;
159 LOGC(DDEV, INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
160
161 for (size_t i = 0; i < rx_gains.size(); i++) {
162 double gain = (rx_gain_min + rx_gain_max) / 2;
163 status = bladerf_set_gain_mode(dev, BLADERF_CHANNEL_RX(i), BLADERF_GAIN_MGC);
164 CHKRET()
165 bladerf_gain_mode m;
166 bladerf_get_gain_mode(dev, BLADERF_CHANNEL_RX(i), &m);
167 LOGC(DDEV, INFO) << (m == BLADERF_GAIN_MANUAL ? "gain manual" : "gain AUTO");
168
169 status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0);
170 CHKRET()
171 int actual_gain;
172 status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain);
173 CHKRET()
174 LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain << " scale "
175 << r->scale << " actual " << actual_gain;
176 rx_gains[i] = actual_gain;
177
178 status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0);
179 CHKRET()
180 status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain);
181 CHKRET()
182 LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain << " scale "
183 << r->scale << " actual " << actual_gain;
184 rx_gains[i] = actual_gain;
185 }
186
187 status = bladerf_get_gain_range(dev, BLADERF_TX, &r);
188 CHKRET()
189 tx_gain_min = r->min;
190 tx_gain_max = r->max;
191 LOGC(DDEV, INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
192
193 for (size_t i = 0; i < tx_gains.size(); i++) {
194 double gain = (tx_gain_min + tx_gain_max) / 2;
195 status = bladerf_set_gain(dev, BLADERF_CHANNEL_TX(i), 30);
196 CHKRET()
197 int actual_gain;
198 status = bladerf_get_gain(dev, BLADERF_CHANNEL_TX(i), &actual_gain);
199 CHKRET()
200 LOGC(DDEV, INFO) << "Default setting Tx gain for channel " << i << " to " << gain << " scale "
201 << r->scale << " actual " << actual_gain;
202 tx_gains[i] = actual_gain;
203 }
204
205 return;
206}
207
208void blade_device::set_rates()
209{
210 struct bladerf_rational_rate rate = { 0, static_cast<uint64_t>((1625e3 * 4)), 6 }, actual;
211 auto status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_RX(0), &rate, &actual);
212 CHKRET()
213 status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_TX(0), &rate, &actual);
214 CHKRET()
215
216 tx_rate = rx_rate = (double)rate.num / (double)rate.den;
217
218 LOGC(DDEV, INFO) << "Rates set to" << tx_rate << " / " << rx_rate;
219
220 bladerf_set_bandwidth(dev, BLADERF_CHANNEL_RX(0), (bladerf_bandwidth)2e6, (bladerf_bandwidth *)NULL);
221 bladerf_set_bandwidth(dev, BLADERF_CHANNEL_TX(0), (bladerf_bandwidth)2e6, (bladerf_bandwidth *)NULL);
222
223 ts_offset = 60; // FIXME: actual blade offset, should equal b2xx
224}
225
226double blade_device::setRxGain(double db, size_t chan)
227{
228 if (chan >= rx_gains.size()) {
229 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
230 return 0.0f;
231 }
232
233 bladerf_set_gain(dev, BLADERF_CHANNEL_RX(chan), 30); //db);
234 int actual_gain;
235 bladerf_get_gain(dev, BLADERF_CHANNEL_RX(chan), &actual_gain);
236
237 rx_gains[chan] = actual_gain;
238
239 LOGC(DDEV, INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
240
241 return rx_gains[chan];
242}
243
244double blade_device::getRxGain(size_t chan)
245{
246 if (chan >= rx_gains.size()) {
247 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
248 return 0.0f;
249 }
250
251 return rx_gains[chan];
252}
253
254double blade_device::rssiOffset(size_t chan)
255{
256 double rssiOffset;
257 dev_band_desc desc;
258
259 if (chan >= rx_gains.size()) {
260 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
261 return 0.0f;
262 }
263
264 get_dev_band_desc(desc);
265 rssiOffset = rx_gains[chan] + desc.rxgain2rssioffset_rel;
266 return rssiOffset;
267}
268
269double blade_device::setPowerAttenuation(int atten, size_t chan)
270{
271 double tx_power, db;
272 dev_band_desc desc;
273
274 if (chan >= tx_gains.size()) {
275 LOGC(DDEV, ALERT) << "Requested non-existent channel" << chan;
276 return 0.0f;
277 }
278
279 get_dev_band_desc(desc);
280 tx_power = desc.nom_out_tx_power - atten;
281 db = TxPower2TxGain(desc, tx_power);
282
283 bladerf_set_gain(dev, BLADERF_CHANNEL_TX(chan), 30);
284 int actual_gain;
285 bladerf_get_gain(dev, BLADERF_CHANNEL_RX(chan), &actual_gain);
286
287 tx_gains[chan] = actual_gain;
288
289 LOGC(DDEV, INFO)
290 << "Set TX gain to " << tx_gains[chan] << "dB, ~" << TxGain2TxPower(desc, tx_gains[chan]) << " dBm "
291 << "(asked for " << db << " dB, ~" << tx_power << " dBm)";
292
293 return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
294}
295double blade_device::getPowerAttenuation(size_t chan)
296{
297 dev_band_desc desc;
298 if (chan >= tx_gains.size()) {
299 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
300 return 0.0f;
301 }
302
303 get_dev_band_desc(desc);
304 return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
305}
306
307int blade_device::getNominalTxPower(size_t chan)
308{
309 dev_band_desc desc;
310 get_dev_band_desc(desc);
311
312 return desc.nom_out_tx_power;
313}
314
315int blade_device::open(const std::string &args, int ref, bool swap_channels)
316{
317 bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_VERBOSE);
318 bladerf_set_usb_reset_on_open(true);
319 auto success = bladerf_open(&dev, args.c_str());
320 if (success != 0) {
321 struct bladerf_devinfo *info;
322 auto num_devs = bladerf_get_device_list(&info);
323 LOGC(DDEV, ALERT) << "No bladerf devices found with identifier '" << args << "'";
324 if (num_devs) {
325 for (int i = 0; i < num_devs; i++)
326 LOGC(DDEV, ALERT) << "Found device:" << info[i].product << " serial " << info[i].serial;
327 }
328
329 return -1;
330 }
331 if (strcmp("bladerf2", bladerf_get_board_name(dev))) {
332 LOGC(DDEV, ALERT) << "Only BladeRF2 supported! found:" << bladerf_get_board_name(dev);
333 return -1;
334 }
335
336 dev_type = blade_dev_type::BLADE2;
337 tx_window = TX_WINDOW_FIXED;
338
339 struct bladerf_devinfo info;
340 bladerf_get_devinfo(dev, &info);
341 LOGC(DDEV, INFO) << "Using discovered bladerf device " << info.serial;
342
343 tx_freqs.resize(chans);
344 rx_freqs.resize(chans);
345 tx_gains.resize(chans);
346 rx_gains.resize(chans);
347 rx_buffers.resize(chans);
348
349 switch (ref) {
350 case REF_INTERNAL:
351 case REF_EXTERNAL:
352 break;
353 default:
354 LOGC(DDEV, ALERT) << "Invalid reference type";
355 return -1;
356 }
357
358 if (ref == REF_EXTERNAL) {
359 bool is_locked;
360 int status = bladerf_set_pll_enable(dev, true);
361 CHKRET()
362 status = bladerf_set_pll_refclk(dev, 10000000);
363 CHKRET()
364 for (int i = 0; i < 20; i++) {
365 usleep(50 * 1000);
366 status = bladerf_get_pll_lock_state(dev, &is_locked);
367 CHKRET()
368 if (is_locked)
369 break;
370 }
371 if (!is_locked) {
372 LOGC(DDEV, ALERT) << "unable to lock refclk!";
373 return -1;
374 }
375 }
376
377 LOGC(DDEV, INFO) << "Selected clock source is " << ((ref == REF_INTERNAL) ? "internal" : "external 10Mhz");
378
379 set_rates();
380
381 /*
382 1ts = 3/5200s
383 1024*2 = small gap(~180us) every 9.23ms = every 16 ts? -> every 2 frames
384 1024*1 = large gap(~627us) every 9.23ms = every 16 ts? -> every 2 frames
385
386 rif convertbuffer = 625*4 = 2500 -> 4 ts
387 rif rxtxbuf = 4 * segment(625*4) = 10000 -> 16 ts
388 */
389 const unsigned int num_buffers = 256;
390 const unsigned int buffer_size = 1024 * 4; /* Must be a multiple of 1024 */
391 const unsigned int num_transfers = 32;
392 const unsigned int timeout_ms = 3500;
393
394 bladerf_sync_config(dev, BLADERF_RX_X1, BLADERF_FORMAT_SC16_Q11_META, num_buffers, buffer_size, num_transfers,
395 timeout_ms);
396
397 bladerf_sync_config(dev, BLADERF_TX_X1, BLADERF_FORMAT_SC16_Q11_META, num_buffers, buffer_size, num_transfers,
398 timeout_ms);
399
400 /* Number of samples per over-the-wire packet */
401 tx_spp = rx_spp = buffer_size;
402
403 size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
404 for (size_t i = 0; i < rx_buffers.size(); i++)
405 rx_buffers[i] = new smpl_buf(buf_len);
406
407 pkt_bufs = std::vector<std::vector<short> >(chans, std::vector<short>(2 * rx_spp));
408 for (size_t i = 0; i < pkt_bufs.size(); i++)
409 pkt_ptrs.push_back(&pkt_bufs[i].front());
410
411 init_gains();
412
413 return NORMAL;
414}
415
416bool blade_device::restart()
417{
418 /* Allow 100 ms delay to align multi-channel streams */
419 double delay = 0.2;
420 int status;
421
422 status = bladerf_enable_module(dev, BLADERF_CHANNEL_RX(0), true);
423 CHKRET()
424 status = bladerf_enable_module(dev, BLADERF_CHANNEL_TX(0), true);
425 CHKRET()
426
427 bladerf_timestamp now;
428 status = bladerf_get_timestamp(dev, BLADERF_RX, &now);
429 ts_initial = now + rx_rate * delay;
430 LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
431
432 return true;
433}
434
435bool blade_device::start()
436{
437 LOGC(DDEV, INFO) << "Starting USRP...";
438
439 if (started) {
440 LOGC(DDEV, ERROR) << "Device already started";
441 return false;
442 }
443
444 if (!restart())
445 return false;
446
447 started = true;
448 return true;
449}
450
451bool blade_device::stop()
452{
453 if (!started)
454 return false;
455
456 /* reset internal buffer timestamps */
457 for (size_t i = 0; i < rx_buffers.size(); i++)
458 rx_buffers[i]->reset();
459
460 band_ass_curr_sess = false;
461
462 started = false;
463 return true;
464}
465
466int blade_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun)
467{
468 ssize_t rc;
469 uint64_t ts;
470
471 if (bufs.size() != chans) {
472 LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
473 return -1;
474 }
475
476 *overrun = false;
477 *underrun = false;
478
479 // Shift read time with respect to transmit clock
480 timestamp += ts_offset;
481
482 ts = timestamp;
483 LOGC(DDEV, DEBUG) << "Requested timestamp = " << ts;
484
485 // Check that timestamp is valid
486 rc = rx_buffers[0]->avail_smpls(timestamp);
487 if (rc < 0) {
488 LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc);
489 LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp);
490 return 0;
491 }
492
493 struct bladerf_metadata meta = {};
494 meta.timestamp = ts;
495
496 while (rx_buffers[0]->avail_smpls(timestamp) < len) {
497 thread_enable_cancel(false);
498 int status = bladerf_sync_rx(dev, pkt_ptrs[0], len, &meta, 200U);
499 thread_enable_cancel(true);
500
501 if (status != 0)
502 LOGC(DDEV, ERROR) << "RX broken: " << bladerf_strerror(status);
503 if (meta.flags & BLADERF_META_STATUS_OVERRUN)
504 LOGC(DDEV, ERROR) << "RX borken, OVERRUN: " << bladerf_strerror(status);
505
506 size_t num_smpls = meta.actual_count;
507 ;
508 ts = meta.timestamp;
509
510 for (size_t i = 0; i < rx_buffers.size(); i++) {
511 rc = rx_buffers[i]->write((short *)&pkt_bufs[i].front(), num_smpls, ts);
512
513 // Continue on local overrun, exit on other errors
514 if ((rc < 0)) {
515 LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
516 LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
517 if (rc != smpl_buf::ERROR_OVERFLOW)
518 return 0;
519 }
520 }
521 meta = {};
522 meta.timestamp = ts + num_smpls;
523 }
524
525 for (size_t i = 0; i < rx_buffers.size(); i++) {
526 rc = rx_buffers[i]->read(bufs[i], len, timestamp);
527 if ((rc < 0) || (rc != len)) {
528 LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
529 LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
530 return 0;
531 }
532 }
533
534 return len;
535}
536
537int blade_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun, unsigned long long timestamp)
538{
539 *underrun = false;
540 static bool first_tx = true;
541 struct bladerf_metadata meta = {};
542 if (first_tx) {
543 meta.timestamp = timestamp;
544 meta.flags = BLADERF_META_FLAG_TX_BURST_START;
545 first_tx = false;
546 }
547
548 thread_enable_cancel(false);
549 int status = bladerf_sync_tx(dev, (const void *)bufs[0], len, &meta, 200U);
550 thread_enable_cancel(true);
551
552 if (status != 0)
553 LOGC(DDEV, ERROR) << "TX broken: " << bladerf_strerror(status);
554
555 return len;
556}
557
558bool blade_device::updateAlignment(TIMESTAMP timestamp)
559{
560 return true;
561}
562
563bool blade_device::set_freq(double freq, size_t chan, bool tx)
564{
565 if (tx) {
566 bladerf_set_frequency(dev, BLADERF_CHANNEL_TX(chan), freq);
567 bladerf_frequency f;
568 bladerf_get_frequency(dev, BLADERF_CHANNEL_TX(chan), &f);
569 tx_freqs[chan] = f;
570 } else {
571 bladerf_set_frequency(dev, BLADERF_CHANNEL_RX(chan), freq);
572 bladerf_frequency f;
573 bladerf_get_frequency(dev, BLADERF_CHANNEL_RX(chan), &f);
574 rx_freqs[chan] = f;
575 }
576 LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << (tx ? "TX" : "RX") << "): " << std::endl;
577
578 return true;
579}
580
581bool blade_device::setTxFreq(double wFreq, size_t chan)
582{
583 uint16_t req_arfcn;
584 enum gsm_band req_band;
585
586 if (chan >= tx_freqs.size()) {
587 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
588 return false;
589 }
590 ScopedLock lock(tune_lock);
591
592 req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 0);
593 if (req_arfcn == 0xffff) {
594 LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
595 return false;
596 }
597 if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
598 LOGCHAN(chan, DDEV, ALERT)
599 << "Unknown GSM band for Tx Frequency " << wFreq << " Hz (ARFCN " << req_arfcn << " )";
600 return false;
601 }
602
603 if (!set_band(req_band))
604 return false;
605
606 if (!set_freq(wFreq, chan, true))
607 return false;
608
609 return true;
610}
611
612bool blade_device::setRxFreq(double wFreq, size_t chan)
613{
614 uint16_t req_arfcn;
615 enum gsm_band req_band;
616
617 if (chan >= rx_freqs.size()) {
618 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
619 return false;
620 }
621 ScopedLock lock(tune_lock);
622
623 req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
624 if (req_arfcn == 0xffff) {
625 LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
626 return false;
627 }
628 if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
629 LOGCHAN(chan, DDEV, ALERT)
630 << "Unknown GSM band for Rx Frequency " << wFreq << " Hz (ARFCN " << req_arfcn << " )";
631 return false;
632 }
633
634 if (!set_band(req_band))
635 return false;
636
637 return set_freq(wFreq, chan, false);
638}
639
640double blade_device::getTxFreq(size_t chan)
641{
642 if (chan >= tx_freqs.size()) {
643 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
644 return 0.0;
645 }
646
647 return tx_freqs[chan];
648}
649
650double blade_device::getRxFreq(size_t chan)
651{
652 if (chan >= rx_freqs.size()) {
653 LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
654 return 0.0;
655 }
656
657 return rx_freqs[chan];
658}
659
660bool blade_device::requiresRadioAlign()
661{
662 return false;
663}
664
665GSM::Time blade_device::minLatency()
666{
667 return GSM::Time(6, 7);
668}
669
670TIMESTAMP blade_device::initialWriteTimestamp()
671{
672 return ts_initial;
673}
674
675TIMESTAMP blade_device::initialReadTimestamp()
676{
677 return ts_initial;
678}
679
680double blade_device::fullScaleInputValue()
681{
682 return (double)2047;
683}
684
685double blade_device::fullScaleOutputValue()
686{
687 return (double)2047;
688}
689
690RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
691 const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
692{
693 return new blade_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
694}