blob: c9894f07d87ffd54cb04338c0addde52a7e030e0 [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)
Eric5561f112022-07-19 21:18:21 +0200138 : mChans(cfg->num_chans), cfg(cfg),
139 mCtrlSockets(mChans), mClockSocket(-1),
140 mTxPriorityQueues(mChans), mReceiveFIFO(mChans),
141 mRxServiceLoopThreads(mChans), mRxLowerLoopThread(nullptr), mTxLowerLoopThread(nullptr),
142 mTxPriorityQueueServiceLoopThreads(mChans), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
143 mOn(false),mForceClockInterface(false), mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0),
144 mMaxExpectedDelayNB(0), mWriteBurstToDiskMask(0), mVersionTRXD(mChans), mStates(mChans)
dburgessb3a0ca42011-10-12 07:44:40 +0000145{
dburgessb3a0ca42011-10-12 07:44:40 +0000146 txFullScale = mRadioInterface->fullScaleInputValue();
147 rxFullScale = mRadioInterface->fullScaleOutputValue();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300148
Vadim Yanitskiy0ff9c9f2020-10-24 16:48:33 +0700149 for (size_t i = 0; i < ARRAY_SIZE(mHandover); i++) {
150 for (size_t j = 0; j < ARRAY_SIZE(mHandover[i]); j++)
Alexander Chemeris5a068062015-06-20 01:38:47 +0300151 mHandover[i][j] = false;
152 }
dburgessb3a0ca42011-10-12 07:44:40 +0000153}
154
155Transceiver::~Transceiver()
156{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800157 stop();
158
dburgessb3a0ca42011-10-12 07:44:40 +0000159 sigProcLibDestroy();
Thomas Tsoud647ec52013-10-29 15:17:34 -0400160
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200161 if (mClockSocket >= 0)
162 close(mClockSocket);
163
Thomas Tsou204a9f12013-10-29 18:34:16 -0400164 for (size_t i = 0; i < mChans; i++) {
165 mTxPriorityQueues[i].clear();
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200166 if (mDataSockets[i] >= 0)
167 close(mDataSockets[i]);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400168 }
dburgessb3a0ca42011-10-12 07:44:40 +0000169}
Thomas Tsou83e06892013-08-20 16:10:01 -0400170
Erica7143bf2020-04-08 01:39:17 +0200171int Transceiver::ctrl_sock_cb(struct osmo_fd *bfd, unsigned int flags)
172{
173 int rc = 0;
174 int chan = static_cast<int>(reinterpret_cast<uintptr_t>(bfd->data));
175
176 if (flags & OSMO_FD_READ)
177 rc = transceiver->ctrl_sock_handle_rx(chan);
178 if (rc < 0)
179 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
180
181 if (flags & OSMO_FD_WRITE)
182 rc = transceiver->ctrl_sock_write(chan);
183 if (rc < 0)
184 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
185
186 return rc;
187}
188
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800189/*
190 * Initialize transceiver
191 *
192 * Start or restart the control loop. Any further control is handled through the
193 * socket API. Randomize the central radio clock set the downlink burst
194 * counters. Note that the clock will not update until the radio starts, but we
195 * are still expected to report clock indications through control channel
196 * activity.
197 */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200198bool Transceiver::init()
Thomas Tsou83e06892013-08-20 16:10:01 -0400199{
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500200 int d_srcport, d_dstport, c_srcport, c_dstport;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400201 if (!mChans) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100202 LOG(FATAL) << "No channels assigned";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400203 return false;
204 }
205
Tom Tsou2079a3c2016-03-06 00:58:56 -0800206 if (!sigProcLibSetup()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100207 LOG(FATAL) << "Failed to initialize signal processing library";
Thomas Tsou83e06892013-08-20 16:10:01 -0400208 return false;
209 }
210
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200211 mDataSockets.resize(mChans, -1);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400212
Thomas Tsou204a9f12013-10-29 18:34:16 -0400213
Thomas Tsouccb73e12014-04-15 17:41:28 -0400214 /* Filler table retransmissions - support only on channel 0 */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200215 if (cfg->filler == FILLER_DUMMY)
Thomas Tsouccb73e12014-04-15 17:41:28 -0400216 mStates[0].mRetrans = true;
217
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800218 /* Setup sockets */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200219 mClockSocket = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200220 cfg->bind_addr, cfg->base_port,
221 cfg->remote_addr, cfg->base_port + 100,
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200222 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
Pau Espin Pedrole7195ac2019-08-26 10:55:04 +0200223 if (mClockSocket < 0)
224 return false;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200225
Thomas Tsou204a9f12013-10-29 18:34:16 -0400226 for (size_t i = 0; i < mChans; i++) {
Erica7143bf2020-04-08 01:39:17 +0200227 int rv;
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200228 FillerType filler = cfg->filler;
229 c_srcport = cfg->base_port + 2 * i + 1;
230 c_dstport = cfg->base_port + 2 * i + 101;
231 d_srcport = cfg->base_port + 2 * i + 2;
232 d_dstport = cfg->base_port + 2 * i + 102;
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500233
Erica7143bf2020-04-08 01:39:17 +0200234 rv = osmo_sock_init2_ofd(&mCtrlSockets[i].conn_bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200235 cfg->bind_addr, c_srcport,
236 cfg->remote_addr, c_dstport,
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200237 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
Erica7143bf2020-04-08 01:39:17 +0200238 if (rv < 0)
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200239 return false;
240
Erica7143bf2020-04-08 01:39:17 +0200241 mCtrlSockets[i].conn_bfd.cb = ctrl_sock_cb;
242 mCtrlSockets[i].conn_bfd.data = reinterpret_cast<void*>(i);
243
244
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200245 mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200246 cfg->bind_addr, d_srcport,
247 cfg->remote_addr, d_dstport,
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200248 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
Eric1a26b4f2020-04-13 00:03:01 +0200249 if (mDataSockets[i] < 0)
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200250 return false;
Erica7143bf2020-04-08 01:39:17 +0200251
252 if (i && filler == FILLER_DUMMY)
253 filler = FILLER_ZERO;
254
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200255 mStates[i].init(filler, cfg->tx_sps, txFullScale, cfg->rtsc, cfg->rach_delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400256 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400257
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800258 /* Randomize the central clock */
259 GSM::Time startTime(random() % gHyperframe, 0);
260 mRadioInterface->getClock()->set(startTime);
261 mTransmitDeadlineClock = startTime;
262 mLastClockUpdateTime = startTime;
263 mLatencyUpdateTime = startTime;
264
Thomas Tsou83e06892013-08-20 16:10:01 -0400265 return true;
266}
dburgessb3a0ca42011-10-12 07:44:40 +0000267
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800268/*
269 * Start the transceiver
270 *
271 * Submit command(s) to the radio device to commence streaming samples and
272 * launch threads to handle sample I/O. Re-synchronize the transmit burst
273 * counters to the central radio clock here as well.
274 */
275bool Transceiver::start()
276{
277 ScopedLock lock(mLock);
278
279 if (mOn) {
280 LOG(ERR) << "Transceiver already running";
Ivan Kluchnikov194a9b12015-04-23 17:08:27 +0300281 return true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800282 }
283
284 LOG(NOTICE) << "Starting the transceiver";
285
286 GSM::Time time = mRadioInterface->getClock()->get();
287 mTransmitDeadlineClock = time;
288 mLastClockUpdateTime = time;
289 mLatencyUpdateTime = time;
290
291 if (!mRadioInterface->start()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100292 LOG(FATAL) << "Device failed to start";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800293 return false;
294 }
295
296 /* Device is running - launch I/O threads */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200297 mRxLowerLoopThread = new Thread(cfg->stack_size);
298 mTxLowerLoopThread = new Thread(cfg->stack_size);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800299 mTxLowerLoopThread->start((void * (*)(void*))
300 TxLowerLoopAdapter,(void*) this);
301 mRxLowerLoopThread->start((void * (*)(void*))
302 RxLowerLoopAdapter,(void*) this);
303
304 /* Launch uplink and downlink burst processing threads */
305 for (size_t i = 0; i < mChans; i++) {
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200306 TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
307 params->trx = this;
308 params->num = i;
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200309 mRxServiceLoopThreads[i] = new Thread(cfg->stack_size);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800310 mRxServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200311 RxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800312
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200313 params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
314 params->trx = this;
315 params->num = i;
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200316 mTxPriorityQueueServiceLoopThreads[i] = new Thread(cfg->stack_size);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800317 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200318 TxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800319 }
320
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200321 mForceClockInterface = true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800322 mOn = true;
323 return true;
324}
325
326/*
327 * Stop the transceiver
328 *
329 * Perform stopping by disabling receive streaming and issuing cancellation
330 * requests to running threads. Most threads will timeout and terminate once
331 * device is disabled, but the transmit loop may block waiting on the central
332 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
333 * makes it to the thread cancellation point.
334 */
335void Transceiver::stop()
336{
337 ScopedLock lock(mLock);
338
339 if (!mOn)
340 return;
341
342 LOG(NOTICE) << "Stopping the transceiver";
343 mTxLowerLoopThread->cancel();
344 mRxLowerLoopThread->cancel();
Tom Tsoud67bd602017-06-15 15:35:02 -0700345 mTxLowerLoopThread->join();
346 mRxLowerLoopThread->join();
347 delete mTxLowerLoopThread;
348 delete mRxLowerLoopThread;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800349
350 for (size_t i = 0; i < mChans; i++) {
351 mRxServiceLoopThreads[i]->cancel();
352 mTxPriorityQueueServiceLoopThreads[i]->cancel();
353 }
354
355 LOG(INFO) << "Stopping the device";
356 mRadioInterface->stop();
357
358 for (size_t i = 0; i < mChans; i++) {
359 mRxServiceLoopThreads[i]->join();
360 mTxPriorityQueueServiceLoopThreads[i]->join();
361 delete mRxServiceLoopThreads[i];
362 delete mTxPriorityQueueServiceLoopThreads[i];
363
364 mTxPriorityQueues[i].clear();
365 }
366
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800367 mOn = false;
368 LOG(NOTICE) << "Transceiver stopped";
369}
370
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500371void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400372 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000373{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500374 signalVector *burst;
375 radioVector *radio_burst;
376
Thomas Tsou204a9f12013-10-29 18:34:16 -0400377 if (chan >= mTxPriorityQueues.size()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100378 LOGCHAN(chan, DTRXDDL, FATAL) << "Invalid channel";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400379 return;
380 }
381
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500382 if (wTime.TN() > 7) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100383 LOGCHAN(chan, DTRXDDL, FATAL) << "Received burst with invalid slot " << wTime.TN();
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500384 return;
385 }
386
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800387 /* Use the number of bits as the EDGE burst indicator */
388 if (bits.size() == EDGE_BURST_NBITS)
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200389 burst = modulateEdgeBurst(bits, cfg->tx_sps);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800390 else
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200391 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), cfg->tx_sps);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800392
Vadim Yanitskiy2a637a52021-01-26 23:55:22 +0100393 scaleVector(*burst, txFullScale * pow(10, (double) -RSSI / 20));
dburgessb3a0ca42011-10-12 07:44:40 +0000394
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500395 radio_burst = new radioVector(wTime, burst);
396
397 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000398}
399
Thomas Tsou15d743e2014-01-25 02:34:03 -0500400void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
401{
402 int TN, modFN;
403 TransceiverState *state = &mStates[chan];
404
405 TN = burst->getTime().TN();
406 modFN = burst->getTime().FN() % state->fillerModulus[TN];
407
408 delete state->fillerTable[modFN][TN];
409 state->fillerTable[modFN][TN] = burst->getVector();
410 burst->setVector(NULL);
411}
412
dburgessb3a0ca42011-10-12 07:44:40 +0000413void Transceiver::pushRadioVector(GSM::Time &nowTime)
414{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400415 int TN, modFN;
416 radioVector *burst;
417 TransceiverState *state;
418 std::vector<signalVector *> bursts(mChans);
419 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500420 std::vector<bool> filler(mChans, true);
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200421 bool ratectr_changed;
dburgessb3a0ca42011-10-12 07:44:40 +0000422
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200423 TN = nowTime.TN();
424
Thomas Tsou204a9f12013-10-29 18:34:16 -0400425 for (size_t i = 0; i < mChans; i ++) {
426 state = &mStates[i];
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200427 ratectr_changed = false;
428
Pau Espin Pedrol7d8676a2020-08-26 14:30:46 +0200429 zeros[i] = state->chanType[TN] == NONE || state->mMuted;
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200430
431 Mutex *mtx = mTxPriorityQueues[i].getMutex();
432 mtx->lock();
dburgessb3a0ca42011-10-12 07:44:40 +0000433
Thomas Tsou204a9f12013-10-29 18:34:16 -0400434 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +0200435 LOGCHAN(i, DTRXDDL, INFO) << "dumping STALE burst in TRX->SDR interface ("
Pau Espin Pedrolf37b0ad2018-04-25 18:01:27 +0200436 << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
Pau Espin Pedrol92ba59d2020-06-29 14:34:59 +0200437 state->ctrs.tx_stale_bursts++;
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200438 ratectr_changed = true;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500439 if (state->mRetrans)
440 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500441 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400442 }
443
Thomas Tsou204a9f12013-10-29 18:34:16 -0400444 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500445 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500446
447 if (state->mRetrans) {
448 updateFillerTable(i, burst);
449 } else {
450 burst->setVector(NULL);
451 filler[i] = false;
452 }
453
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500454 delete burst;
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200455 } else {
456 modFN = nowTime.FN() % state->fillerModulus[TN];
457 bursts[i] = state->fillerTable[modFN][TN];
Pau Espin Pedroldf675782020-07-17 14:09:28 +0200458 if (i == 0 && state->mFiller == FILLER_ZERO) {
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +0200459 LOGCHAN(i, DTRXDDL, INFO) << "No Tx burst available for " << nowTime
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200460 << ", retrans=" << state->mRetrans;
461 state->ctrs.tx_unavailable_bursts++;
462 ratectr_changed = true;
463 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400464 }
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200465
466 mtx->unlock();
467
Pau Espin Pedrol1d0c6fe2020-07-09 18:09:10 +0200468 if (ratectr_changed)
Pau Espin Pedrol8b0c5362020-07-09 17:39:20 +0200469 dispatch_trx_rate_ctr_change(state, i);
dburgessb3a0ca42011-10-12 07:44:40 +0000470 }
471
Thomas Tsou204a9f12013-10-29 18:34:16 -0400472 mRadioInterface->driveTransmitRadio(bursts, zeros);
473
Thomas Tsou15d743e2014-01-25 02:34:03 -0500474 for (size_t i = 0; i < mChans; i++) {
475 if (!filler[i])
476 delete bursts[i];
477 }
dburgessb3a0ca42011-10-12 07:44:40 +0000478}
479
Thomas Tsou204a9f12013-10-29 18:34:16 -0400480void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000481{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400482 TransceiverState *state = &mStates[chan];
483
484 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000485 case NONE:
486 case I:
487 case II:
488 case III:
489 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400490 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000491 break;
492 case IV:
493 case VI:
494 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400495 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000496 break;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200497 //case V:
dburgessb3a0ca42011-10-12 07:44:40 +0000498 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400499 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000500 break;
ttsoufc40a842013-06-09 22:38:18 +0000501 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400502 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000503 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000504 default:
505 break;
506 }
507}
508
509
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700510CorrType Transceiver::expectedCorrType(GSM::Time currTime,
511 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000512{
Alexander Chemeris5a068062015-06-20 01:38:47 +0300513 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 };
514 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,
515 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 };
516 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,
517 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 -0400518 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000519 unsigned burstTN = currTime.TN();
520 unsigned burstFN = currTime.FN();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300521 int subch;
dburgessb3a0ca42011-10-12 07:44:40 +0000522
Thomas Tsou204a9f12013-10-29 18:34:16 -0400523 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000524 case NONE:
525 return OFF;
526 break;
527 case FILL:
528 return IDLE;
529 break;
530 case I:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300531 // TODO: Are we expecting RACH on an IDLE frame?
532/* if (burstFN % 26 == 25)
533 return IDLE;*/
534 if (mHandover[burstTN][0])
535 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000536 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000537 break;
538 case II:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300539 subch = tchh_subslot[burstFN % 26];
540 if (subch == 1)
541 return IDLE;
542 if (mHandover[burstTN][0])
543 return RACH;
ttsou20642972013-03-27 22:00:25 +0000544 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000545 break;
546 case III:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300547 subch = tchh_subslot[burstFN % 26];
548 if (mHandover[burstTN][subch])
549 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000550 return TSC;
551 break;
552 case IV:
553 case VI:
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200554 return cfg->ext_rach ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000555 break;
556 case V: {
557 int mod51 = burstFN % 51;
558 if ((mod51 <= 36) && (mod51 >= 14))
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200559 return cfg->ext_rach ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000560 else if ((mod51 == 4) || (mod51 == 5))
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200561 return cfg->ext_rach ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000562 else if ((mod51 == 45) || (mod51 == 46))
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200563 return cfg->ext_rach ? EXT_RACH : RACH;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300564 else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
565 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000566 else
567 return TSC;
568 break;
569 }
570 case VII:
571 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
572 return IDLE;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300573 else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
574 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000575 else
576 return TSC;
577 break;
ttsoufc40a842013-06-09 22:38:18 +0000578 case XIII: {
579 int mod52 = burstFN % 52;
580 if ((mod52 == 12) || (mod52 == 38))
Vadim Yanitskiy00ddcfa2022-10-26 04:11:28 +0700581 return RACH; /* RACH is always 8-bit on PTCCH/U */
ttsoufc40a842013-06-09 22:38:18 +0000582 else if ((mod52 == 25) || (mod52 == 51))
583 return IDLE;
Pau Espin Pedrolbfc1d0b2019-08-26 16:34:45 +0200584 else /* Enable 8-PSK burst detection if EDGE is enabled */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200585 return cfg->egprs ? EDGE : TSC;
ttsoufc40a842013-06-09 22:38:18 +0000586 break;
587 }
dburgessb3a0ca42011-10-12 07:44:40 +0000588 case LOOPBACK:
589 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
590 return IDLE;
591 else
592 return TSC;
593 break;
594 default:
595 return OFF;
596 break;
597 }
dburgessb3a0ca42011-10-12 07:44:40 +0000598}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400599
Alexander Chemerise692ce92015-06-12 00:15:31 -0400600void writeToFile(radioVector *radio_burst, size_t chan)
601{
602 GSM::Time time = radio_burst->getTime();
603 std::ostringstream fname;
604 fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
605 std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
606 outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
607 outfile.close();
608}
609
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200610double Transceiver::rssiOffset(size_t chan)
611{
612 if (cfg->force_rssi_offset)
613 return cfg->rssi_offset;
614 return mRadioInterface->rssiOffset(chan) + cfg->rssi_offset;
615}
616
Thomas Tsou30421a72013-11-13 23:14:48 -0500617/*
618 * Pull bursts from the FIFO and handle according to the slot
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200619 * and burst correlation type. Equalzation is currently disabled.
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200620 * returns 0 on success (bi filled), negative on error (bi content undefined):
621 * -ENOENT: timeslot is off (fn and tn in bi are filled),
622 * -EIO: read error
Thomas Tsou30421a72013-11-13 23:14:48 -0500623 */
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200624int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
dburgessb3a0ca42011-10-12 07:44:40 +0000625{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800626 int rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200627 struct estim_burst_params ebp;
628 float max = -1.0, avg = 0.0;
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200629 unsigned max_toa;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500630 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500631 signalVector *burst;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200632 GSM::Time burstTime;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200633 SoftVector *rxBurst;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500634 TransceiverState *state = &mStates[chan];
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200635 bool ctr_changed = false;
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200636 double rssi_offset;
dburgessb3a0ca42011-10-12 07:44:40 +0000637
Thomas Tsou30421a72013-11-13 23:14:48 -0500638 /* Blocking FIFO read */
639 radioVector *radio_burst = mReceiveFIFO[chan]->read();
Pau Espin Pedrol94c52412019-09-06 13:48:35 +0200640 if (!radio_burst) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100641 LOGCHAN(chan, DTRXDUL, ERROR) << "ReceiveFIFO->read() returned no burst";
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200642 return -EIO;
Pau Espin Pedrol94c52412019-09-06 13:48:35 +0200643 }
dburgessb3a0ca42011-10-12 07:44:40 +0000644
Thomas Tsou30421a72013-11-13 23:14:48 -0500645 /* Set time and determine correlation type */
Ericc27fe602021-05-05 17:33:18 +0200646 burstTime = radio_burst->getTime() + cfg->ul_fn_offset;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200647 CorrType type = expectedCorrType(burstTime, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000648
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200649 /* Initialize struct bi */
650 bi->nbits = 0;
651 bi->fn = burstTime.FN();
652 bi->tn = burstTime.TN();
653 bi->rssi = 0.0;
654 bi->toa = 0.0;
655 bi->noise = 0.0;
656 bi->idle = false;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200657 bi->modulation = MODULATION_GMSK;
658 bi->tss = 0; /* TODO: we only support tss 0 right now */
659 bi->tsc = 0;
660 bi->ci = 0.0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200661
Pau Espin Pedrol0d56d752019-09-09 10:20:58 +0200662 /* Debug: dump bursts to disk */
663 /* bits 0-7 - chan 0 timeslots
664 * bits 8-15 - chan 1 timeslots */
665 if (mWriteBurstToDiskMask & ((1<<bi->tn) << (8*chan)))
666 writeToFile(radio_burst, chan);
667
668 /* No processing if the timeslot is off.
669 * Not even power level or noise calculation. */
670 if (type == OFF) {
671 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200672 return -ENOENT;
Pau Espin Pedrol0d56d752019-09-09 10:20:58 +0200673 }
674
Pau Espin Pedrol7d8676a2020-08-26 14:30:46 +0200675 /* If TRX RF is locked/muted by BTS, send idle burst indications */
676 if (state->mMuted)
677 goto ret_idle;
678
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500679 /* Select the diversity channel with highest energy */
680 for (size_t i = 0; i < radio_burst->chans(); i++) {
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200681 float pow = energyDetect(*radio_burst->getVector(i), 20 * cfg->rx_sps);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500682 if (pow > max) {
683 max = pow;
684 max_i = i;
685 }
686 avg += pow;
687 }
688
689 if (max_i < 0) {
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200690 LOGCHAN(chan, DTRXDUL, INFO) << "Received empty burst";
691 state->ctrs.rx_empty_burst++;
692 ctr_changed = true;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200693 goto ret_idle;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500694 }
695
Thomas Tsou30421a72013-11-13 23:14:48 -0500696 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500697 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500698 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400699
Alexander Chemeris2b542102015-06-08 22:46:38 -0400700 if (type == IDLE) {
701 /* Update noise levels */
702 state->mNoises.insert(avg);
703 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200704 }
Alexander Chemeris2b542102015-06-08 22:46:38 -0400705
Pau Espin Pedrole91544d2020-10-13 17:03:37 +0200706 rssi_offset = rssiOffset(chan);
707 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssi_offset;
708 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssi_offset;
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200709
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200710 if (type == IDLE)
711 goto ret_idle;
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400712
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200713 max_toa = (type == RACH || type == EXT_RACH) ?
714 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200715
Thomas Tsou30421a72013-11-13 23:14:48 -0500716 /* Detect normal or RACH bursts */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +0200717 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200718 if (rc <= 0) {
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200719 if (rc == -SIGERR_CLIP) {
720 LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst";
721 state->ctrs.rx_clipping++;
722 ctr_changed = true;
723 } else if (rc != SIGERR_NONE) {
724 LOGCHAN(chan, DTRXDUL, INFO) << "Unhandled RACH or Normal Burst detection error";
725 state->ctrs.rx_no_burst_detected++;
726 ctr_changed = true;
727 }
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200728 goto ret_idle;
dburgessb3a0ca42011-10-12 07:44:40 +0000729 }
dburgessb3a0ca42011-10-12 07:44:40 +0000730
Sylvain Munautad202d72021-02-04 20:37:01 +0100731 rxBurst = demodAnyBurst(*burst, (CorrType) rc, cfg->rx_sps, &ebp);
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200732 bi->toa = ebp.toa;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200733 bi->tsc = ebp.tsc;
734 bi->ci = ebp.ci;
Thomas Tsouef25dba2013-11-14 15:31:24 -0500735
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200736 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200737 if (rxBurst->size() == EDGE_BURST_NBITS) {
738 bi->modulation = MODULATION_8PSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200739 bi->nbits = EDGE_BURST_NBITS;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200740 } else { /* size() here is actually gSlotLen + 8, due to guard periods */
741 bi->modulation = MODULATION_GMSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200742 bi->nbits = gSlotLen;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200743 }
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200744
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200745 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200746 vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200747
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200748 delete rxBurst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500749 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200750 return 0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200751
752ret_idle:
Pau Espin Pedrol1d165a02020-07-27 11:52:42 +0200753 if (ctr_changed)
754 dispatch_trx_rate_ctr_change(state, chan);
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200755 bi->idle = true;
756 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200757 return 0;
dburgessb3a0ca42011-10-12 07:44:40 +0000758}
759
dburgessb3a0ca42011-10-12 07:44:40 +0000760void Transceiver::reset()
761{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400762 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
763 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000764}
765
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200766
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700767/**
768 * Matches a buffer with a command.
769 * @param buf a buffer to look command in
770 * @param cmd a command to look in buffer
771 * @param params pointer to arguments, or NULL
772 * @return true if command matches, otherwise false
773 */
774static bool match_cmd(char *buf,
775 const char *cmd, char **params)
776{
777 size_t cmd_len = strlen(cmd);
778
779 /* Check a command itself */
780 if (strncmp(buf, cmd, cmd_len))
781 return false;
782
783 /* A command has arguments */
784 if (params != NULL) {
785 /* Make sure there is a space */
786 if (buf[cmd_len] != ' ')
787 return false;
788
789 /* Update external pointer */
790 *params = buf + cmd_len + 1;
791 }
792
793 return true;
794}
795
Erica7143bf2020-04-08 01:39:17 +0200796void Transceiver::ctrl_sock_send(ctrl_msg& m, int chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000797{
Erica7143bf2020-04-08 01:39:17 +0200798 ctrl_sock_state& s = mCtrlSockets[chan];
799 struct osmo_fd *conn_bfd = &s.conn_bfd;
800
801 s.txmsgqueue.push_back(m);
Harald Welte94def472020-10-19 12:28:26 +0200802 osmo_fd_write_enable(conn_bfd);
Erica7143bf2020-04-08 01:39:17 +0200803}
804
805int Transceiver::ctrl_sock_write(int chan)
806{
807 int rc;
808 ctrl_sock_state& s = mCtrlSockets[chan];
809
810 if (s.conn_bfd.fd < 0) {
811 return -EIO;
812 }
813
814 while (s.txmsgqueue.size()) {
815 const ctrl_msg m = s.txmsgqueue.front();
816
Harald Welte94def472020-10-19 12:28:26 +0200817 osmo_fd_write_disable(&s.conn_bfd);
Erica7143bf2020-04-08 01:39:17 +0200818
819 /* try to send it over the socket */
820 rc = write(s.conn_bfd.fd, m.data, strlen(m.data) + 1);
821 if (rc == 0)
822 goto close;
823 if (rc < 0) {
824 if (errno == EAGAIN) {
Harald Welte94def472020-10-19 12:28:26 +0200825 osmo_fd_write_enable(&s.conn_bfd);
Erica7143bf2020-04-08 01:39:17 +0200826 break;
827 }
828 goto close;
829 }
830
831 s.txmsgqueue.pop_front();
832 }
833 return 0;
834
835close:
836 LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets write(" << s.conn_bfd.fd << ") failed: " << rc;
837 return -1;
838}
839
840int Transceiver::ctrl_sock_handle_rx(int chan)
841{
842 ctrl_msg cmd_received;
843 ctrl_msg cmd_to_send;
844 char *buffer = cmd_received.data;
845 char *response = cmd_to_send.data;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700846 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700847 int msgLen;
Erica7143bf2020-04-08 01:39:17 +0200848 ctrl_sock_state& s = mCtrlSockets[chan];
Thomas Tsoud647ec52013-10-29 15:17:34 -0400849
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700850 /* Attempt to read from control socket */
Erica7143bf2020-04-08 01:39:17 +0200851 msgLen = read(s.conn_bfd.fd, buffer, sizeof(cmd_received.data)-1);
852 if (msgLen < 0 && errno == EAGAIN)
853 return 0; /* Try again later */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200854 if (msgLen <= 0) {
Erica7143bf2020-04-08 01:39:17 +0200855 LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets read(" << s.conn_bfd.fd << ") failed: " << msgLen;
856 return -EIO;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200857 }
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700858
Erica7143bf2020-04-08 01:39:17 +0200859
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700860 /* Zero-terminate received string */
861 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000862
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700863 /* Verify a command signature */
864 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100865 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
Erica7143bf2020-04-08 01:39:17 +0200866 return -EIO;
dburgessb3a0ca42011-10-12 07:44:40 +0000867 }
dburgessb3a0ca42011-10-12 07:44:40 +0000868
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700869 /* Set command pointer */
870 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200871 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700872
873 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800874 stop();
875 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700876 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700877 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000878 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700879 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000880 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300881 for (int i = 0; i < 8; i++) {
882 for (int j = 0; j < 8; j++)
883 mHandover[i][j] = false;
884 }
Tom Tsou365bc382016-10-19 15:26:04 -0700885 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700886 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700887 unsigned ts = 0, ss = 0;
888 sscanf(params, "%u %u", &ts, &ss);
889 if (ts > 7 || ss > 7) {
Pau Espin Pedrol0fafe032019-11-27 13:17:59 +0100890 sprintf(response, "RSP HANDOVER 1 %u %u", ts, ss);
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700891 } else {
892 mHandover[ts][ss] = true;
893 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
894 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700895 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700896 unsigned ts = 0, ss = 0;
897 sscanf(params, "%u %u", &ts, &ss);
898 if (ts > 7 || ss > 7) {
899 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
900 } else {
901 mHandover[ts][ss] = false;
902 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
903 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700904 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000905 //set expected maximum time-of-arrival
906 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700907 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300908 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000909 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700910 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300911 //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 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
915 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700916 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000917 //set expected maximum time-of-arrival
918 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700919 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400920 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000921 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700922 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000923 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500924 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000925 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500926 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000927 }
928 else {
Pau Espin Pedrol1b3a8882020-05-29 16:15:18 +0200929 sprintf(response,"RSP NOISELEV 1 0");
dburgessb3a0ca42011-10-12 07:44:40 +0000930 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700931 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800932 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700933 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800934 power = mRadioInterface->setPowerAttenuation(power, chan);
935 mStates[chan].mPower = power;
936 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700937 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800938 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700939 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800940 power = mStates[chan].mPower + step;
941 power = mRadioInterface->setPowerAttenuation(power, chan);
942 mStates[chan].mPower = power;
943 sprintf(response, "RSP ADJPOWER 0 %d", power);
Pau Espin Pedrol0e09e7c2020-05-29 16:39:07 +0200944} else if (match_cmd(command, "NOMTXPOWER", NULL)) {
945 int power = mRadioInterface->getNominalTxPower(chan);
Pau Espin Pedrol405f17a2020-06-19 14:48:35 +0200946 sprintf(response, "RSP NOMTXPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700947 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000948 // tune receiver
949 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700950 sscanf(params, "%d", &freqKhz);
Vadim Yanitskiy3b8f7c42018-08-23 13:41:06 +0700951 mRxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400952 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100953 LOGCHAN(chan, DTRXCTRL, FATAL) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000954 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
955 }
956 else
957 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700958 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000959 // tune txmtr
960 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700961 sscanf(params, "%d", &freqKhz);
Vadim Yanitskiy3b8f7c42018-08-23 13:41:06 +0700962 mTxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400963 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100964 LOGCHAN(chan, DTRXCTRL, FATAL) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000965 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
966 }
967 else
968 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700969 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000970 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500971 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700972 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700973 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500974 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700975 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100976 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000977 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400978 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000979 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700980 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700981 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000982 int corrCode;
983 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700984 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000985 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100986 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000987 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
Erica7143bf2020-04-08 01:39:17 +0200988 return 0;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200989 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400990 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
991 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000992 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200993 } else if (match_cmd(command, "SETFORMAT", &params)) {
994 // set TRXD protocol version
995 unsigned version_recv;
996 sscanf(params, "%u", &version_recv);
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200997 LOGCHAN(chan, DTRXCTRL, INFO) << "BTS requests TRXD version switch: " << version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200998 if (version_recv > TRX_DATA_FORMAT_VER) {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200999 LOGCHAN(chan, DTRXCTRL, INFO) << "rejecting TRXD version " << version_recv
Vadim Yanitskiyb6f238c2021-05-27 23:35:21 +02001000 << " in favor of " << TRX_DATA_FORMAT_VER;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +02001001 sprintf(response, "RSP SETFORMAT %u %u", TRX_DATA_FORMAT_VER, version_recv);
1002 } else {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001003 LOGCHAN(chan, DTRXCTRL, NOTICE) << "switching to TRXD version " << version_recv;
1004 mVersionTRXD[chan] = version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +02001005 sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
1006 }
Pau Espin Pedrol7d8676a2020-08-26 14:30:46 +02001007 } else if (match_cmd(command, "RFMUTE", &params)) {
1008 // (Un)mute RF TX and RX
1009 unsigned mute;
1010 sscanf(params, "%u", &mute);
1011 mStates[chan].mMuted = mute ? true : false;
1012 sprintf(response, "RSP RFMUTE 0 %u", mute);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001013 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Martin Hauke066fd042019-10-13 19:08:00 +02001014 // debug command! may change or disappear without notice
Alexander Chemerise692ce92015-06-12 00:15:31 -04001015 // set a mask which bursts to dump to disk
1016 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001017 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -04001018 mWriteBurstToDiskMask = mask;
1019 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +07001020 } else {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001021 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +02001022 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +00001023 }
1024
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +02001025 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Erica7143bf2020-04-08 01:39:17 +02001026 transceiver->ctrl_sock_send(cmd_to_send, chan);
1027 return 0;
dburgessb3a0ca42011-10-12 07:44:40 +00001028}
1029
Thomas Tsou204a9f12013-10-29 18:34:16 -04001030bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001031{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001032 int msgLen;
Tom Tsoue8871082016-07-01 02:46:04 -07001033 int burstLen;
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001034 struct trxd_hdr_v01_dl *dl;
1035 char buffer[sizeof(*dl) + EDGE_BURST_NBITS];
Vadim Yanitskiyb3123252019-07-15 23:53:08 +07001036 uint32_t fn;
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001037 uint8_t tn;
dburgessb3a0ca42011-10-12 07:44:40 +00001038
1039 // check data socket
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001040 msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
1041 if (msgLen <= 0) {
Eric1a26b4f2020-04-13 00:03:01 +02001042 LOGCHAN(chan, DTRXDDL, NOTICE) << "mDataSockets read(" << mDataSockets[chan] << ") failed: " << msgLen;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001043 return false;
1044 }
dburgessb3a0ca42011-10-12 07:44:40 +00001045
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001046 switch (msgLen) {
1047 case sizeof(*dl) + gSlotLen: /* GSM burst */
1048 burstLen = gSlotLen;
1049 break;
1050 case sizeof(*dl) + EDGE_BURST_NBITS: /* EDGE burst */
Pau Espin Pedrol93fee1f2020-10-13 17:27:08 +02001051 if (cfg->tx_sps != 4) {
1052 LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << cfg->tx_sps;
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001053 return false;
1054 }
1055 burstLen = EDGE_BURST_NBITS;
1056 break;
1057 default:
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001058 LOGCHAN(chan, DTRXDDL, ERROR) << "badly formatted packet on GSM->TRX interface (len="<< msgLen << ")";
Tom Tsoue8871082016-07-01 02:46:04 -07001059 return false;
dburgessb3a0ca42011-10-12 07:44:40 +00001060 }
1061
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001062 dl = (struct trxd_hdr_v01_dl *) buffer;
dburgessb3a0ca42011-10-12 07:44:40 +00001063
Vadim Yanitskiyb3123252019-07-15 23:53:08 +07001064 /* Convert TDMA FN to the host endianness */
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001065 fn = osmo_load32be(&dl->common.fn);
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001066 tn = dl->common.tn;
Vadim Yanitskiyb3123252019-07-15 23:53:08 +07001067
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001068 /* Make sure we support the received header format */
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001069 switch (dl->common.version) {
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001070 case 0:
1071 /* Version 1 has the same format */
1072 case 1:
1073 break;
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001074 default:
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001075 LOGCHAN(chan, DTRXDDL, ERROR) << "Rx TRXD message with unknown header version " << unsigned(dl->common.version);
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001076 return false;
1077 }
1078
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001079 LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version)
Pau Espin Pedrol99330742020-07-15 14:05:31 +02001080 << "): fn=" << fn << ", tn=" << unsigned(tn) << ", burst_len=" << burstLen;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001081
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001082 TransceiverState *state = &mStates[chan];
1083 GSM::Time currTime = GSM::Time(fn, tn);
1084
1085 /* Verify proper FN order in DL stream */
1086 if (state->first_dl_fn_rcv[tn]) {
1087 int32_t delta = GSM::FNDelta(currTime.FN(), state->last_dl_time_rcv[tn].FN());
1088 if (delta == 1) {
1089 /* usual expected scenario, continue code flow */
1090 } else if (delta == 0) {
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +02001091 LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with repeated FN " << currTime;
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001092 state->ctrs.tx_trxd_fn_repeated++;
1093 dispatch_trx_rate_ctr_change(state, chan);
1094 return true;
1095 } else if (delta < 0) {
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +02001096 LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with previous FN " << currTime
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001097 << " vs last " << state->last_dl_time_rcv[tn];
1098 state->ctrs.tx_trxd_fn_outoforder++;
1099 dispatch_trx_rate_ctr_change(state, chan);
1100 /* Allow adding radio vector below, since it gets sorted in the queue */
1101 } else if (chan == 0 && state->mFiller == FILLER_ZERO) {
1102 /* delta > 1. Some FN was lost in the middle. We can only easily rely
1103 * on consecutive FNs in TRX0 since it must transmit continuously in all
1104 * setups. Also, osmo-trx supports optionally filling empty bursts on
1105 * its own. In that case bts-trx is not obliged to submit all bursts. */
Pau Espin Pedrolc249ce22020-07-17 18:33:10 +02001106 LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with future FN " << currTime
Pau Espin Pedrolc0d6fd22020-07-09 16:51:47 +02001107 << " vs last " << state->last_dl_time_rcv[tn]
1108 << ", " << delta - 1 << " FN lost";
1109 state->ctrs.tx_trxd_fn_skipped += delta - 1;
1110 dispatch_trx_rate_ctr_change(state, chan);
1111 }
1112 if (delta > 0)
1113 state->last_dl_time_rcv[tn] = currTime;
1114 } else { /* Initial check, simply store state */
1115 state->first_dl_fn_rcv[tn] = true;
1116 state->last_dl_time_rcv[tn] = currTime;
1117 }
1118
Tom Tsou7c741ec2016-07-19 11:20:59 -07001119 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +00001120 BitVector::iterator itr = newBurst.begin();
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001121 uint8_t *bufferItr = dl->soft_bits;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001122 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +00001123 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001124
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001125 addRadioVector(chan, newBurst, dl->tx_att, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +00001126
1127 return true;
dburgessb3a0ca42011-10-12 07:44:40 +00001128}
dburgessb3a0ca42011-10-12 07:44:40 +00001129
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001130bool Transceiver::driveReceiveRadio()
Thomas Tsou204a9f12013-10-29 18:34:16 -04001131{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +02001132 int rc = mRadioInterface->driveReceiveRadio();
1133 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001134 usleep(100000);
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001135 return true;
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -04001136 }
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001137 if (rc < 0)
1138 return false;
1139
1140 if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
Philipp Maierfdefbfa2020-05-22 12:17:13 +02001141 if (mForceClockInterface)
1142 LOGC(DTRXCLK, NOTICE) << "Sending CLOCK indications";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001143 mForceClockInterface = false;
1144 return writeClockInterface();
1145 }
1146 return true;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001147}
1148
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001149void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001150{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001151 std::ostringstream os;
1152 for (size_t i=0; i < bi->nbits; i++) {
1153 if (bi->rx_burst[i] > 0.5) os << "1";
1154 else if (bi->rx_burst[i] > 0.25) os << "|";
1155 else if (bi->rx_burst[i] > 0.0) os << "'";
1156 else os << "-";
1157 }
1158
Pau Espin Pedrole91544d2020-10-13 17:03:37 +02001159 double rssi_offset = rssiOffset(chan);
1160
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001161 LOGCHAN(chan, DTRXDUL, DEBUG) << std::fixed << std::right
Pau Espin Pedrol1fba1042019-09-06 14:22:31 +02001162 << " time: " << unsigned(bi->tn) << ":" << bi->fn
Pau Espin Pedrole91544d2020-10-13 17:03:37 +02001163 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssi_offset)
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001164 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
Pau Espin Pedrole91544d2020-10-13 17:03:37 +02001165 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssi_offset)
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001166 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001167 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001168 << " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB"
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001169 << " bits: " << os;
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001170}
1171
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001172bool Transceiver::driveReceiveFIFO(size_t chan)
Thomas Tsou204a9f12013-10-29 18:34:16 -04001173{
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001174 struct trx_ul_burst_ind bi;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001175 int rc;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001176
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001177 if ((rc = pullRadioVector(chan, &bi)) < 0) {
1178 if (rc == -ENOENT) { /* timeslot off, continue processing */
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001179 LOGCHAN(chan, DTRXDUL, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " timeslot is off";
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001180 return true;
1181 }
1182 return false; /* other errors: we want to stop the process */
1183 }
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001184
Pau Espin Pedrol199a3062020-07-27 10:58:51 +02001185 if (!bi.idle && log_check_level(DTRXDUL, LOGL_DEBUG))
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001186 logRxBurst(chan, &bi);
dburgessb3a0ca42011-10-12 07:44:40 +00001187
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001188 switch (mVersionTRXD[chan]) {
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001189 case 0:
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001190 return trxd_send_burst_ind_v0(chan, mDataSockets[chan], &bi);
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001191 case 1:
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001192 return trxd_send_burst_ind_v1(chan, mDataSockets[chan], &bi);
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001193 default:
1194 OSMO_ASSERT(false);
1195 }
dburgessb3a0ca42011-10-12 07:44:40 +00001196}
1197
Thomas Tsou204a9f12013-10-29 18:34:16 -04001198void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +00001199{
1200
1201 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001202 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +00001203 assure that transmit packets arrive at the radio/USRP
1204 before they need to be transmitted.
1205
1206 Deadline clock indicates the burst that needs to be
1207 pushed into the FIFO right NOW. If transmit queue does
1208 not have a burst, stick in filler data.
1209 */
1210
1211
1212 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001213
dburgessb3a0ca42011-10-12 07:44:40 +00001214 if (mOn) {
1215 //radioClock->wait(); // wait until clock updates
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001216 LOGC(DTRXCLK, DEBUG) << "radio clock " << radioClock->get();
dburgessb3a0ca42011-10-12 07:44:40 +00001217 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1218 // if underrun, then we're not providing bursts to radio/USRP fast
1219 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001220 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001221 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001222 // only update latency at the defined frame interval
1223 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001224 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001225 LOGC(DTRXCLK, INFO) << "new latency: " << mTransmitLatency << " (underrun "
1226 << radioClock->get() << " vs "
1227 << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001228 mLatencyUpdateTime = radioClock->get();
1229 }
1230 }
1231 else {
1232 // if underrun hasn't occurred in the last sec (216 frames) drop
1233 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001234 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001235 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1236 mTransmitLatency.decTN();
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001237 LOGC(DTRXCLK, INFO) << "reduced latency: " << mTransmitLatency;
kurtis.heimerle380af32011-11-26 03:18:55 +00001238 mLatencyUpdateTime = radioClock->get();
1239 }
1240 }
1241 }
dburgessb3a0ca42011-10-12 07:44:40 +00001242 }
dburgessb3a0ca42011-10-12 07:44:40 +00001243 // time to push burst to transmit FIFO
1244 pushRadioVector(mTransmitDeadlineClock);
1245 mTransmitDeadlineClock.incTN();
1246 }
dburgessb3a0ca42011-10-12 07:44:40 +00001247 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001248
1249 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001250}
1251
1252
1253
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001254bool Transceiver::writeClockInterface()
dburgessb3a0ca42011-10-12 07:44:40 +00001255{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001256 int msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001257 char command[50];
1258 // FIXME -- This should be adaptive.
1259 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1260
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001261 LOGC(DTRXCLK, INFO) << "sending " << command;
dburgessb3a0ca42011-10-12 07:44:40 +00001262
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001263 msgLen = write(mClockSocket, command, strlen(command) + 1);
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001264 if (msgLen <= 0) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001265 LOGC(DTRXCLK, ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001266 return false;
1267 }
dburgessb3a0ca42011-10-12 07:44:40 +00001268
1269 mLastClockUpdateTime = mTransmitDeadlineClock;
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001270 return true;
Thomas Tsou92c16df2013-09-28 18:04:19 -04001271}
dburgessb3a0ca42011-10-12 07:44:40 +00001272
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001273void *RxUpperLoopAdapter(TrxChanThParams *params)
Thomas Tsou204a9f12013-10-29 18:34:16 -04001274{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001275 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001276 Transceiver *trx = params->trx;
1277 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001278
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001279 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001280
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001281 snprintf(thread_name, 16, "RxUpper%zu", num);
1282 set_selfthread_name(thread_name);
Pau Espin Pedrol553a2502020-07-29 18:05:25 +02001283 OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001284
Thomas Tsou204a9f12013-10-29 18:34:16 -04001285 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001286 if (!trx->driveReceiveFIFO(num)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001287 LOGCHAN(num, DTRXDUL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001288 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1289 break;
1290 }
Thomas Tsou204a9f12013-10-29 18:34:16 -04001291 pthread_testcancel();
1292 }
1293 return NULL;
1294}
1295
1296void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001297{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001298 set_selfthread_name("RxLower");
Pau Espin Pedrol553a2502020-07-29 18:05:25 +02001299 OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001300
dburgessb3a0ca42011-10-12 07:44:40 +00001301 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001302 if (!transceiver->driveReceiveRadio()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001303 LOGC(DTRXDUL, FATAL) << "Something went wrong in thread RxLower, requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001304 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1305 break;
1306 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001307 pthread_testcancel();
1308 }
1309 return NULL;
1310}
1311
Thomas Tsou204a9f12013-10-29 18:34:16 -04001312void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001313{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001314 set_selfthread_name("TxLower");
Pau Espin Pedrol553a2502020-07-29 18:05:25 +02001315 OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001316
Thomas Tsou92c16df2013-09-28 18:04:19 -04001317 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001318 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001319 pthread_testcancel();
1320 }
1321 return NULL;
1322}
1323
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001324void *TxUpperLoopAdapter(TrxChanThParams *params)
dburgessb3a0ca42011-10-12 07:44:40 +00001325{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001326 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001327 Transceiver *trx = params->trx;
1328 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001329
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001330 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001331
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001332 snprintf(thread_name, 16, "TxUpper%zu", num);
1333 set_selfthread_name(thread_name);
Pau Espin Pedrol553a2502020-07-29 18:05:25 +02001334 OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001335
dburgessb3a0ca42011-10-12 07:44:40 +00001336 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001337 if (!trx->driveTxPriorityQueue(num)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001338 LOGCHAN(num, DTRXDDL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001339 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1340 break;
1341 }
dburgessb3a0ca42011-10-12 07:44:40 +00001342 pthread_testcancel();
1343 }
1344 return NULL;
1345}