blob: d9bda1d931f15b9772e2fd2e17b31b4102b656d5 [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 Pedroldb936b92018-09-03 16:50:49 +020040}
41
ttsou2173abf2012-08-08 00:51:31 +000042#ifdef HAVE_CONFIG_H
43#include "config.h"
44#endif
dburgessb3a0ca42011-10-12 07:44:40 +000045
Alexander Chemerisd734e2d2013-06-16 14:30:58 +040046using namespace GSM;
47
Erica7143bf2020-04-08 01:39:17 +020048Transceiver *transceiver;
49
kurtis.heimerlec842de2012-11-23 08:37:32 +000050#define USB_LATENCY_INTRVL 10,0
ttsou2173abf2012-08-08 00:51:31 +000051
Thomas Tsoufa3a7872013-10-17 21:23:34 -040052/* Number of running values use in noise average */
53#define NOISE_CNT 20
ttsoue8dde022012-12-06 15:43:55 +000054
Thomas Tsouf0782732013-10-29 15:55:47 -040055TransceiverState::TransceiverState()
Tom Tsoua4d1a412014-11-25 15:46:56 -080056 : mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
Thomas Tsouf0782732013-10-29 15:55:47 -040057{
58 for (int i = 0; i < 8; i++) {
59 chanType[i] = Transceiver::NONE;
60 fillerModulus[i] = 26;
61 chanResponse[i] = NULL;
62 DFEForward[i] = NULL;
63 DFEFeedback[i] = NULL;
64
65 for (int n = 0; n < 102; n++)
66 fillerTable[n][i] = NULL;
67 }
68}
69
70TransceiverState::~TransceiverState()
71{
72 for (int i = 0; i < 8; i++) {
73 delete chanResponse[i];
74 delete DFEForward[i];
75 delete DFEFeedback[i];
76
77 for (int n = 0; n < 102; n++)
78 delete fillerTable[n][i];
79 }
80}
81
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010082bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
Tom Tsou64ad7122015-05-19 18:26:31 -070083{
Tom Tsou64ad7122015-05-19 18:26:31 -070084 signalVector *burst;
85
86 if ((sps != 1) && (sps != 4))
87 return false;
88
89 for (size_t n = 0; n < 8; n++) {
Tom Tsou64ad7122015-05-19 18:26:31 -070090 for (size_t i = 0; i < 102; i++) {
91 switch (filler) {
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010092 case FILLER_DUMMY:
Tom Tsou8ee2f382016-03-06 20:57:34 -080093 burst = generateDummyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070094 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010095 case FILLER_NORM_RAND:
Tom Tsou8ee2f382016-03-06 20:57:34 -080096 burst = genRandNormalBurst(rtsc, sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070097 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010098 case FILLER_EDGE_RAND:
Tom Tsouaf717b22016-03-06 22:19:15 -080099 burst = generateEdgeBurst(rtsc);
100 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100101 case FILLER_ACCESS_RAND:
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300102 burst = genRandAccessBurst(rach_delay, sps, n);
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300103 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100104 case FILLER_ZERO:
Tom Tsou64ad7122015-05-19 18:26:31 -0700105 default:
Tom Tsou8ee2f382016-03-06 20:57:34 -0800106 burst = generateEmptyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -0700107 }
108
109 scaleVector(*burst, scale);
110 fillerTable[i][n] = burst;
111 }
112
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100113 if ((filler == FILLER_NORM_RAND) ||
114 (filler == FILLER_EDGE_RAND)) {
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700115 chanType[n] = TSC;
Tom Tsouaf717b22016-03-06 22:19:15 -0800116 }
Thomas Tsou15d743e2014-01-25 02:34:03 -0500117 }
Tom Tsou64ad7122015-05-19 18:26:31 -0700118
119 return false;
Thomas Tsouf0782732013-10-29 15:55:47 -0400120}
121
dburgessb3a0ca42011-10-12 07:44:40 +0000122Transceiver::Transceiver(int wBasePort,
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200123 const char *TRXAddress,
124 const char *GSMcoreAddress,
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800125 size_t tx_sps, size_t rx_sps, size_t chans,
Alexander Chemerise8905a02015-06-03 23:47:56 -0400126 GSM::Time wTransmitLatency,
127 RadioInterface *wRadioInterface,
Eric Wildac0487e2019-06-17 13:02:44 +0200128 double wRssiOffset, int wStackSize)
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200129 : mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200130 mClockSocket(-1), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
Eric Wildac0487e2019-06-17 13:02:44 +0200131 rssiOffset(wRssiOffset), stackSize(wStackSize),
Pau Espin Pedrol758381b2019-07-16 21:55:03 +0200132 mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mExtRACH(false), mEdge(false),
133 mOn(false), mForceClockInterface(false),
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300134 mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200135 mWriteBurstToDiskMask(0)
dburgessb3a0ca42011-10-12 07:44:40 +0000136{
dburgessb3a0ca42011-10-12 07:44:40 +0000137 txFullScale = mRadioInterface->fullScaleInputValue();
138 rxFullScale = mRadioInterface->fullScaleOutputValue();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300139
140 for (int i = 0; i < 8; i++) {
141 for (int j = 0; j < 8; j++)
142 mHandover[i][j] = false;
143 }
dburgessb3a0ca42011-10-12 07:44:40 +0000144}
145
146Transceiver::~Transceiver()
147{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800148 stop();
149
dburgessb3a0ca42011-10-12 07:44:40 +0000150 sigProcLibDestroy();
Thomas Tsoud647ec52013-10-29 15:17:34 -0400151
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200152 if (mClockSocket >= 0)
153 close(mClockSocket);
154
Thomas Tsou204a9f12013-10-29 18:34:16 -0400155 for (size_t i = 0; i < mChans; i++) {
156 mTxPriorityQueues[i].clear();
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200157 if (mDataSockets[i] >= 0)
158 close(mDataSockets[i]);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400159 }
dburgessb3a0ca42011-10-12 07:44:40 +0000160}
Thomas Tsou83e06892013-08-20 16:10:01 -0400161
Erica7143bf2020-04-08 01:39:17 +0200162int Transceiver::ctrl_sock_cb(struct osmo_fd *bfd, unsigned int flags)
163{
164 int rc = 0;
165 int chan = static_cast<int>(reinterpret_cast<uintptr_t>(bfd->data));
166
167 if (flags & OSMO_FD_READ)
168 rc = transceiver->ctrl_sock_handle_rx(chan);
169 if (rc < 0)
170 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
171
172 if (flags & OSMO_FD_WRITE)
173 rc = transceiver->ctrl_sock_write(chan);
174 if (rc < 0)
175 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
176
177 return rc;
178}
179
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800180/*
181 * Initialize transceiver
182 *
183 * Start or restart the control loop. Any further control is handled through the
184 * socket API. Randomize the central radio clock set the downlink burst
185 * counters. Note that the clock will not update until the radio starts, but we
186 * are still expected to report clock indications through control channel
187 * activity.
188 */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200189bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
190 bool edge, bool ext_rach)
Thomas Tsou83e06892013-08-20 16:10:01 -0400191{
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500192 int d_srcport, d_dstport, c_srcport, c_dstport;
Thomas Tsouf0782732013-10-29 15:55:47 -0400193
Thomas Tsou204a9f12013-10-29 18:34:16 -0400194 if (!mChans) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100195 LOG(FATAL) << "No channels assigned";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400196 return false;
197 }
198
Tom Tsou2079a3c2016-03-06 00:58:56 -0800199 if (!sigProcLibSetup()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100200 LOG(FATAL) << "Failed to initialize signal processing library";
Thomas Tsou83e06892013-08-20 16:10:01 -0400201 return false;
202 }
203
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200204 mExtRACH = ext_rach;
Tom Tsou64464e62016-07-01 03:46:46 -0700205 mEdge = edge;
206
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200207 mDataSockets.resize(mChans, -1);
Erica7143bf2020-04-08 01:39:17 +0200208 mCtrlSockets.resize(mChans);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400209 mTxPriorityQueueServiceLoopThreads.resize(mChans);
210 mRxServiceLoopThreads.resize(mChans);
211
212 mTxPriorityQueues.resize(mChans);
213 mReceiveFIFO.resize(mChans);
214 mStates.resize(mChans);
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200215 mVersionTRXD.resize(mChans);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400216
Thomas Tsouccb73e12014-04-15 17:41:28 -0400217 /* Filler table retransmissions - support only on channel 0 */
Tom Tsou64ad7122015-05-19 18:26:31 -0700218 if (filler == FILLER_DUMMY)
Thomas Tsouccb73e12014-04-15 17:41:28 -0400219 mStates[0].mRetrans = true;
220
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800221 /* Setup sockets */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200222 mClockSocket = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
223 mLocalAddr.c_str(), mBasePort,
224 mRemoteAddr.c_str(), mBasePort + 100,
225 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
Pau Espin Pedrole7195ac2019-08-26 10:55:04 +0200226 if (mClockSocket < 0)
227 return false;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200228
Thomas Tsou204a9f12013-10-29 18:34:16 -0400229 for (size_t i = 0; i < mChans; i++) {
Erica7143bf2020-04-08 01:39:17 +0200230 int rv;
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500231 c_srcport = mBasePort + 2 * i + 1;
232 c_dstport = mBasePort + 2 * i + 101;
233 d_srcport = mBasePort + 2 * i + 2;
234 d_dstport = mBasePort + 2 * i + 102;
235
Erica7143bf2020-04-08 01:39:17 +0200236 rv = osmo_sock_init2_ofd(&mCtrlSockets[i].conn_bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200237 mLocalAddr.c_str(), c_srcport,
238 mRemoteAddr.c_str(), c_dstport,
239 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
Erica7143bf2020-04-08 01:39:17 +0200240 if (rv < 0)
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200241 return false;
242
Erica7143bf2020-04-08 01:39:17 +0200243 mCtrlSockets[i].conn_bfd.cb = ctrl_sock_cb;
244 mCtrlSockets[i].conn_bfd.data = reinterpret_cast<void*>(i);
245
246
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200247 mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
248 mLocalAddr.c_str(), d_srcport,
249 mRemoteAddr.c_str(), d_dstport,
250 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
Eric1a26b4f2020-04-13 00:03:01 +0200251 if (mDataSockets[i] < 0)
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200252 return false;
Erica7143bf2020-04-08 01:39:17 +0200253
254 if (i && filler == FILLER_DUMMY)
255 filler = FILLER_ZERO;
256
257 mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400258 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400259
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800260 /* Randomize the central clock */
261 GSM::Time startTime(random() % gHyperframe, 0);
262 mRadioInterface->getClock()->set(startTime);
263 mTransmitDeadlineClock = startTime;
264 mLastClockUpdateTime = startTime;
265 mLatencyUpdateTime = startTime;
266
Thomas Tsou83e06892013-08-20 16:10:01 -0400267 return true;
268}
dburgessb3a0ca42011-10-12 07:44:40 +0000269
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800270/*
271 * Start the transceiver
272 *
273 * Submit command(s) to the radio device to commence streaming samples and
274 * launch threads to handle sample I/O. Re-synchronize the transmit burst
275 * counters to the central radio clock here as well.
276 */
277bool Transceiver::start()
278{
279 ScopedLock lock(mLock);
280
281 if (mOn) {
282 LOG(ERR) << "Transceiver already running";
Ivan Kluchnikov194a9b12015-04-23 17:08:27 +0300283 return true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800284 }
285
286 LOG(NOTICE) << "Starting the transceiver";
287
288 GSM::Time time = mRadioInterface->getClock()->get();
289 mTransmitDeadlineClock = time;
290 mLastClockUpdateTime = time;
291 mLatencyUpdateTime = time;
292
293 if (!mRadioInterface->start()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100294 LOG(FATAL) << "Device failed to start";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800295 return false;
296 }
297
298 /* Device is running - launch I/O threads */
Eric Wildac0487e2019-06-17 13:02:44 +0200299 mRxLowerLoopThread = new Thread(stackSize);
300 mTxLowerLoopThread = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800301 mTxLowerLoopThread->start((void * (*)(void*))
302 TxLowerLoopAdapter,(void*) this);
303 mRxLowerLoopThread->start((void * (*)(void*))
304 RxLowerLoopAdapter,(void*) this);
305
306 /* Launch uplink and downlink burst processing threads */
307 for (size_t i = 0; i < mChans; i++) {
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200308 TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
309 params->trx = this;
310 params->num = i;
Eric Wildac0487e2019-06-17 13:02:44 +0200311 mRxServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800312 mRxServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200313 RxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800314
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200315 params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
316 params->trx = this;
317 params->num = i;
Eric Wildac0487e2019-06-17 13:02:44 +0200318 mTxPriorityQueueServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800319 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200320 TxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800321 }
322
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200323 mForceClockInterface = true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800324 mOn = true;
325 return true;
326}
327
328/*
329 * Stop the transceiver
330 *
331 * Perform stopping by disabling receive streaming and issuing cancellation
332 * requests to running threads. Most threads will timeout and terminate once
333 * device is disabled, but the transmit loop may block waiting on the central
334 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
335 * makes it to the thread cancellation point.
336 */
337void Transceiver::stop()
338{
339 ScopedLock lock(mLock);
340
341 if (!mOn)
342 return;
343
344 LOG(NOTICE) << "Stopping the transceiver";
345 mTxLowerLoopThread->cancel();
346 mRxLowerLoopThread->cancel();
Tom Tsoud67bd602017-06-15 15:35:02 -0700347 mTxLowerLoopThread->join();
348 mRxLowerLoopThread->join();
349 delete mTxLowerLoopThread;
350 delete mRxLowerLoopThread;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800351
352 for (size_t i = 0; i < mChans; i++) {
353 mRxServiceLoopThreads[i]->cancel();
354 mTxPriorityQueueServiceLoopThreads[i]->cancel();
355 }
356
357 LOG(INFO) << "Stopping the device";
358 mRadioInterface->stop();
359
360 for (size_t i = 0; i < mChans; i++) {
361 mRxServiceLoopThreads[i]->join();
362 mTxPriorityQueueServiceLoopThreads[i]->join();
363 delete mRxServiceLoopThreads[i];
364 delete mTxPriorityQueueServiceLoopThreads[i];
365
366 mTxPriorityQueues[i].clear();
367 }
368
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800369 mOn = false;
370 LOG(NOTICE) << "Transceiver stopped";
371}
372
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500373void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400374 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000375{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500376 signalVector *burst;
377 radioVector *radio_burst;
378
Thomas Tsou204a9f12013-10-29 18:34:16 -0400379 if (chan >= mTxPriorityQueues.size()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100380 LOGCHAN(chan, DTRXDDL, FATAL) << "Invalid channel";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400381 return;
382 }
383
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500384 if (wTime.TN() > 7) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100385 LOGCHAN(chan, DTRXDDL, FATAL) << "Received burst with invalid slot " << wTime.TN();
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500386 return;
387 }
388
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800389 /* Use the number of bits as the EDGE burst indicator */
390 if (bits.size() == EDGE_BURST_NBITS)
391 burst = modulateEdgeBurst(bits, mSPSTx);
392 else
393 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
394
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500395 scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
dburgessb3a0ca42011-10-12 07:44:40 +0000396
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500397 radio_burst = new radioVector(wTime, burst);
398
399 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000400}
401
Thomas Tsou15d743e2014-01-25 02:34:03 -0500402void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
403{
404 int TN, modFN;
405 TransceiverState *state = &mStates[chan];
406
407 TN = burst->getTime().TN();
408 modFN = burst->getTime().FN() % state->fillerModulus[TN];
409
410 delete state->fillerTable[modFN][TN];
411 state->fillerTable[modFN][TN] = burst->getVector();
412 burst->setVector(NULL);
413}
414
dburgessb3a0ca42011-10-12 07:44:40 +0000415void Transceiver::pushRadioVector(GSM::Time &nowTime)
416{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400417 int TN, modFN;
418 radioVector *burst;
419 TransceiverState *state;
420 std::vector<signalVector *> bursts(mChans);
421 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500422 std::vector<bool> filler(mChans, true);
dburgessb3a0ca42011-10-12 07:44:40 +0000423
Thomas Tsou204a9f12013-10-29 18:34:16 -0400424 for (size_t i = 0; i < mChans; i ++) {
425 state = &mStates[i];
dburgessb3a0ca42011-10-12 07:44:40 +0000426
Thomas Tsou204a9f12013-10-29 18:34:16 -0400427 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100428 LOGCHAN(i, DTRXDDL, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
Pau Espin Pedrolf37b0ad2018-04-25 18:01:27 +0200429 << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500430 if (state->mRetrans)
431 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500432 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400433 }
434
435 TN = nowTime.TN();
436 modFN = nowTime.FN() % state->fillerModulus[TN];
437
438 bursts[i] = state->fillerTable[modFN][TN];
439 zeros[i] = state->chanType[TN] == NONE;
440
441 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500442 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500443
444 if (state->mRetrans) {
445 updateFillerTable(i, burst);
446 } else {
447 burst->setVector(NULL);
448 filler[i] = false;
449 }
450
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500451 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400452 }
dburgessb3a0ca42011-10-12 07:44:40 +0000453 }
454
Thomas Tsou204a9f12013-10-29 18:34:16 -0400455 mRadioInterface->driveTransmitRadio(bursts, zeros);
456
Thomas Tsou15d743e2014-01-25 02:34:03 -0500457 for (size_t i = 0; i < mChans; i++) {
458 if (!filler[i])
459 delete bursts[i];
460 }
dburgessb3a0ca42011-10-12 07:44:40 +0000461}
462
Thomas Tsou204a9f12013-10-29 18:34:16 -0400463void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000464{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400465 TransceiverState *state = &mStates[chan];
466
467 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000468 case NONE:
469 case I:
470 case II:
471 case III:
472 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400473 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000474 break;
475 case IV:
476 case VI:
477 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400478 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000479 break;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200480 //case V:
dburgessb3a0ca42011-10-12 07:44:40 +0000481 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400482 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000483 break;
ttsoufc40a842013-06-09 22:38:18 +0000484 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400485 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000486 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000487 default:
488 break;
489 }
490}
491
492
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700493CorrType Transceiver::expectedCorrType(GSM::Time currTime,
494 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000495{
Alexander Chemeris5a068062015-06-20 01:38:47 +0300496 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 };
497 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,
498 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 };
499 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,
500 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 -0400501 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000502 unsigned burstTN = currTime.TN();
503 unsigned burstFN = currTime.FN();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300504 int subch;
dburgessb3a0ca42011-10-12 07:44:40 +0000505
Thomas Tsou204a9f12013-10-29 18:34:16 -0400506 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000507 case NONE:
508 return OFF;
509 break;
510 case FILL:
511 return IDLE;
512 break;
513 case I:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300514 // TODO: Are we expecting RACH on an IDLE frame?
515/* if (burstFN % 26 == 25)
516 return IDLE;*/
517 if (mHandover[burstTN][0])
518 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000519 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000520 break;
521 case II:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300522 subch = tchh_subslot[burstFN % 26];
523 if (subch == 1)
524 return IDLE;
525 if (mHandover[burstTN][0])
526 return RACH;
ttsou20642972013-03-27 22:00:25 +0000527 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000528 break;
529 case III:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300530 subch = tchh_subslot[burstFN % 26];
531 if (mHandover[burstTN][subch])
532 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000533 return TSC;
534 break;
535 case IV:
536 case VI:
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200537 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000538 break;
539 case V: {
540 int mod51 = burstFN % 51;
541 if ((mod51 <= 36) && (mod51 >= 14))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200542 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000543 else if ((mod51 == 4) || (mod51 == 5))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200544 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000545 else if ((mod51 == 45) || (mod51 == 46))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200546 return mExtRACH ? EXT_RACH : RACH;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300547 else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
548 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000549 else
550 return TSC;
551 break;
552 }
553 case VII:
554 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
555 return IDLE;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300556 else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
557 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000558 else
559 return TSC;
560 break;
ttsoufc40a842013-06-09 22:38:18 +0000561 case XIII: {
562 int mod52 = burstFN % 52;
563 if ((mod52 == 12) || (mod52 == 38))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200564 return mExtRACH ? EXT_RACH : RACH;
ttsoufc40a842013-06-09 22:38:18 +0000565 else if ((mod52 == 25) || (mod52 == 51))
566 return IDLE;
Pau Espin Pedrolbfc1d0b2019-08-26 16:34:45 +0200567 else /* Enable 8-PSK burst detection if EDGE is enabled */
568 return mEdge ? EDGE : TSC;
ttsoufc40a842013-06-09 22:38:18 +0000569 break;
570 }
dburgessb3a0ca42011-10-12 07:44:40 +0000571 case LOOPBACK:
572 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
573 return IDLE;
574 else
575 return TSC;
576 break;
577 default:
578 return OFF;
579 break;
580 }
dburgessb3a0ca42011-10-12 07:44:40 +0000581}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400582
Alexander Chemerise692ce92015-06-12 00:15:31 -0400583void writeToFile(radioVector *radio_burst, size_t chan)
584{
585 GSM::Time time = radio_burst->getTime();
586 std::ostringstream fname;
587 fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
588 std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
589 outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
590 outfile.close();
591}
592
Thomas Tsou30421a72013-11-13 23:14:48 -0500593/*
594 * Pull bursts from the FIFO and handle according to the slot
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200595 * and burst correlation type. Equalzation is currently disabled.
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200596 * returns 0 on success (bi filled), negative on error (bi content undefined):
597 * -ENOENT: timeslot is off (fn and tn in bi are filled),
598 * -EIO: read error
Thomas Tsou30421a72013-11-13 23:14:48 -0500599 */
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200600int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
dburgessb3a0ca42011-10-12 07:44:40 +0000601{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800602 int rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200603 struct estim_burst_params ebp;
604 float max = -1.0, avg = 0.0;
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200605 unsigned max_toa;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500606 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500607 signalVector *burst;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200608 GSM::Time burstTime;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200609 SoftVector *rxBurst;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500610 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000611
Thomas Tsou30421a72013-11-13 23:14:48 -0500612 /* Blocking FIFO read */
613 radioVector *radio_burst = mReceiveFIFO[chan]->read();
Pau Espin Pedrol94c52412019-09-06 13:48:35 +0200614 if (!radio_burst) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100615 LOGCHAN(chan, DTRXDUL, ERROR) << "ReceiveFIFO->read() returned no burst";
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200616 return -EIO;
Pau Espin Pedrol94c52412019-09-06 13:48:35 +0200617 }
dburgessb3a0ca42011-10-12 07:44:40 +0000618
Thomas Tsou30421a72013-11-13 23:14:48 -0500619 /* Set time and determine correlation type */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200620 burstTime = radio_burst->getTime();
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200621 CorrType type = expectedCorrType(burstTime, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000622
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200623 /* Initialize struct bi */
624 bi->nbits = 0;
625 bi->fn = burstTime.FN();
626 bi->tn = burstTime.TN();
627 bi->rssi = 0.0;
628 bi->toa = 0.0;
629 bi->noise = 0.0;
630 bi->idle = false;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200631 bi->modulation = MODULATION_GMSK;
632 bi->tss = 0; /* TODO: we only support tss 0 right now */
633 bi->tsc = 0;
634 bi->ci = 0.0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200635
Pau Espin Pedrol0d56d752019-09-09 10:20:58 +0200636 /* Debug: dump bursts to disk */
637 /* bits 0-7 - chan 0 timeslots
638 * bits 8-15 - chan 1 timeslots */
639 if (mWriteBurstToDiskMask & ((1<<bi->tn) << (8*chan)))
640 writeToFile(radio_burst, chan);
641
642 /* No processing if the timeslot is off.
643 * Not even power level or noise calculation. */
644 if (type == OFF) {
645 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200646 return -ENOENT;
Pau Espin Pedrol0d56d752019-09-09 10:20:58 +0200647 }
648
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500649 /* Select the diversity channel with highest energy */
650 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300651 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500652 if (pow > max) {
653 max = pow;
654 max_i = i;
655 }
656 avg += pow;
657 }
658
659 if (max_i < 0) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100660 LOGCHAN(chan, DTRXDUL, FATAL) << "Received empty burst";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200661 goto ret_idle;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500662 }
663
Thomas Tsou30421a72013-11-13 23:14:48 -0500664 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500665 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500666 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400667
Alexander Chemeris2b542102015-06-08 22:46:38 -0400668 if (type == IDLE) {
669 /* Update noise levels */
670 state->mNoises.insert(avg);
671 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200672 }
Alexander Chemeris2b542102015-06-08 22:46:38 -0400673
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200674 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
675 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
676
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200677 if (type == IDLE)
678 goto ret_idle;
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400679
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200680 max_toa = (type == RACH || type == EXT_RACH) ?
681 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200682
Thomas Tsou30421a72013-11-13 23:14:48 -0500683 /* Detect normal or RACH bursts */
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200684 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, max_toa, &ebp);
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200685 if (rc <= 0) {
686 if (rc == -SIGERR_CLIP)
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100687 LOGCHAN(chan, DTRXDUL, NOTICE) << "Clipping detected on received RACH or Normal Burst";
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200688 else if (rc != SIGERR_NONE)
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100689 LOGCHAN(chan, DTRXDUL, NOTICE) << "Unhandled RACH or Normal Burst detection error";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200690 goto ret_idle;
dburgessb3a0ca42011-10-12 07:44:40 +0000691 }
dburgessb3a0ca42011-10-12 07:44:40 +0000692
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200693 type = (CorrType) rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200694 bi->toa = ebp.toa;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200695 bi->tsc = ebp.tsc;
696 bi->ci = ebp.ci;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200697 rxBurst = demodAnyBurst(*burst, mSPSRx, ebp.amp, ebp.toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500698
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200699 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200700 if (rxBurst->size() == EDGE_BURST_NBITS) {
701 bi->modulation = MODULATION_8PSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200702 bi->nbits = EDGE_BURST_NBITS;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200703 } else { /* size() here is actually gSlotLen + 8, due to guard periods */
704 bi->modulation = MODULATION_GMSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200705 bi->nbits = gSlotLen;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200706 }
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200707
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200708 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200709 vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200710
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200711 delete rxBurst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500712 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200713 return 0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200714
715ret_idle:
716 bi->idle = true;
717 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200718 return 0;
dburgessb3a0ca42011-10-12 07:44:40 +0000719}
720
dburgessb3a0ca42011-10-12 07:44:40 +0000721void Transceiver::reset()
722{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400723 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
724 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000725}
726
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200727
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700728/**
729 * Matches a buffer with a command.
730 * @param buf a buffer to look command in
731 * @param cmd a command to look in buffer
732 * @param params pointer to arguments, or NULL
733 * @return true if command matches, otherwise false
734 */
735static bool match_cmd(char *buf,
736 const char *cmd, char **params)
737{
738 size_t cmd_len = strlen(cmd);
739
740 /* Check a command itself */
741 if (strncmp(buf, cmd, cmd_len))
742 return false;
743
744 /* A command has arguments */
745 if (params != NULL) {
746 /* Make sure there is a space */
747 if (buf[cmd_len] != ' ')
748 return false;
749
750 /* Update external pointer */
751 *params = buf + cmd_len + 1;
752 }
753
754 return true;
755}
756
Erica7143bf2020-04-08 01:39:17 +0200757void Transceiver::ctrl_sock_send(ctrl_msg& m, int chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000758{
Erica7143bf2020-04-08 01:39:17 +0200759 ctrl_sock_state& s = mCtrlSockets[chan];
760 struct osmo_fd *conn_bfd = &s.conn_bfd;
761
762 s.txmsgqueue.push_back(m);
763 conn_bfd->when |= OSMO_FD_WRITE;
764}
765
766int Transceiver::ctrl_sock_write(int chan)
767{
768 int rc;
769 ctrl_sock_state& s = mCtrlSockets[chan];
770
771 if (s.conn_bfd.fd < 0) {
772 return -EIO;
773 }
774
775 while (s.txmsgqueue.size()) {
776 const ctrl_msg m = s.txmsgqueue.front();
777
778 s.conn_bfd.when &= ~OSMO_FD_WRITE;
779
780 /* try to send it over the socket */
781 rc = write(s.conn_bfd.fd, m.data, strlen(m.data) + 1);
782 if (rc == 0)
783 goto close;
784 if (rc < 0) {
785 if (errno == EAGAIN) {
786 s.conn_bfd.when |= OSMO_FD_WRITE;
787 break;
788 }
789 goto close;
790 }
791
792 s.txmsgqueue.pop_front();
793 }
794 return 0;
795
796close:
797 LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets write(" << s.conn_bfd.fd << ") failed: " << rc;
798 return -1;
799}
800
801int Transceiver::ctrl_sock_handle_rx(int chan)
802{
803 ctrl_msg cmd_received;
804 ctrl_msg cmd_to_send;
805 char *buffer = cmd_received.data;
806 char *response = cmd_to_send.data;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700807 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700808 int msgLen;
Erica7143bf2020-04-08 01:39:17 +0200809 ctrl_sock_state& s = mCtrlSockets[chan];
Thomas Tsoud647ec52013-10-29 15:17:34 -0400810
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700811 /* Attempt to read from control socket */
Erica7143bf2020-04-08 01:39:17 +0200812 msgLen = read(s.conn_bfd.fd, buffer, sizeof(cmd_received.data)-1);
813 if (msgLen < 0 && errno == EAGAIN)
814 return 0; /* Try again later */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200815 if (msgLen <= 0) {
Erica7143bf2020-04-08 01:39:17 +0200816 LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets read(" << s.conn_bfd.fd << ") failed: " << msgLen;
817 return -EIO;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200818 }
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700819
Erica7143bf2020-04-08 01:39:17 +0200820
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700821 /* Zero-terminate received string */
822 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000823
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700824 /* Verify a command signature */
825 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100826 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
Erica7143bf2020-04-08 01:39:17 +0200827 return -EIO;
dburgessb3a0ca42011-10-12 07:44:40 +0000828 }
dburgessb3a0ca42011-10-12 07:44:40 +0000829
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700830 /* Set command pointer */
831 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200832 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700833
834 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800835 stop();
836 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700837 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700838 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000839 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700840 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000841 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300842 for (int i = 0; i < 8; i++) {
843 for (int j = 0; j < 8; j++)
844 mHandover[i][j] = false;
845 }
Tom Tsou365bc382016-10-19 15:26:04 -0700846 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700847 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700848 unsigned ts = 0, ss = 0;
849 sscanf(params, "%u %u", &ts, &ss);
850 if (ts > 7 || ss > 7) {
Pau Espin Pedrol0fafe032019-11-27 13:17:59 +0100851 sprintf(response, "RSP HANDOVER 1 %u %u", ts, ss);
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700852 } else {
853 mHandover[ts][ss] = true;
854 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
855 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700856 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700857 unsigned ts = 0, ss = 0;
858 sscanf(params, "%u %u", &ts, &ss);
859 if (ts > 7 || ss > 7) {
860 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
861 } else {
862 mHandover[ts][ss] = false;
863 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
864 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700865 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000866 //set expected maximum time-of-arrival
867 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700868 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300869 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000870 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700871 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300872 //set expected maximum time-of-arrival
873 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700874 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300875 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
876 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700877 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000878 //set expected maximum time-of-arrival
879 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700880 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400881 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000882 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700883 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000884 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500885 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000886 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500887 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000888 }
889 else {
890 sprintf(response,"RSP NOISELEV 1 0");
891 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700892 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800893 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700894 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800895 power = mRadioInterface->setPowerAttenuation(power, chan);
896 mStates[chan].mPower = power;
897 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700898 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800899 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700900 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800901 power = mStates[chan].mPower + step;
902 power = mRadioInterface->setPowerAttenuation(power, chan);
903 mStates[chan].mPower = power;
904 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700905 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000906 // tune receiver
907 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700908 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500909 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400910 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100911 LOGCHAN(chan, DTRXCTRL, FATAL) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000912 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
913 }
914 else
915 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700916 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000917 // tune txmtr
918 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700919 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500920 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400921 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100922 LOGCHAN(chan, DTRXCTRL, FATAL) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000923 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
924 }
925 else
926 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700927 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000928 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500929 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700930 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700931 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500932 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700933 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100934 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000935 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400936 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000937 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700938 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700939 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000940 int corrCode;
941 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700942 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000943 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100944 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000945 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
Erica7143bf2020-04-08 01:39:17 +0200946 return 0;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200947 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400948 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
949 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000950 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200951 } else if (match_cmd(command, "SETFORMAT", &params)) {
952 // set TRXD protocol version
953 unsigned version_recv;
954 sscanf(params, "%u", &version_recv);
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200955 LOGCHAN(chan, DTRXCTRL, INFO) << "BTS requests TRXD version switch: " << version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200956 if (version_recv > TRX_DATA_FORMAT_VER) {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200957 LOGCHAN(chan, DTRXCTRL, INFO) << "rejecting TRXD version " << version_recv
958 << "in favor of " << TRX_DATA_FORMAT_VER;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200959 sprintf(response, "RSP SETFORMAT %u %u", TRX_DATA_FORMAT_VER, version_recv);
960 } else {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200961 LOGCHAN(chan, DTRXCTRL, NOTICE) << "switching to TRXD version " << version_recv;
962 mVersionTRXD[chan] = version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200963 sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
964 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700965 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Martin Hauke066fd042019-10-13 19:08:00 +0200966 // debug command! may change or disappear without notice
Alexander Chemerise692ce92015-06-12 00:15:31 -0400967 // set a mask which bursts to dump to disk
968 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700969 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400970 mWriteBurstToDiskMask = mask;
971 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700972 } else {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100973 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200974 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000975 }
976
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200977 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Erica7143bf2020-04-08 01:39:17 +0200978 transceiver->ctrl_sock_send(cmd_to_send, chan);
979 return 0;
dburgessb3a0ca42011-10-12 07:44:40 +0000980}
981
Thomas Tsou204a9f12013-10-29 18:34:16 -0400982bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000983{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200984 int msgLen;
Tom Tsoue8871082016-07-01 02:46:04 -0700985 int burstLen;
Pau Espin Pedrole4166be2019-08-26 11:21:55 +0200986 struct trxd_hdr_v01_dl *dl;
987 char buffer[sizeof(*dl) + EDGE_BURST_NBITS];
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700988 uint32_t fn;
dburgessb3a0ca42011-10-12 07:44:40 +0000989
990 // check data socket
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200991 msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
992 if (msgLen <= 0) {
Eric1a26b4f2020-04-13 00:03:01 +0200993 LOGCHAN(chan, DTRXDDL, NOTICE) << "mDataSockets read(" << mDataSockets[chan] << ") failed: " << msgLen;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200994 return false;
995 }
dburgessb3a0ca42011-10-12 07:44:40 +0000996
Pau Espin Pedrole4166be2019-08-26 11:21:55 +0200997 switch (msgLen) {
998 case sizeof(*dl) + gSlotLen: /* GSM burst */
999 burstLen = gSlotLen;
1000 break;
1001 case sizeof(*dl) + EDGE_BURST_NBITS: /* EDGE burst */
1002 if (mSPSTx != 4) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001003 LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << mSPSTx;
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001004 return false;
1005 }
1006 burstLen = EDGE_BURST_NBITS;
1007 break;
1008 default:
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001009 LOGCHAN(chan, DTRXDDL, ERROR) << "badly formatted packet on GSM->TRX interface (len="<< msgLen << ")";
Tom Tsoue8871082016-07-01 02:46:04 -07001010 return false;
dburgessb3a0ca42011-10-12 07:44:40 +00001011 }
1012
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001013 dl = (struct trxd_hdr_v01_dl *) buffer;
dburgessb3a0ca42011-10-12 07:44:40 +00001014
Vadim Yanitskiyb3123252019-07-15 23:53:08 +07001015 /* Convert TDMA FN to the host endianness */
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001016 fn = osmo_load32be(&dl->common.fn);
Vadim Yanitskiyb3123252019-07-15 23:53:08 +07001017
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001018 /* Make sure we support the received header format */
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001019 switch (dl->common.version) {
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001020 case 0:
1021 /* Version 1 has the same format */
1022 case 1:
1023 break;
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001024 default:
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001025 LOGCHAN(chan, DTRXDDL, ERROR) << "Rx TRXD message with unknown header version " << unsigned(dl->common.version);
Vadim Yanitskiydd571c62019-07-15 23:56:56 +07001026 return false;
1027 }
1028
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001029 LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version)
1030 << "): fn=" << fn << ", tn=" << unsigned(dl->common.tn) << ", burst_len=" << burstLen;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001031
Tom Tsou7c741ec2016-07-19 11:20:59 -07001032 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +00001033 BitVector::iterator itr = newBurst.begin();
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001034 uint8_t *bufferItr = dl->soft_bits;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001035 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +00001036 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001037
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001038 GSM::Time currTime = GSM::Time(fn, dl->common.tn);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001039
Pau Espin Pedrole4166be2019-08-26 11:21:55 +02001040 addRadioVector(chan, newBurst, dl->tx_att, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +00001041
1042 return true;
dburgessb3a0ca42011-10-12 07:44:40 +00001043}
dburgessb3a0ca42011-10-12 07:44:40 +00001044
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001045bool Transceiver::driveReceiveRadio()
Thomas Tsou204a9f12013-10-29 18:34:16 -04001046{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +02001047 int rc = mRadioInterface->driveReceiveRadio();
1048 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001049 usleep(100000);
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001050 return true;
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -04001051 }
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001052 if (rc < 0)
1053 return false;
1054
1055 if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
1056 mForceClockInterface = false;
1057 return writeClockInterface();
1058 }
1059 return true;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001060}
1061
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001062void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001063{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001064 std::ostringstream os;
1065 for (size_t i=0; i < bi->nbits; i++) {
1066 if (bi->rx_burst[i] > 0.5) os << "1";
1067 else if (bi->rx_burst[i] > 0.25) os << "|";
1068 else if (bi->rx_burst[i] > 0.0) os << "'";
1069 else os << "-";
1070 }
1071
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001072 LOGCHAN(chan, DTRXDUL, DEBUG) << std::fixed << std::right
Pau Espin Pedrol1fba1042019-09-06 14:22:31 +02001073 << " time: " << unsigned(bi->tn) << ":" << bi->fn
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001074 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
1075 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
1076 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
1077 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001078 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001079 << " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB"
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001080 << " bits: " << os;
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001081}
1082
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001083bool Transceiver::driveReceiveFIFO(size_t chan)
Thomas Tsou204a9f12013-10-29 18:34:16 -04001084{
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001085 struct trx_ul_burst_ind bi;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001086 int rc;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001087
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001088 if ((rc = pullRadioVector(chan, &bi)) < 0) {
1089 if (rc == -ENOENT) { /* timeslot off, continue processing */
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001090 LOGCHAN(chan, DTRXDUL, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " timeslot is off";
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001091 return true;
1092 }
1093 return false; /* other errors: we want to stop the process */
1094 }
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001095
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001096 if (!bi.idle)
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001097 logRxBurst(chan, &bi);
dburgessb3a0ca42011-10-12 07:44:40 +00001098
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001099 switch (mVersionTRXD[chan]) {
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001100 case 0:
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001101 return trxd_send_burst_ind_v0(chan, mDataSockets[chan], &bi);
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001102 case 1:
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001103 return trxd_send_burst_ind_v1(chan, mDataSockets[chan], &bi);
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001104 default:
1105 OSMO_ASSERT(false);
1106 }
dburgessb3a0ca42011-10-12 07:44:40 +00001107}
1108
Thomas Tsou204a9f12013-10-29 18:34:16 -04001109void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +00001110{
1111
1112 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001113 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +00001114 assure that transmit packets arrive at the radio/USRP
1115 before they need to be transmitted.
1116
1117 Deadline clock indicates the burst that needs to be
1118 pushed into the FIFO right NOW. If transmit queue does
1119 not have a burst, stick in filler data.
1120 */
1121
1122
1123 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001124
dburgessb3a0ca42011-10-12 07:44:40 +00001125 if (mOn) {
1126 //radioClock->wait(); // wait until clock updates
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001127 LOGC(DTRXCLK, DEBUG) << "radio clock " << radioClock->get();
dburgessb3a0ca42011-10-12 07:44:40 +00001128 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1129 // if underrun, then we're not providing bursts to radio/USRP fast
1130 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001131 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001132 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001133 // only update latency at the defined frame interval
1134 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001135 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001136 LOGC(DTRXCLK, INFO) << "new latency: " << mTransmitLatency << " (underrun "
1137 << radioClock->get() << " vs "
1138 << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001139 mLatencyUpdateTime = radioClock->get();
1140 }
1141 }
1142 else {
1143 // if underrun hasn't occurred in the last sec (216 frames) drop
1144 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001145 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001146 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1147 mTransmitLatency.decTN();
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001148 LOGC(DTRXCLK, INFO) << "reduced latency: " << mTransmitLatency;
kurtis.heimerle380af32011-11-26 03:18:55 +00001149 mLatencyUpdateTime = radioClock->get();
1150 }
1151 }
1152 }
dburgessb3a0ca42011-10-12 07:44:40 +00001153 }
dburgessb3a0ca42011-10-12 07:44:40 +00001154 // time to push burst to transmit FIFO
1155 pushRadioVector(mTransmitDeadlineClock);
1156 mTransmitDeadlineClock.incTN();
1157 }
dburgessb3a0ca42011-10-12 07:44:40 +00001158 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001159
1160 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001161}
1162
1163
1164
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001165bool Transceiver::writeClockInterface()
dburgessb3a0ca42011-10-12 07:44:40 +00001166{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001167 int msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001168 char command[50];
1169 // FIXME -- This should be adaptive.
1170 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1171
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001172 LOGC(DTRXCLK, INFO) << "sending " << command;
dburgessb3a0ca42011-10-12 07:44:40 +00001173
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001174 msgLen = write(mClockSocket, command, strlen(command) + 1);
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001175 if (msgLen <= 0) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001176 LOGC(DTRXCLK, ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001177 return false;
1178 }
dburgessb3a0ca42011-10-12 07:44:40 +00001179
1180 mLastClockUpdateTime = mTransmitDeadlineClock;
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001181 return true;
Thomas Tsou92c16df2013-09-28 18:04:19 -04001182}
dburgessb3a0ca42011-10-12 07:44:40 +00001183
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001184void *RxUpperLoopAdapter(TrxChanThParams *params)
Thomas Tsou204a9f12013-10-29 18:34:16 -04001185{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001186 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001187 Transceiver *trx = params->trx;
1188 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001189
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001190 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001191
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001192 snprintf(thread_name, 16, "RxUpper%zu", num);
1193 set_selfthread_name(thread_name);
1194
Thomas Tsou204a9f12013-10-29 18:34:16 -04001195 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001196 if (!trx->driveReceiveFIFO(num)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001197 LOGCHAN(num, DTRXDUL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001198 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1199 break;
1200 }
Thomas Tsou204a9f12013-10-29 18:34:16 -04001201 pthread_testcancel();
1202 }
1203 return NULL;
1204}
1205
1206void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001207{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001208 set_selfthread_name("RxLower");
1209
dburgessb3a0ca42011-10-12 07:44:40 +00001210 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001211 if (!transceiver->driveReceiveRadio()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001212 LOGC(DTRXDUL, FATAL) << "Something went wrong in thread RxLower, requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001213 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1214 break;
1215 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001216 pthread_testcancel();
1217 }
1218 return NULL;
1219}
1220
Thomas Tsou204a9f12013-10-29 18:34:16 -04001221void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001222{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001223 set_selfthread_name("TxLower");
1224
Thomas Tsou92c16df2013-09-28 18:04:19 -04001225 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001226 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001227 pthread_testcancel();
1228 }
1229 return NULL;
1230}
1231
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001232void *TxUpperLoopAdapter(TrxChanThParams *params)
dburgessb3a0ca42011-10-12 07:44:40 +00001233{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001234 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001235 Transceiver *trx = params->trx;
1236 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001237
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001238 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001239
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001240 snprintf(thread_name, 16, "TxUpper%zu", num);
1241 set_selfthread_name(thread_name);
1242
dburgessb3a0ca42011-10-12 07:44:40 +00001243 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001244 if (!trx->driveTxPriorityQueue(num)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001245 LOGCHAN(num, DTRXDDL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001246 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1247 break;
1248 }
dburgessb3a0ca42011-10-12 07:44:40 +00001249 pthread_testcancel();
1250 }
1251 return NULL;
1252}