blob: 91f06ecad945917321fba17d964be4302092c106 [file] [log] [blame]
dburgessb3a0ca42011-10-12 07:44:40 +00001/*
2* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
3*
Pau Espin Pedrol21d03d32019-07-22 12:05:52 +02004* SPDX-License-Identifier: GPL-3.0+
5*
dburgessb3a0ca42011-10-12 07:44:40 +00006* This software is distributed under the terms of the GNU Public License.
7* See the COPYING file in the main directory for details.
8*
9* This use of this software may be subject to additional restrictions.
10* See the LEGAL file in the main directory for details.
11
12 This program is free software: you can redistribute it and/or modify
13 it under the terms of the GNU General Public License as published by
14 the Free Software Foundation, either version 3 of the License, or
15 (at your option) any later version.
16
17 This program is distributed in the hope that it will be useful,
18 but WITHOUT ANY WARRANTY; without even the implied warranty of
19 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 GNU General Public License for more details.
21
22 You should have received a copy of the GNU General Public License
23 along with this program. If not, see <http://www.gnu.org/licenses/>.
24*/
25
dburgessb3a0ca42011-10-12 07:44:40 +000026#include <stdio.h>
Pau Espin Pedrol27424a32019-08-07 12:45:53 +020027#include <netinet/in.h>
Alexander Chemerise8905a02015-06-03 23:47:56 -040028#include <iomanip> // std::setprecision
Alexander Chemerise692ce92015-06-12 00:15:31 -040029#include <fstream>
dburgessb3a0ca42011-10-12 07:44:40 +000030#include "Transceiver.h"
31#include <Logger.h>
32
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020033extern "C" {
34#include "osmo_signal.h"
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +020035#include "proto_trxd.h"
36
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +020037#include <osmocom/core/utils.h>
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +020038#include <osmocom/core/socket.h>
Vadim Yanitskiyb3123252019-07-15 23:53:08 +070039#include <osmocom/core/bits.h>
Pau Espin Pedrol553a2502020-07-29 18:05:25 +020040#include <osmocom/vty/cpu_sched_vty.h>
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020041}
42
ttsou2173abf2012-08-08 00:51:31 +000043#ifdef HAVE_CONFIG_H
44#include "config.h"
45#endif
dburgessb3a0ca42011-10-12 07:44:40 +000046
Alexander Chemerisd734e2d2013-06-16 14:30:58 +040047using namespace GSM;
48
Erica7143bf2020-04-08 01:39:17 +020049Transceiver *transceiver;
50
kurtis.heimerlec842de2012-11-23 08:37:32 +000051#define USB_LATENCY_INTRVL 10,0
ttsou2173abf2012-08-08 00:51:31 +000052
Thomas Tsoufa3a7872013-10-17 21:23:34 -040053/* Number of running values use in noise average */
54#define NOISE_CNT 20
ttsoue8dde022012-12-06 15:43:55 +000055
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +020056
57static void dispatch_trx_rate_ctr_change(TransceiverState *state, unsigned int chan) {
58 thread_enable_cancel(false);
59 state->ctrs.chan = chan;
60 osmo_signal_dispatch(SS_DEVICE, S_TRX_COUNTER_CHANGE, &state->ctrs);
61 thread_enable_cancel(true);
62}
63
Thomas Tsouf0782732013-10-29 15:55:47 -040064TransceiverState::TransceiverState()
Pau Espin Pedrol7d8676a2020-08-26 14:30:46 +020065 : mFiller(FILLER_ZERO), mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT),
Eric1f37e4d2020-09-04 20:36:34 +020066 mPower(0.0), mMuted(false), first_dl_fn_rcv()
Thomas Tsouf0782732013-10-29 15:55:47 -040067{
68 for (int i = 0; i < 8; i++) {
69 chanType[i] = Transceiver::NONE;
70 fillerModulus[i] = 26;
71 chanResponse[i] = NULL;
72 DFEForward[i] = NULL;
73 DFEFeedback[i] = NULL;
74
75 for (int n = 0; n < 102; n++)
76 fillerTable[n][i] = NULL;
77 }
Pau Espin Pedrola71c5d02020-07-01 11:50:01 +020078 memset(&ctrs, 0, sizeof(struct trx_counters));
Thomas Tsouf0782732013-10-29 15:55:47 -040079}
80
81TransceiverState::~TransceiverState()
82{
83 for (int i = 0; i < 8; i++) {
84 delete chanResponse[i];
85 delete DFEForward[i];
86 delete DFEFeedback[i];
87
88 for (int n = 0; n < 102; n++)
89 delete fillerTable[n][i];
90 }
91}
92
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010093bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
Tom Tsou64ad7122015-05-19 18:26:31 -070094{
Tom Tsou64ad7122015-05-19 18:26:31 -070095 signalVector *burst;
96
97 if ((sps != 1) && (sps != 4))
98 return false;
99
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +0200100 mFiller = filler;
101
Tom Tsou64ad7122015-05-19 18:26:31 -0700102 for (size_t n = 0; n < 8; n++) {
Tom Tsou64ad7122015-05-19 18:26:31 -0700103 for (size_t i = 0; i < 102; i++) {
104 switch (filler) {
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100105 case FILLER_DUMMY:
Tom Tsou8ee2f382016-03-06 20:57:34 -0800106 burst = generateDummyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -0700107 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100108 case FILLER_NORM_RAND:
Tom Tsou8ee2f382016-03-06 20:57:34 -0800109 burst = genRandNormalBurst(rtsc, sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -0700110 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100111 case FILLER_EDGE_RAND:
Tom Tsouaf717b22016-03-06 22:19:15 -0800112 burst = generateEdgeBurst(rtsc);
113 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100114 case FILLER_ACCESS_RAND:
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300115 burst = genRandAccessBurst(rach_delay, sps, n);
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300116 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100117 case FILLER_ZERO:
Tom Tsou64ad7122015-05-19 18:26:31 -0700118 default:
Tom Tsou8ee2f382016-03-06 20:57:34 -0800119 burst = generateEmptyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -0700120 }
121
122 scaleVector(*burst, scale);
123 fillerTable[i][n] = burst;
124 }
125
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100126 if ((filler == FILLER_NORM_RAND) ||
127 (filler == FILLER_EDGE_RAND)) {
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700128 chanType[n] = TSC;
Tom Tsouaf717b22016-03-06 22:19:15 -0800129 }
Thomas Tsou15d743e2014-01-25 02:34:03 -0500130 }
Tom Tsou64ad7122015-05-19 18:26:31 -0700131
132 return false;
Thomas Tsouf0782732013-10-29 15:55:47 -0400133}
134
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200135Transceiver::Transceiver(const struct trx_cfg *cfg,
Alexander Chemerise8905a02015-06-03 23:47:56 -0400136 GSM::Time wTransmitLatency,
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200137 RadioInterface *wRadioInterface)
138 : cfg(cfg), mClockSocket(-1),
Vadim Yanitskiyc7a750d2020-10-24 16:54:48 +0700139 mRxLowerLoopThread(nullptr), mTxLowerLoopThread(nullptr),
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200140 mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
141 mChans(cfg->num_chans), mOn(false), mForceClockInterface(false),
142 mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0),
143 mMaxExpectedDelayNB(0), mWriteBurstToDiskMask(0)
dburgessb3a0ca42011-10-12 07:44:40 +0000144{
dburgessb3a0ca42011-10-12 07:44:40 +0000145 txFullScale = mRadioInterface->fullScaleInputValue();
146 rxFullScale = mRadioInterface->fullScaleOutputValue();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300147
Vadim Yanitskiy0ff9c9f2020-10-24 16:48:33 +0700148 for (size_t i = 0; i < ARRAY_SIZE(mHandover); i++) {
149 for (size_t j = 0; j < ARRAY_SIZE(mHandover[i]); j++)
Alexander Chemeris5a068062015-06-20 01:38:47 +0300150 mHandover[i][j] = false;
151 }
dburgessb3a0ca42011-10-12 07:44:40 +0000152}
153
154Transceiver::~Transceiver()
155{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800156 stop();
157
dburgessb3a0ca42011-10-12 07:44:40 +0000158 sigProcLibDestroy();
Thomas Tsoud647ec52013-10-29 15:17:34 -0400159
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200160 if (mClockSocket >= 0)
161 close(mClockSocket);
162
Thomas Tsou204a9f12013-10-29 18:34:16 -0400163 for (size_t i = 0; i < mChans; i++) {
164 mTxPriorityQueues[i].clear();
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200165 if (mDataSockets[i] >= 0)
166 close(mDataSockets[i]);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400167 }
dburgessb3a0ca42011-10-12 07:44:40 +0000168}
Thomas Tsou83e06892013-08-20 16:10:01 -0400169
Erica7143bf2020-04-08 01:39:17 +0200170int Transceiver::ctrl_sock_cb(struct osmo_fd *bfd, unsigned int flags)
171{
172 int rc = 0;
173 int chan = static_cast<int>(reinterpret_cast<uintptr_t>(bfd->data));
174
175 if (flags & OSMO_FD_READ)
176 rc = transceiver->ctrl_sock_handle_rx(chan);
177 if (rc < 0)
178 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
179
180 if (flags & OSMO_FD_WRITE)
181 rc = transceiver->ctrl_sock_write(chan);
182 if (rc < 0)
183 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
184
185 return rc;
186}
187
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800188/*
189 * Initialize transceiver
190 *
191 * Start or restart the control loop. Any further control is handled through the
192 * socket API. Randomize the central radio clock set the downlink burst
193 * counters. Note that the clock will not update until the radio starts, but we
194 * are still expected to report clock indications through control channel
195 * activity.
196 */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200197bool Transceiver::init()
Thomas Tsou83e06892013-08-20 16:10:01 -0400198{
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500199 int d_srcport, d_dstport, c_srcport, c_dstport;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400200 if (!mChans) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100201 LOG(FATAL) << "No channels assigned";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400202 return false;
203 }
204
Tom Tsou2079a3c2016-03-06 00:58:56 -0800205 if (!sigProcLibSetup()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100206 LOG(FATAL) << "Failed to initialize signal processing library";
Thomas Tsou83e06892013-08-20 16:10:01 -0400207 return false;
208 }
209
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200210 mDataSockets.resize(mChans, -1);
Erica7143bf2020-04-08 01:39:17 +0200211 mCtrlSockets.resize(mChans);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400212 mTxPriorityQueueServiceLoopThreads.resize(mChans);
213 mRxServiceLoopThreads.resize(mChans);
214
215 mTxPriorityQueues.resize(mChans);
216 mReceiveFIFO.resize(mChans);
217 mStates.resize(mChans);
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200218 mVersionTRXD.resize(mChans);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400219
Thomas Tsouccb73e12014-04-15 17:41:28 -0400220 /* Filler table retransmissions - support only on channel 0 */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200221 if (cfg->filler == FILLER_DUMMY)
Thomas Tsouccb73e12014-04-15 17:41:28 -0400222 mStates[0].mRetrans = true;
223
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800224 /* Setup sockets */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200225 mClockSocket = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200226 cfg->bind_addr, cfg->base_port,
227 cfg->remote_addr, cfg->base_port + 100,
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200228 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
Pau Espin Pedrole7195ac2019-08-26 10:55:04 +0200229 if (mClockSocket < 0)
230 return false;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200231
Thomas Tsou204a9f12013-10-29 18:34:16 -0400232 for (size_t i = 0; i < mChans; i++) {
Erica7143bf2020-04-08 01:39:17 +0200233 int rv;
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200234 FillerType filler = cfg->filler;
235 c_srcport = cfg->base_port + 2 * i + 1;
236 c_dstport = cfg->base_port + 2 * i + 101;
237 d_srcport = cfg->base_port + 2 * i + 2;
238 d_dstport = cfg->base_port + 2 * i + 102;
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500239
Erica7143bf2020-04-08 01:39:17 +0200240 rv = osmo_sock_init2_ofd(&mCtrlSockets[i].conn_bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200241 cfg->bind_addr, c_srcport,
242 cfg->remote_addr, c_dstport,
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200243 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
Erica7143bf2020-04-08 01:39:17 +0200244 if (rv < 0)
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200245 return false;
246
Erica7143bf2020-04-08 01:39:17 +0200247 mCtrlSockets[i].conn_bfd.cb = ctrl_sock_cb;
248 mCtrlSockets[i].conn_bfd.data = reinterpret_cast<void*>(i);
249
250
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200251 mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200252 cfg->bind_addr, d_srcport,
253 cfg->remote_addr, d_dstport,
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200254 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
Eric1a26b4f2020-04-13 00:03:01 +0200255 if (mDataSockets[i] < 0)
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200256 return false;
Erica7143bf2020-04-08 01:39:17 +0200257
258 if (i && filler == FILLER_DUMMY)
259 filler = FILLER_ZERO;
260
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200261 mStates[i].init(filler, cfg->tx_sps, txFullScale, cfg->rtsc, cfg->rach_delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400262 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400263
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800264 /* Randomize the central clock */
265 GSM::Time startTime(random() % gHyperframe, 0);
266 mRadioInterface->getClock()->set(startTime);
267 mTransmitDeadlineClock = startTime;
268 mLastClockUpdateTime = startTime;
269 mLatencyUpdateTime = startTime;
270
Thomas Tsou83e06892013-08-20 16:10:01 -0400271 return true;
272}
dburgessb3a0ca42011-10-12 07:44:40 +0000273
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800274/*
275 * Start the transceiver
276 *
277 * Submit command(s) to the radio device to commence streaming samples and
278 * launch threads to handle sample I/O. Re-synchronize the transmit burst
279 * counters to the central radio clock here as well.
280 */
281bool Transceiver::start()
282{
283 ScopedLock lock(mLock);
284
285 if (mOn) {
286 LOG(ERR) << "Transceiver already running";
Ivan Kluchnikov194a9b12015-04-23 17:08:27 +0300287 return true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800288 }
289
290 LOG(NOTICE) << "Starting the transceiver";
291
292 GSM::Time time = mRadioInterface->getClock()->get();
293 mTransmitDeadlineClock = time;
294 mLastClockUpdateTime = time;
295 mLatencyUpdateTime = time;
296
297 if (!mRadioInterface->start()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100298 LOG(FATAL) << "Device failed to start";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800299 return false;
300 }
301
302 /* Device is running - launch I/O threads */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200303 mRxLowerLoopThread = new Thread(cfg->stack_size);
304 mTxLowerLoopThread = new Thread(cfg->stack_size);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800305 mTxLowerLoopThread->start((void * (*)(void*))
306 TxLowerLoopAdapter,(void*) this);
307 mRxLowerLoopThread->start((void * (*)(void*))
308 RxLowerLoopAdapter,(void*) this);
309
310 /* Launch uplink and downlink burst processing threads */
311 for (size_t i = 0; i < mChans; i++) {
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200312 TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
313 params->trx = this;
314 params->num = i;
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200315 mRxServiceLoopThreads[i] = new Thread(cfg->stack_size);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800316 mRxServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200317 RxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800318
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200319 params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
320 params->trx = this;
321 params->num = i;
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200322 mTxPriorityQueueServiceLoopThreads[i] = new Thread(cfg->stack_size);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800323 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200324 TxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800325 }
326
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200327 mForceClockInterface = true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800328 mOn = true;
329 return true;
330}
331
332/*
333 * Stop the transceiver
334 *
335 * Perform stopping by disabling receive streaming and issuing cancellation
336 * requests to running threads. Most threads will timeout and terminate once
337 * device is disabled, but the transmit loop may block waiting on the central
338 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
339 * makes it to the thread cancellation point.
340 */
341void Transceiver::stop()
342{
343 ScopedLock lock(mLock);
344
345 if (!mOn)
346 return;
347
348 LOG(NOTICE) << "Stopping the transceiver";
349 mTxLowerLoopThread->cancel();
350 mRxLowerLoopThread->cancel();
Tom Tsoud67bd602017-06-15 15:35:02 -0700351 mTxLowerLoopThread->join();
352 mRxLowerLoopThread->join();
353 delete mTxLowerLoopThread;
354 delete mRxLowerLoopThread;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800355
356 for (size_t i = 0; i < mChans; i++) {
357 mRxServiceLoopThreads[i]->cancel();
358 mTxPriorityQueueServiceLoopThreads[i]->cancel();
359 }
360
361 LOG(INFO) << "Stopping the device";
362 mRadioInterface->stop();
363
364 for (size_t i = 0; i < mChans; i++) {
365 mRxServiceLoopThreads[i]->join();
366 mTxPriorityQueueServiceLoopThreads[i]->join();
367 delete mRxServiceLoopThreads[i];
368 delete mTxPriorityQueueServiceLoopThreads[i];
369
370 mTxPriorityQueues[i].clear();
371 }
372
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800373 mOn = false;
374 LOG(NOTICE) << "Transceiver stopped";
375}
376
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500377void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400378 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000379{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500380 signalVector *burst;
381 radioVector *radio_burst;
382
Thomas Tsou204a9f12013-10-29 18:34:16 -0400383 if (chan >= mTxPriorityQueues.size()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100384 LOGCHAN(chan, DTRXDDL, FATAL) << "Invalid channel";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400385 return;
386 }
387
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500388 if (wTime.TN() > 7) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100389 LOGCHAN(chan, DTRXDDL, FATAL) << "Received burst with invalid slot " << wTime.TN();
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500390 return;
391 }
392
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800393 /* Use the number of bits as the EDGE burst indicator */
394 if (bits.size() == EDGE_BURST_NBITS)
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200395 burst = modulateEdgeBurst(bits, cfg->tx_sps);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800396 else
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200397 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), cfg->tx_sps);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800398
Vadim Yanitskiy2a637a52021-01-26 23:55:22 +0100399 scaleVector(*burst, txFullScale * pow(10, (double) -RSSI / 20));
dburgessb3a0ca42011-10-12 07:44:40 +0000400
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500401 radio_burst = new radioVector(wTime, burst);
402
403 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000404}
405
Thomas Tsou15d743e2014-01-25 02:34:03 -0500406void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
407{
408 int TN, modFN;
409 TransceiverState *state = &mStates[chan];
410
411 TN = burst->getTime().TN();
412 modFN = burst->getTime().FN() % state->fillerModulus[TN];
413
414 delete state->fillerTable[modFN][TN];
415 state->fillerTable[modFN][TN] = burst->getVector();
416 burst->setVector(NULL);
417}
418
dburgessb3a0ca42011-10-12 07:44:40 +0000419void Transceiver::pushRadioVector(GSM::Time &nowTime)
420{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400421 int TN, modFN;
422 radioVector *burst;
423 TransceiverState *state;
424 std::vector<signalVector *> bursts(mChans);
425 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500426 std::vector<bool> filler(mChans, true);
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200427 bool ratectr_changed;
dburgessb3a0ca42011-10-12 07:44:40 +0000428
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200429 TN = nowTime.TN();
430
Thomas Tsou204a9f12013-10-29 18:34:16 -0400431 for (size_t i = 0; i < mChans; i ++) {
432 state = &mStates[i];
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200433 ratectr_changed = false;
434
Pau Espin Pedrol7d8676a2020-08-26 14:30:46 +0200435 zeros[i] = state->chanType[TN] == NONE || state->mMuted;
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200436
437 Mutex *mtx = mTxPriorityQueues[i].getMutex();
438 mtx->lock();
dburgessb3a0ca42011-10-12 07:44:40 +0000439
Thomas Tsou204a9f12013-10-29 18:34:16 -0400440 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +0200441 LOGCHAN(i, DTRXDDL, INFO) << "dumping STALE burst in TRX->SDR interface ("
Pau Espin Pedrolf37b0ad2018-04-25 18:01:27 +0200442 << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200443 state->ctrs.tx_stale_bursts++;
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200444 ratectr_changed = true;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500445 if (state->mRetrans)
446 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500447 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400448 }
449
Thomas Tsou204a9f12013-10-29 18:34:16 -0400450 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500451 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500452
453 if (state->mRetrans) {
454 updateFillerTable(i, burst);
455 } else {
456 burst->setVector(NULL);
457 filler[i] = false;
458 }
459
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500460 delete burst;
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200461 } else {
462 modFN = nowTime.FN() % state->fillerModulus[TN];
463 bursts[i] = state->fillerTable[modFN][TN];
Pau Espin Pedroldf675782020-07-17 14:09:28 +0200464 if (i == 0 && state->mFiller == FILLER_ZERO) {
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +0200465 LOGCHAN(i, DTRXDDL, INFO) << "No Tx burst available for " << nowTime
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200466 << ", retrans=" << state->mRetrans;
467 state->ctrs.tx_unavailable_bursts++;
468 ratectr_changed = true;
469 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400470 }
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200471
472 mtx->unlock();
473
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200474 if (ratectr_changed)
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200475 dispatch_trx_rate_ctr_change(state, i);
dburgessb3a0ca42011-10-12 07:44:40 +0000476 }
477
Thomas Tsou204a9f12013-10-29 18:34:16 -0400478 mRadioInterface->driveTransmitRadio(bursts, zeros);
479
Thomas Tsou15d743e2014-01-25 02:34:03 -0500480 for (size_t i = 0; i < mChans; i++) {
481 if (!filler[i])
482 delete bursts[i];
483 }
dburgessb3a0ca42011-10-12 07:44:40 +0000484}
485
Thomas Tsou204a9f12013-10-29 18:34:16 -0400486void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000487{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400488 TransceiverState *state = &mStates[chan];
489
490 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000491 case NONE:
492 case I:
493 case II:
494 case III:
495 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400496 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000497 break;
498 case IV:
499 case VI:
500 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400501 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000502 break;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200503 //case V:
dburgessb3a0ca42011-10-12 07:44:40 +0000504 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400505 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000506 break;
ttsoufc40a842013-06-09 22:38:18 +0000507 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400508 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000509 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000510 default:
511 break;
512 }
513}
514
515
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700516CorrType Transceiver::expectedCorrType(GSM::Time currTime,
517 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000518{
Alexander Chemeris5a068062015-06-20 01:38:47 +0300519 static int tchh_subslot[26] = { 0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,1 };
520 static int sdcch4_subslot[102] = { 3,3,3,3,0,0,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2,
521 3,3,3,3,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2 };
522 static int sdcch8_subslot[102] = { 5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,
523 1,1,1,1,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,4,4,4,4 };
Thomas Tsou204a9f12013-10-29 18:34:16 -0400524 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000525 unsigned burstTN = currTime.TN();
526 unsigned burstFN = currTime.FN();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300527 int subch;
dburgessb3a0ca42011-10-12 07:44:40 +0000528
Thomas Tsou204a9f12013-10-29 18:34:16 -0400529 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000530 case NONE:
531 return OFF;
532 break;
533 case FILL:
534 return IDLE;
535 break;
536 case I:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300537 // TODO: Are we expecting RACH on an IDLE frame?
538/* if (burstFN % 26 == 25)
539 return IDLE;*/
540 if (mHandover[burstTN][0])
541 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000542 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000543 break;
544 case II:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300545 subch = tchh_subslot[burstFN % 26];
546 if (subch == 1)
547 return IDLE;
548 if (mHandover[burstTN][0])
549 return RACH;
ttsou20642972013-03-27 22:00:25 +0000550 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000551 break;
552 case III:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300553 subch = tchh_subslot[burstFN % 26];
554 if (mHandover[burstTN][subch])
555 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000556 return TSC;
557 break;
558 case IV:
559 case VI:
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200560 return cfg->ext_rach ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000561 break;
562 case V: {
563 int mod51 = burstFN % 51;
564 if ((mod51 <= 36) && (mod51 >= 14))
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200565 return cfg->ext_rach ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000566 else if ((mod51 == 4) || (mod51 == 5))
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200567 return cfg->ext_rach ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000568 else if ((mod51 == 45) || (mod51 == 46))
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200569 return cfg->ext_rach ? EXT_RACH : RACH;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300570 else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
571 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000572 else
573 return TSC;
574 break;
575 }
576 case VII:
577 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
578 return IDLE;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300579 else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
580 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000581 else
582 return TSC;
583 break;
ttsoufc40a842013-06-09 22:38:18 +0000584 case XIII: {
585 int mod52 = burstFN % 52;
586 if ((mod52 == 12) || (mod52 == 38))
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200587 return cfg->ext_rach ? EXT_RACH : RACH;
ttsoufc40a842013-06-09 22:38:18 +0000588 else if ((mod52 == 25) || (mod52 == 51))
589 return IDLE;
Pau Espin Pedrolbfc1d0b2019-08-26 16:34:45 +0200590 else /* Enable 8-PSK burst detection if EDGE is enabled */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200591 return cfg->egprs ? EDGE : TSC;
ttsoufc40a842013-06-09 22:38:18 +0000592 break;
593 }
dburgessb3a0ca42011-10-12 07:44:40 +0000594 case LOOPBACK:
595 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
596 return IDLE;
597 else
598 return TSC;
599 break;
600 default:
601 return OFF;
602 break;
603 }
dburgessb3a0ca42011-10-12 07:44:40 +0000604}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400605
Alexander Chemerise692ce92015-06-12 00:15:31 -0400606void writeToFile(radioVector *radio_burst, size_t chan)
607{
608 GSM::Time time = radio_burst->getTime();
609 std::ostringstream fname;
610 fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
611 std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
612 outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
613 outfile.close();
614}
615
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200616double Transceiver::rssiOffset(size_t chan)
617{
618 if (cfg->force_rssi_offset)
619 return cfg->rssi_offset;
620 return mRadioInterface->rssiOffset(chan) + cfg->rssi_offset;
621}
622
Thomas Tsou30421a72013-11-13 23:14:48 -0500623/*
624 * Pull bursts from the FIFO and handle according to the slot
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200625 * and burst correlation type. Equalzation is currently disabled.
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200626 * returns 0 on success (bi filled), negative on error (bi content undefined):
627 * -ENOENT: timeslot is off (fn and tn in bi are filled),
628 * -EIO: read error
Thomas Tsou30421a72013-11-13 23:14:48 -0500629 */
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200630int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
dburgessb3a0ca42011-10-12 07:44:40 +0000631{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800632 int rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200633 struct estim_burst_params ebp;
634 float max = -1.0, avg = 0.0;
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200635 unsigned max_toa;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500636 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500637 signalVector *burst;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200638 GSM::Time burstTime;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200639 SoftVector *rxBurst;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500640 TransceiverState *state = &mStates[chan];
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200641 bool ctr_changed = false;
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200642 double rssi_offset;
dburgessb3a0ca42011-10-12 07:44:40 +0000643
Thomas Tsou30421a72013-11-13 23:14:48 -0500644 /* Blocking FIFO read */
645 radioVector *radio_burst = mReceiveFIFO[chan]->read();
Pau Espin Pedrol94c52412019-09-06 13:48:35 +0200646 if (!radio_burst) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100647 LOGCHAN(chan, DTRXDUL, ERROR) << "ReceiveFIFO->read() returned no burst";
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200648 return -EIO;
Pau Espin Pedrol94c52412019-09-06 13:48:35 +0200649 }
dburgessb3a0ca42011-10-12 07:44:40 +0000650
Thomas Tsou30421a72013-11-13 23:14:48 -0500651 /* Set time and determine correlation type */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200652 burstTime = radio_burst->getTime();
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200653 CorrType type = expectedCorrType(burstTime, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000654
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200655 /* Initialize struct bi */
656 bi->nbits = 0;
657 bi->fn = burstTime.FN();
658 bi->tn = burstTime.TN();
659 bi->rssi = 0.0;
660 bi->toa = 0.0;
661 bi->noise = 0.0;
662 bi->idle = false;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200663 bi->modulation = MODULATION_GMSK;
664 bi->tss = 0; /* TODO: we only support tss 0 right now */
665 bi->tsc = 0;
666 bi->ci = 0.0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200667
Pau Espin Pedrol0d56d752019-09-09 10:20:58 +0200668 /* Debug: dump bursts to disk */
669 /* bits 0-7 - chan 0 timeslots
670 * bits 8-15 - chan 1 timeslots */
671 if (mWriteBurstToDiskMask & ((1<<bi->tn) << (8*chan)))
672 writeToFile(radio_burst, chan);
673
674 /* No processing if the timeslot is off.
675 * Not even power level or noise calculation. */
676 if (type == OFF) {
677 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200678 return -ENOENT;
Pau Espin Pedrol0d56d752019-09-09 10:20:58 +0200679 }
680
Pau Espin Pedrol7d8676a2020-08-26 14:30:46 +0200681 /* If TRX RF is locked/muted by BTS, send idle burst indications */
682 if (state->mMuted)
683 goto ret_idle;
684
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500685 /* Select the diversity channel with highest energy */
686 for (size_t i = 0; i < radio_burst->chans(); i++) {
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200687 float pow = energyDetect(*radio_burst->getVector(i), 20 * cfg->rx_sps);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500688 if (pow > max) {
689 max = pow;
690 max_i = i;
691 }
692 avg += pow;
693 }
694
695 if (max_i < 0) {
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200696 LOGCHAN(chan, DTRXDUL, INFO) << "Received empty burst";
697 state->ctrs.rx_empty_burst++;
698 ctr_changed = true;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200699 goto ret_idle;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500700 }
701
Thomas Tsou30421a72013-11-13 23:14:48 -0500702 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500703 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500704 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400705
Alexander Chemeris2b542102015-06-08 22:46:38 -0400706 if (type == IDLE) {
707 /* Update noise levels */
708 state->mNoises.insert(avg);
709 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200710 }
Alexander Chemeris2b542102015-06-08 22:46:38 -0400711
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200712 rssi_offset = rssiOffset(chan);
713 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssi_offset;
714 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssi_offset;
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200715
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200716 if (type == IDLE)
717 goto ret_idle;
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400718
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200719 max_toa = (type == RACH || type == EXT_RACH) ?
720 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200721
Thomas Tsou30421a72013-11-13 23:14:48 -0500722 /* Detect normal or RACH bursts */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200723 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200724 if (rc <= 0) {
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200725 if (rc == -SIGERR_CLIP) {
726 LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst";
727 state->ctrs.rx_clipping++;
728 ctr_changed = true;
729 } else if (rc != SIGERR_NONE) {
730 LOGCHAN(chan, DTRXDUL, INFO) << "Unhandled RACH or Normal Burst detection error";
731 state->ctrs.rx_no_burst_detected++;
732 ctr_changed = true;
733 }
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200734 goto ret_idle;
dburgessb3a0ca42011-10-12 07:44:40 +0000735 }
dburgessb3a0ca42011-10-12 07:44:40 +0000736
Sylvain Munautad202d72021-02-04 20:37:01 +0100737 rxBurst = demodAnyBurst(*burst, (CorrType) rc, cfg->rx_sps, &ebp);
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200738 bi->toa = ebp.toa;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200739 bi->tsc = ebp.tsc;
740 bi->ci = ebp.ci;
Thomas Tsouef25dba2013-11-14 15:31:24 -0500741
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200742 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200743 if (rxBurst->size() == EDGE_BURST_NBITS) {
744 bi->modulation = MODULATION_8PSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200745 bi->nbits = EDGE_BURST_NBITS;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200746 } else { /* size() here is actually gSlotLen + 8, due to guard periods */
747 bi->modulation = MODULATION_GMSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200748 bi->nbits = gSlotLen;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200749 }
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200750
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200751 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200752 vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200753
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200754 delete rxBurst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500755 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200756 return 0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200757
758ret_idle:
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200759 if (ctr_changed)
760 dispatch_trx_rate_ctr_change(state, chan);
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200761 bi->idle = true;
762 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200763 return 0;
dburgessb3a0ca42011-10-12 07:44:40 +0000764}
765
dburgessb3a0ca42011-10-12 07:44:40 +0000766void Transceiver::reset()
767{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400768 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
769 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000770}
771
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200772
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700773/**
774 * Matches a buffer with a command.
775 * @param buf a buffer to look command in
776 * @param cmd a command to look in buffer
777 * @param params pointer to arguments, or NULL
778 * @return true if command matches, otherwise false
779 */
780static bool match_cmd(char *buf,
781 const char *cmd, char **params)
782{
783 size_t cmd_len = strlen(cmd);
784
785 /* Check a command itself */
786 if (strncmp(buf, cmd, cmd_len))
787 return false;
788
789 /* A command has arguments */
790 if (params != NULL) {
791 /* Make sure there is a space */
792 if (buf[cmd_len] != ' ')
793 return false;
794
795 /* Update external pointer */
796 *params = buf + cmd_len + 1;
797 }
798
799 return true;
800}
801
Erica7143bf2020-04-08 01:39:17 +0200802void Transceiver::ctrl_sock_send(ctrl_msg& m, int chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000803{
Erica7143bf2020-04-08 01:39:17 +0200804 ctrl_sock_state& s = mCtrlSockets[chan];
805 struct osmo_fd *conn_bfd = &s.conn_bfd;
806
807 s.txmsgqueue.push_back(m);
Harald Welte94def472020-10-19 12:28:26 +0200808 osmo_fd_write_enable(conn_bfd);
Erica7143bf2020-04-08 01:39:17 +0200809}
810
811int Transceiver::ctrl_sock_write(int chan)
812{
813 int rc;
814 ctrl_sock_state& s = mCtrlSockets[chan];
815
816 if (s.conn_bfd.fd < 0) {
817 return -EIO;
818 }
819
820 while (s.txmsgqueue.size()) {
821 const ctrl_msg m = s.txmsgqueue.front();
822
Harald Welte94def472020-10-19 12:28:26 +0200823 osmo_fd_write_disable(&s.conn_bfd);
Erica7143bf2020-04-08 01:39:17 +0200824
825 /* try to send it over the socket */
826 rc = write(s.conn_bfd.fd, m.data, strlen(m.data) + 1);
827 if (rc == 0)
828 goto close;
829 if (rc < 0) {
830 if (errno == EAGAIN) {
Harald Welte94def472020-10-19 12:28:26 +0200831 osmo_fd_write_enable(&s.conn_bfd);
Erica7143bf2020-04-08 01:39:17 +0200832 break;
833 }
834 goto close;
835 }
836
837 s.txmsgqueue.pop_front();
838 }
839 return 0;
840
841close:
842 LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets write(" << s.conn_bfd.fd << ") failed: " << rc;
843 return -1;
844}
845
846int Transceiver::ctrl_sock_handle_rx(int chan)
847{
848 ctrl_msg cmd_received;
849 ctrl_msg cmd_to_send;
850 char *buffer = cmd_received.data;
851 char *response = cmd_to_send.data;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700852 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700853 int msgLen;
Erica7143bf2020-04-08 01:39:17 +0200854 ctrl_sock_state& s = mCtrlSockets[chan];
Thomas Tsoud647ec52013-10-29 15:17:34 -0400855
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700856 /* Attempt to read from control socket */
Erica7143bf2020-04-08 01:39:17 +0200857 msgLen = read(s.conn_bfd.fd, buffer, sizeof(cmd_received.data)-1);
858 if (msgLen < 0 && errno == EAGAIN)
859 return 0; /* Try again later */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200860 if (msgLen <= 0) {
Erica7143bf2020-04-08 01:39:17 +0200861 LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets read(" << s.conn_bfd.fd << ") failed: " << msgLen;
862 return -EIO;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200863 }
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700864
Erica7143bf2020-04-08 01:39:17 +0200865
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700866 /* Zero-terminate received string */
867 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000868
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700869 /* Verify a command signature */
870 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100871 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
Erica7143bf2020-04-08 01:39:17 +0200872 return -EIO;
dburgessb3a0ca42011-10-12 07:44:40 +0000873 }
dburgessb3a0ca42011-10-12 07:44:40 +0000874
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700875 /* Set command pointer */
876 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200877 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700878
879 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800880 stop();
881 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700882 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700883 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000884 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700885 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000886 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300887 for (int i = 0; i < 8; i++) {
888 for (int j = 0; j < 8; j++)
889 mHandover[i][j] = false;
890 }
Tom Tsou365bc382016-10-19 15:26:04 -0700891 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700892 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700893 unsigned ts = 0, ss = 0;
894 sscanf(params, "%u %u", &ts, &ss);
895 if (ts > 7 || ss > 7) {
Pau Espin Pedrol0fafe032019-11-27 13:17:59 +0100896 sprintf(response, "RSP HANDOVER 1 %u %u", ts, ss);
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700897 } else {
898 mHandover[ts][ss] = true;
899 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
900 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700901 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700902 unsigned ts = 0, ss = 0;
903 sscanf(params, "%u %u", &ts, &ss);
904 if (ts > 7 || ss > 7) {
905 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
906 } else {
907 mHandover[ts][ss] = false;
908 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
909 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700910 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000911 //set expected maximum time-of-arrival
912 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700913 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300914 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000915 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700916 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300917 //set expected maximum time-of-arrival
918 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700919 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300920 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
921 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700922 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000923 //set expected maximum time-of-arrival
924 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700925 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400926 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000927 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700928 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000929 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500930 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000931 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500932 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000933 }
934 else {
Pau Espin Pedrol1b3a8882020-05-29 16:15:18 +0200935 sprintf(response,"RSP NOISELEV 1 0");
dburgessb3a0ca42011-10-12 07:44:40 +0000936 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700937 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800938 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700939 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800940 power = mRadioInterface->setPowerAttenuation(power, chan);
941 mStates[chan].mPower = power;
942 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700943 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800944 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700945 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800946 power = mStates[chan].mPower + step;
947 power = mRadioInterface->setPowerAttenuation(power, chan);
948 mStates[chan].mPower = power;
949 sprintf(response, "RSP ADJPOWER 0 %d", power);
Pau Espin Pedrol0e09e7c2020-05-29 16:39:07 +0200950} else if (match_cmd(command, "NOMTXPOWER", NULL)) {
951 int power = mRadioInterface->getNominalTxPower(chan);
Pau Espin Pedrol405f17a2020-06-19 14:48:35 +0200952 sprintf(response, "RSP NOMTXPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700953 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000954 // tune receiver
955 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700956 sscanf(params, "%d", &freqKhz);
Vadim Yanitskiy3b8f7c42018-08-23 13:41:06 +0700957 mRxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400958 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100959 LOGCHAN(chan, DTRXCTRL, FATAL) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000960 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
961 }
962 else
963 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700964 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000965 // tune txmtr
966 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700967 sscanf(params, "%d", &freqKhz);
Vadim Yanitskiy3b8f7c42018-08-23 13:41:06 +0700968 mTxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400969 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100970 LOGCHAN(chan, DTRXCTRL, FATAL) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000971 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
972 }
973 else
974 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700975 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000976 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500977 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700978 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700979 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500980 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700981 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100982 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000983 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400984 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000985 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700986 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700987 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000988 int corrCode;
989 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700990 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000991 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100992 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000993 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
Erica7143bf2020-04-08 01:39:17 +0200994 return 0;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200995 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400996 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
997 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000998 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200999 } else if (match_cmd(command, "SETFORMAT", &params)) {
1000 // set TRXD protocol version
1001 unsigned version_recv;
1002 sscanf(params, "%u", &version_recv);
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001003 LOGCHAN(chan, DTRXCTRL, INFO) << "BTS requests TRXD version switch: " << version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +02001004 if (version_recv > TRX_DATA_FORMAT_VER) {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001005 LOGCHAN(chan, DTRXCTRL, INFO) << "rejecting TRXD version " << version_recv
1006 << "in favor of " << TRX_DATA_FORMAT_VER;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +02001007 sprintf(response, "RSP SETFORMAT %u %u", TRX_DATA_FORMAT_VER, version_recv);
1008 } else {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001009 LOGCHAN(chan, DTRXCTRL, NOTICE) << "switching to TRXD version " << version_recv;
1010 mVersionTRXD[chan] = version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +02001011 sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
1012 }
Pau Espin Pedrol7d8676a2020-08-26 14:30:46 +02001013 } else if (match_cmd(command, "RFMUTE", &params)) {
1014 // (Un)mute RF TX and RX
1015 unsigned mute;
1016 sscanf(params, "%u", &mute);
1017 mStates[chan].mMuted = mute ? true : false;
1018 sprintf(response, "RSP RFMUTE 0 %u", mute);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001019 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Martin Hauke066fd042019-10-13 19:08:00 +02001020 // debug command! may change or disappear without notice
Alexander Chemerise692ce92015-06-12 00:15:31 -04001021 // set a mask which bursts to dump to disk
1022 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001023 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -04001024 mWriteBurstToDiskMask = mask;
1025 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001026 } else {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001027 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +02001028 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +00001029 }
1030
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +02001031 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Erica7143bf2020-04-08 01:39:17 +02001032 transceiver->ctrl_sock_send(cmd_to_send, chan);
1033 return 0;
dburgessb3a0ca42011-10-12 07:44:40 +00001034}
1035
Thomas Tsou204a9f12013-10-29 18:34:16 -04001036bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001037{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001038 int msgLen;
Tom Tsoue8871082016-07-01 02:46:04 -07001039 int burstLen;
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001040 struct trxd_hdr_v01_dl *dl;
1041 char buffer[sizeof(*dl) + EDGE_BURST_NBITS];
Vadim Yanitskiyb3123252019-07-15 23:53:08 +07001042 uint32_t fn;
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001043 uint8_t tn;
dburgessb3a0ca42011-10-12 07:44:40 +00001044
1045 // check data socket
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001046 msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
1047 if (msgLen <= 0) {
Eric1a26b4f2020-04-13 00:03:01 +02001048 LOGCHAN(chan, DTRXDDL, NOTICE) << "mDataSockets read(" << mDataSockets[chan] << ") failed: " << msgLen;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001049 return false;
1050 }
dburgessb3a0ca42011-10-12 07:44:40 +00001051
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001052 switch (msgLen) {
1053 case sizeof(*dl) + gSlotLen: /* GSM burst */
1054 burstLen = gSlotLen;
1055 break;
1056 case sizeof(*dl) + EDGE_BURST_NBITS: /* EDGE burst */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +02001057 if (cfg->tx_sps != 4) {
1058 LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << cfg->tx_sps;
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001059 return false;
1060 }
1061 burstLen = EDGE_BURST_NBITS;
1062 break;
1063 default:
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001064 LOGCHAN(chan, DTRXDDL, ERROR) << "badly formatted packet on GSM->TRX interface (len="<< msgLen << ")";
Tom Tsoue8871082016-07-01 02:46:04 -07001065 return false;
dburgessb3a0ca42011-10-12 07:44:40 +00001066 }
1067
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001068 dl = (struct trxd_hdr_v01_dl *) buffer;
dburgessb3a0ca42011-10-12 07:44:40 +00001069
Vadim Yanitskiyb3123252019-07-15 23:53:08 +07001070 /* Convert TDMA FN to the host endianness */
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001071 fn = osmo_load32be(&dl->common.fn);
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001072 tn = dl->common.tn;
Vadim Yanitskiyb3123252019-07-15 23:53:08 +07001073
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001074 /* Make sure we support the received header format */
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001075 switch (dl->common.version) {
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001076 case 0:
1077 /* Version 1 has the same format */
1078 case 1:
1079 break;
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001080 default:
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001081 LOGCHAN(chan, DTRXDDL, ERROR) << "Rx TRXD message with unknown header version " << unsigned(dl->common.version);
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001082 return false;
1083 }
1084
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001085 LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version)
Pau Espin Pedrol99330742020-07-15 14:05:31 +02001086 << "): fn=" << fn << ", tn=" << unsigned(tn) << ", burst_len=" << burstLen;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001087
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001088 TransceiverState *state = &mStates[chan];
1089 GSM::Time currTime = GSM::Time(fn, tn);
1090
1091 /* Verify proper FN order in DL stream */
1092 if (state->first_dl_fn_rcv[tn]) {
1093 int32_t delta = GSM::FNDelta(currTime.FN(), state->last_dl_time_rcv[tn].FN());
1094 if (delta == 1) {
1095 /* usual expected scenario, continue code flow */
1096 } else if (delta == 0) {
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +02001097 LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with repeated FN " << currTime;
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001098 state->ctrs.tx_trxd_fn_repeated++;
1099 dispatch_trx_rate_ctr_change(state, chan);
1100 return true;
1101 } else if (delta < 0) {
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +02001102 LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with previous FN " << currTime
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001103 << " vs last " << state->last_dl_time_rcv[tn];
1104 state->ctrs.tx_trxd_fn_outoforder++;
1105 dispatch_trx_rate_ctr_change(state, chan);
1106 /* Allow adding radio vector below, since it gets sorted in the queue */
1107 } else if (chan == 0 && state->mFiller == FILLER_ZERO) {
1108 /* delta > 1. Some FN was lost in the middle. We can only easily rely
1109 * on consecutive FNs in TRX0 since it must transmit continuously in all
1110 * setups. Also, osmo-trx supports optionally filling empty bursts on
1111 * its own. In that case bts-trx is not obliged to submit all bursts. */
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +02001112 LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with future FN " << currTime
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001113 << " vs last " << state->last_dl_time_rcv[tn]
1114 << ", " << delta - 1 << " FN lost";
1115 state->ctrs.tx_trxd_fn_skipped += delta - 1;
1116 dispatch_trx_rate_ctr_change(state, chan);
1117 }
1118 if (delta > 0)
1119 state->last_dl_time_rcv[tn] = currTime;
1120 } else { /* Initial check, simply store state */
1121 state->first_dl_fn_rcv[tn] = true;
1122 state->last_dl_time_rcv[tn] = currTime;
1123 }
1124
Tom Tsou7c741ec2016-07-19 11:20:59 -07001125 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +00001126 BitVector::iterator itr = newBurst.begin();
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001127 uint8_t *bufferItr = dl->soft_bits;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001128 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +00001129 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001130
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001131 addRadioVector(chan, newBurst, dl->tx_att, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +00001132
1133 return true;
dburgessb3a0ca42011-10-12 07:44:40 +00001134}
dburgessb3a0ca42011-10-12 07:44:40 +00001135
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001136bool Transceiver::driveReceiveRadio()
Thomas Tsou204a9f12013-10-29 18:34:16 -04001137{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +02001138 int rc = mRadioInterface->driveReceiveRadio();
1139 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001140 usleep(100000);
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001141 return true;
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -04001142 }
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001143 if (rc < 0)
1144 return false;
1145
1146 if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
Philipp Maierfdefbfa2020-05-22 12:17:13 +02001147 if (mForceClockInterface)
1148 LOGC(DTRXCLK, NOTICE) << "Sending CLOCK indications";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001149 mForceClockInterface = false;
1150 return writeClockInterface();
1151 }
1152 return true;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001153}
1154
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001155void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001156{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001157 std::ostringstream os;
1158 for (size_t i=0; i < bi->nbits; i++) {
1159 if (bi->rx_burst[i] > 0.5) os << "1";
1160 else if (bi->rx_burst[i] > 0.25) os << "|";
1161 else if (bi->rx_burst[i] > 0.0) os << "'";
1162 else os << "-";
1163 }
1164
Pau Espin Pedrole91544d2020-10-13 17:03:37 +02001165 double rssi_offset = rssiOffset(chan);
1166
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001167 LOGCHAN(chan, DTRXDUL, DEBUG) << std::fixed << std::right
Pau Espin Pedrol1fba1042019-09-06 14:22:31 +02001168 << " time: " << unsigned(bi->tn) << ":" << bi->fn
Pau Espin Pedrole91544d2020-10-13 17:03:37 +02001169 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssi_offset)
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001170 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
Pau Espin Pedrole91544d2020-10-13 17:03:37 +02001171 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssi_offset)
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001172 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001173 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001174 << " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB"
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001175 << " bits: " << os;
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001176}
1177
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001178bool Transceiver::driveReceiveFIFO(size_t chan)
Thomas Tsou204a9f12013-10-29 18:34:16 -04001179{
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001180 struct trx_ul_burst_ind bi;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001181 int rc;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001182
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001183 if ((rc = pullRadioVector(chan, &bi)) < 0) {
1184 if (rc == -ENOENT) { /* timeslot off, continue processing */
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001185 LOGCHAN(chan, DTRXDUL, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " timeslot is off";
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001186 return true;
1187 }
1188 return false; /* other errors: we want to stop the process */
1189 }
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001190
Pau Espin Pedrol199a3062020-07-27 10:58:51 +02001191 if (!bi.idle && log_check_level(DTRXDUL, LOGL_DEBUG))
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001192 logRxBurst(chan, &bi);
dburgessb3a0ca42011-10-12 07:44:40 +00001193
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001194 switch (mVersionTRXD[chan]) {
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001195 case 0:
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001196 return trxd_send_burst_ind_v0(chan, mDataSockets[chan], &bi);
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001197 case 1:
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001198 return trxd_send_burst_ind_v1(chan, mDataSockets[chan], &bi);
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001199 default:
1200 OSMO_ASSERT(false);
1201 }
dburgessb3a0ca42011-10-12 07:44:40 +00001202}
1203
Thomas Tsou204a9f12013-10-29 18:34:16 -04001204void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +00001205{
1206
1207 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001208 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +00001209 assure that transmit packets arrive at the radio/USRP
1210 before they need to be transmitted.
1211
1212 Deadline clock indicates the burst that needs to be
1213 pushed into the FIFO right NOW. If transmit queue does
1214 not have a burst, stick in filler data.
1215 */
1216
1217
1218 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001219
dburgessb3a0ca42011-10-12 07:44:40 +00001220 if (mOn) {
1221 //radioClock->wait(); // wait until clock updates
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001222 LOGC(DTRXCLK, DEBUG) << "radio clock " << radioClock->get();
dburgessb3a0ca42011-10-12 07:44:40 +00001223 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1224 // if underrun, then we're not providing bursts to radio/USRP fast
1225 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001226 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001227 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001228 // only update latency at the defined frame interval
1229 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001230 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001231 LOGC(DTRXCLK, INFO) << "new latency: " << mTransmitLatency << " (underrun "
1232 << radioClock->get() << " vs "
1233 << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001234 mLatencyUpdateTime = radioClock->get();
1235 }
1236 }
1237 else {
1238 // if underrun hasn't occurred in the last sec (216 frames) drop
1239 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001240 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001241 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1242 mTransmitLatency.decTN();
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001243 LOGC(DTRXCLK, INFO) << "reduced latency: " << mTransmitLatency;
kurtis.heimerle380af32011-11-26 03:18:55 +00001244 mLatencyUpdateTime = radioClock->get();
1245 }
1246 }
1247 }
dburgessb3a0ca42011-10-12 07:44:40 +00001248 }
dburgessb3a0ca42011-10-12 07:44:40 +00001249 // time to push burst to transmit FIFO
1250 pushRadioVector(mTransmitDeadlineClock);
1251 mTransmitDeadlineClock.incTN();
1252 }
dburgessb3a0ca42011-10-12 07:44:40 +00001253 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001254
1255 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001256}
1257
1258
1259
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001260bool Transceiver::writeClockInterface()
dburgessb3a0ca42011-10-12 07:44:40 +00001261{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001262 int msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001263 char command[50];
1264 // FIXME -- This should be adaptive.
1265 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1266
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001267 LOGC(DTRXCLK, INFO) << "sending " << command;
dburgessb3a0ca42011-10-12 07:44:40 +00001268
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001269 msgLen = write(mClockSocket, command, strlen(command) + 1);
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001270 if (msgLen <= 0) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001271 LOGC(DTRXCLK, ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001272 return false;
1273 }
dburgessb3a0ca42011-10-12 07:44:40 +00001274
1275 mLastClockUpdateTime = mTransmitDeadlineClock;
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001276 return true;
Thomas Tsou92c16df2013-09-28 18:04:19 -04001277}
dburgessb3a0ca42011-10-12 07:44:40 +00001278
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001279void *RxUpperLoopAdapter(TrxChanThParams *params)
Thomas Tsou204a9f12013-10-29 18:34:16 -04001280{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001281 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001282 Transceiver *trx = params->trx;
1283 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001284
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001285 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001286
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001287 snprintf(thread_name, 16, "RxUpper%zu", num);
1288 set_selfthread_name(thread_name);
Pau Espin Pedrol553a2502020-07-29 18:05:25 +02001289 OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001290
Thomas Tsou204a9f12013-10-29 18:34:16 -04001291 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001292 if (!trx->driveReceiveFIFO(num)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001293 LOGCHAN(num, DTRXDUL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001294 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1295 break;
1296 }
Thomas Tsou204a9f12013-10-29 18:34:16 -04001297 pthread_testcancel();
1298 }
1299 return NULL;
1300}
1301
1302void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001303{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001304 set_selfthread_name("RxLower");
Pau Espin Pedrol553a2502020-07-29 18:05:25 +02001305 OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001306
dburgessb3a0ca42011-10-12 07:44:40 +00001307 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001308 if (!transceiver->driveReceiveRadio()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001309 LOGC(DTRXDUL, FATAL) << "Something went wrong in thread RxLower, requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001310 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1311 break;
1312 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001313 pthread_testcancel();
1314 }
1315 return NULL;
1316}
1317
Thomas Tsou204a9f12013-10-29 18:34:16 -04001318void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001319{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001320 set_selfthread_name("TxLower");
Pau Espin Pedrol553a2502020-07-29 18:05:25 +02001321 OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001322
Thomas Tsou92c16df2013-09-28 18:04:19 -04001323 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001324 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001325 pthread_testcancel();
1326 }
1327 return NULL;
1328}
1329
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001330void *TxUpperLoopAdapter(TrxChanThParams *params)
dburgessb3a0ca42011-10-12 07:44:40 +00001331{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001332 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001333 Transceiver *trx = params->trx;
1334 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001335
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001336 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001337
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001338 snprintf(thread_name, 16, "TxUpper%zu", num);
1339 set_selfthread_name(thread_name);
Pau Espin Pedrol553a2502020-07-29 18:05:25 +02001340 OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001341
dburgessb3a0ca42011-10-12 07:44:40 +00001342 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001343 if (!trx->driveTxPriorityQueue(num)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001344 LOGCHAN(num, DTRXDDL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001345 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1346 break;
1347 }
dburgessb3a0ca42011-10-12 07:44:40 +00001348 pthread_testcancel();
1349 }
1350 return NULL;
1351}