blob: dc218d78ed2319dc44b700d353b43e3371f1e865 [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
kurtis.heimerlec842de2012-11-23 08:37:32 +000048#define USB_LATENCY_INTRVL 10,0
ttsou2173abf2012-08-08 00:51:31 +000049
Thomas Tsoufa3a7872013-10-17 21:23:34 -040050/* Number of running values use in noise average */
51#define NOISE_CNT 20
ttsoue8dde022012-12-06 15:43:55 +000052
Thomas Tsouf0782732013-10-29 15:55:47 -040053TransceiverState::TransceiverState()
Tom Tsoua4d1a412014-11-25 15:46:56 -080054 : mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
Thomas Tsouf0782732013-10-29 15:55:47 -040055{
56 for (int i = 0; i < 8; i++) {
57 chanType[i] = Transceiver::NONE;
58 fillerModulus[i] = 26;
59 chanResponse[i] = NULL;
60 DFEForward[i] = NULL;
61 DFEFeedback[i] = NULL;
62
63 for (int n = 0; n < 102; n++)
64 fillerTable[n][i] = NULL;
65 }
66}
67
68TransceiverState::~TransceiverState()
69{
70 for (int i = 0; i < 8; i++) {
71 delete chanResponse[i];
72 delete DFEForward[i];
73 delete DFEFeedback[i];
74
75 for (int n = 0; n < 102; n++)
76 delete fillerTable[n][i];
77 }
78}
79
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010080bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
Tom Tsou64ad7122015-05-19 18:26:31 -070081{
Tom Tsou64ad7122015-05-19 18:26:31 -070082 signalVector *burst;
83
84 if ((sps != 1) && (sps != 4))
85 return false;
86
87 for (size_t n = 0; n < 8; n++) {
Tom Tsou64ad7122015-05-19 18:26:31 -070088 for (size_t i = 0; i < 102; i++) {
89 switch (filler) {
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010090 case FILLER_DUMMY:
Tom Tsou8ee2f382016-03-06 20:57:34 -080091 burst = generateDummyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070092 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010093 case FILLER_NORM_RAND:
Tom Tsou8ee2f382016-03-06 20:57:34 -080094 burst = genRandNormalBurst(rtsc, sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070095 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010096 case FILLER_EDGE_RAND:
Tom Tsouaf717b22016-03-06 22:19:15 -080097 burst = generateEdgeBurst(rtsc);
98 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010099 case FILLER_ACCESS_RAND:
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300100 burst = genRandAccessBurst(rach_delay, sps, n);
Alexander Chemeris5efe0502016-03-23 17:06:32 +0300101 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100102 case FILLER_ZERO:
Tom Tsou64ad7122015-05-19 18:26:31 -0700103 default:
Tom Tsou8ee2f382016-03-06 20:57:34 -0800104 burst = generateEmptyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -0700105 }
106
107 scaleVector(*burst, scale);
108 fillerTable[i][n] = burst;
109 }
110
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100111 if ((filler == FILLER_NORM_RAND) ||
112 (filler == FILLER_EDGE_RAND)) {
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700113 chanType[n] = TSC;
Tom Tsouaf717b22016-03-06 22:19:15 -0800114 }
Thomas Tsou15d743e2014-01-25 02:34:03 -0500115 }
Tom Tsou64ad7122015-05-19 18:26:31 -0700116
117 return false;
Thomas Tsouf0782732013-10-29 15:55:47 -0400118}
119
dburgessb3a0ca42011-10-12 07:44:40 +0000120Transceiver::Transceiver(int wBasePort,
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200121 const char *TRXAddress,
122 const char *GSMcoreAddress,
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800123 size_t tx_sps, size_t rx_sps, size_t chans,
Alexander Chemerise8905a02015-06-03 23:47:56 -0400124 GSM::Time wTransmitLatency,
125 RadioInterface *wRadioInterface,
Eric Wildac0487e2019-06-17 13:02:44 +0200126 double wRssiOffset, int wStackSize)
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200127 : mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200128 mClockSocket(-1), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
Eric Wildac0487e2019-06-17 13:02:44 +0200129 rssiOffset(wRssiOffset), stackSize(wStackSize),
Pau Espin Pedrol758381b2019-07-16 21:55:03 +0200130 mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mExtRACH(false), mEdge(false),
131 mOn(false), mForceClockInterface(false),
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300132 mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200133 mWriteBurstToDiskMask(0)
dburgessb3a0ca42011-10-12 07:44:40 +0000134{
dburgessb3a0ca42011-10-12 07:44:40 +0000135 txFullScale = mRadioInterface->fullScaleInputValue();
136 rxFullScale = mRadioInterface->fullScaleOutputValue();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300137
138 for (int i = 0; i < 8; i++) {
139 for (int j = 0; j < 8; j++)
140 mHandover[i][j] = false;
141 }
dburgessb3a0ca42011-10-12 07:44:40 +0000142}
143
144Transceiver::~Transceiver()
145{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800146 stop();
147
dburgessb3a0ca42011-10-12 07:44:40 +0000148 sigProcLibDestroy();
Thomas Tsoud647ec52013-10-29 15:17:34 -0400149
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200150 if (mClockSocket >= 0)
151 close(mClockSocket);
152
Thomas Tsou204a9f12013-10-29 18:34:16 -0400153 for (size_t i = 0; i < mChans; i++) {
Pau Espin Pedrolca6a78e2019-08-26 10:50:16 +0200154 if (mControlServiceLoopThreads[i]) {
155 mControlServiceLoopThreads[i]->cancel();
156 mControlServiceLoopThreads[i]->join();
157 delete mControlServiceLoopThreads[i];
158 }
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800159
Thomas Tsou204a9f12013-10-29 18:34:16 -0400160 mTxPriorityQueues[i].clear();
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200161 if (mCtrlSockets[i] >= 0)
162 close(mCtrlSockets[i]);
163 if (mDataSockets[i] >= 0)
164 close(mDataSockets[i]);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400165 }
dburgessb3a0ca42011-10-12 07:44:40 +0000166}
Thomas Tsou83e06892013-08-20 16:10:01 -0400167
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800168/*
169 * Initialize transceiver
170 *
171 * Start or restart the control loop. Any further control is handled through the
172 * socket API. Randomize the central radio clock set the downlink burst
173 * counters. Note that the clock will not update until the radio starts, but we
174 * are still expected to report clock indications through control channel
175 * activity.
176 */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200177bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
178 bool edge, bool ext_rach)
Thomas Tsou83e06892013-08-20 16:10:01 -0400179{
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500180 int d_srcport, d_dstport, c_srcport, c_dstport;
Thomas Tsouf0782732013-10-29 15:55:47 -0400181
Thomas Tsou204a9f12013-10-29 18:34:16 -0400182 if (!mChans) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100183 LOG(FATAL) << "No channels assigned";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400184 return false;
185 }
186
Tom Tsou2079a3c2016-03-06 00:58:56 -0800187 if (!sigProcLibSetup()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100188 LOG(FATAL) << "Failed to initialize signal processing library";
Thomas Tsou83e06892013-08-20 16:10:01 -0400189 return false;
190 }
191
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200192 mExtRACH = ext_rach;
Tom Tsou64464e62016-07-01 03:46:46 -0700193 mEdge = edge;
194
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200195 mDataSockets.resize(mChans, -1);
196 mCtrlSockets.resize(mChans, -1);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400197 mControlServiceLoopThreads.resize(mChans);
198 mTxPriorityQueueServiceLoopThreads.resize(mChans);
199 mRxServiceLoopThreads.resize(mChans);
200
201 mTxPriorityQueues.resize(mChans);
202 mReceiveFIFO.resize(mChans);
203 mStates.resize(mChans);
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200204 mVersionTRXD.resize(mChans);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400205
Thomas Tsouccb73e12014-04-15 17:41:28 -0400206 /* Filler table retransmissions - support only on channel 0 */
Tom Tsou64ad7122015-05-19 18:26:31 -0700207 if (filler == FILLER_DUMMY)
Thomas Tsouccb73e12014-04-15 17:41:28 -0400208 mStates[0].mRetrans = true;
209
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800210 /* Setup sockets */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200211 mClockSocket = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
212 mLocalAddr.c_str(), mBasePort,
213 mRemoteAddr.c_str(), mBasePort + 100,
214 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
Pau Espin Pedrole7195ac2019-08-26 10:55:04 +0200215 if (mClockSocket < 0)
216 return false;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200217
Thomas Tsou204a9f12013-10-29 18:34:16 -0400218 for (size_t i = 0; i < mChans; i++) {
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500219 c_srcport = mBasePort + 2 * i + 1;
220 c_dstport = mBasePort + 2 * i + 101;
221 d_srcport = mBasePort + 2 * i + 2;
222 d_dstport = mBasePort + 2 * i + 102;
223
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200224 mCtrlSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
225 mLocalAddr.c_str(), c_srcport,
226 mRemoteAddr.c_str(), c_dstport,
227 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
228 if (mCtrlSockets[i] < 0)
229 return false;
230
231 mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
232 mLocalAddr.c_str(), d_srcport,
233 mRemoteAddr.c_str(), d_dstport,
234 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
235 if (mCtrlSockets[i] < 0)
236 return false;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400237 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400238
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800239 /* Randomize the central clock */
240 GSM::Time startTime(random() % gHyperframe, 0);
241 mRadioInterface->getClock()->set(startTime);
242 mTransmitDeadlineClock = startTime;
243 mLastClockUpdateTime = startTime;
244 mLatencyUpdateTime = startTime;
245
246 /* Start control threads */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400247 for (size_t i = 0; i < mChans; i++) {
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200248 TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
249 params->trx = this;
250 params->num = i;
Eric Wildac0487e2019-06-17 13:02:44 +0200251 mControlServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800252 mControlServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200253 ControlServiceLoopAdapter, (void*) params);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400254
Tom Tsou64ad7122015-05-19 18:26:31 -0700255 if (i && filler == FILLER_DUMMY)
256 filler = FILLER_ZERO;
257
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300258 mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
Thomas Tsou83e06892013-08-20 16:10:01 -0400259 }
260
261 return true;
262}
dburgessb3a0ca42011-10-12 07:44:40 +0000263
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800264/*
265 * Start the transceiver
266 *
267 * Submit command(s) to the radio device to commence streaming samples and
268 * launch threads to handle sample I/O. Re-synchronize the transmit burst
269 * counters to the central radio clock here as well.
270 */
271bool Transceiver::start()
272{
273 ScopedLock lock(mLock);
274
275 if (mOn) {
276 LOG(ERR) << "Transceiver already running";
Ivan Kluchnikov194a9b12015-04-23 17:08:27 +0300277 return true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800278 }
279
280 LOG(NOTICE) << "Starting the transceiver";
281
282 GSM::Time time = mRadioInterface->getClock()->get();
283 mTransmitDeadlineClock = time;
284 mLastClockUpdateTime = time;
285 mLatencyUpdateTime = time;
286
287 if (!mRadioInterface->start()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100288 LOG(FATAL) << "Device failed to start";
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800289 return false;
290 }
291
292 /* Device is running - launch I/O threads */
Eric Wildac0487e2019-06-17 13:02:44 +0200293 mRxLowerLoopThread = new Thread(stackSize);
294 mTxLowerLoopThread = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800295 mTxLowerLoopThread->start((void * (*)(void*))
296 TxLowerLoopAdapter,(void*) this);
297 mRxLowerLoopThread->start((void * (*)(void*))
298 RxLowerLoopAdapter,(void*) this);
299
300 /* Launch uplink and downlink burst processing threads */
301 for (size_t i = 0; i < mChans; i++) {
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200302 TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
303 params->trx = this;
304 params->num = i;
Eric Wildac0487e2019-06-17 13:02:44 +0200305 mRxServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800306 mRxServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200307 RxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800308
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200309 params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
310 params->trx = this;
311 params->num = i;
Eric Wildac0487e2019-06-17 13:02:44 +0200312 mTxPriorityQueueServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800313 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200314 TxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800315 }
316
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200317 mForceClockInterface = true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800318 mOn = true;
319 return true;
320}
321
322/*
323 * Stop the transceiver
324 *
325 * Perform stopping by disabling receive streaming and issuing cancellation
326 * requests to running threads. Most threads will timeout and terminate once
327 * device is disabled, but the transmit loop may block waiting on the central
328 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
329 * makes it to the thread cancellation point.
330 */
331void Transceiver::stop()
332{
333 ScopedLock lock(mLock);
334
335 if (!mOn)
336 return;
337
338 LOG(NOTICE) << "Stopping the transceiver";
339 mTxLowerLoopThread->cancel();
340 mRxLowerLoopThread->cancel();
Tom Tsoud67bd602017-06-15 15:35:02 -0700341 mTxLowerLoopThread->join();
342 mRxLowerLoopThread->join();
343 delete mTxLowerLoopThread;
344 delete mRxLowerLoopThread;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800345
346 for (size_t i = 0; i < mChans; i++) {
347 mRxServiceLoopThreads[i]->cancel();
348 mTxPriorityQueueServiceLoopThreads[i]->cancel();
349 }
350
351 LOG(INFO) << "Stopping the device";
352 mRadioInterface->stop();
353
354 for (size_t i = 0; i < mChans; i++) {
355 mRxServiceLoopThreads[i]->join();
356 mTxPriorityQueueServiceLoopThreads[i]->join();
357 delete mRxServiceLoopThreads[i];
358 delete mTxPriorityQueueServiceLoopThreads[i];
359
360 mTxPriorityQueues[i].clear();
361 }
362
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800363 mOn = false;
364 LOG(NOTICE) << "Transceiver stopped";
365}
366
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500367void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400368 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000369{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500370 signalVector *burst;
371 radioVector *radio_burst;
372
Thomas Tsou204a9f12013-10-29 18:34:16 -0400373 if (chan >= mTxPriorityQueues.size()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100374 LOGCHAN(chan, DTRXDDL, FATAL) << "Invalid channel";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400375 return;
376 }
377
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500378 if (wTime.TN() > 7) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100379 LOGCHAN(chan, DTRXDDL, FATAL) << "Received burst with invalid slot " << wTime.TN();
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500380 return;
381 }
382
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800383 /* Use the number of bits as the EDGE burst indicator */
384 if (bits.size() == EDGE_BURST_NBITS)
385 burst = modulateEdgeBurst(bits, mSPSTx);
386 else
387 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
388
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500389 scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
dburgessb3a0ca42011-10-12 07:44:40 +0000390
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500391 radio_burst = new radioVector(wTime, burst);
392
393 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000394}
395
Thomas Tsou15d743e2014-01-25 02:34:03 -0500396void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
397{
398 int TN, modFN;
399 TransceiverState *state = &mStates[chan];
400
401 TN = burst->getTime().TN();
402 modFN = burst->getTime().FN() % state->fillerModulus[TN];
403
404 delete state->fillerTable[modFN][TN];
405 state->fillerTable[modFN][TN] = burst->getVector();
406 burst->setVector(NULL);
407}
408
dburgessb3a0ca42011-10-12 07:44:40 +0000409void Transceiver::pushRadioVector(GSM::Time &nowTime)
410{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400411 int TN, modFN;
412 radioVector *burst;
413 TransceiverState *state;
414 std::vector<signalVector *> bursts(mChans);
415 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500416 std::vector<bool> filler(mChans, true);
dburgessb3a0ca42011-10-12 07:44:40 +0000417
Thomas Tsou204a9f12013-10-29 18:34:16 -0400418 for (size_t i = 0; i < mChans; i ++) {
419 state = &mStates[i];
dburgessb3a0ca42011-10-12 07:44:40 +0000420
Thomas Tsou204a9f12013-10-29 18:34:16 -0400421 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100422 LOGCHAN(i, DTRXDDL, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
Pau Espin Pedrolf37b0ad2018-04-25 18:01:27 +0200423 << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500424 if (state->mRetrans)
425 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500426 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400427 }
428
429 TN = nowTime.TN();
430 modFN = nowTime.FN() % state->fillerModulus[TN];
431
432 bursts[i] = state->fillerTable[modFN][TN];
433 zeros[i] = state->chanType[TN] == NONE;
434
435 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500436 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500437
438 if (state->mRetrans) {
439 updateFillerTable(i, burst);
440 } else {
441 burst->setVector(NULL);
442 filler[i] = false;
443 }
444
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500445 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400446 }
dburgessb3a0ca42011-10-12 07:44:40 +0000447 }
448
Thomas Tsou204a9f12013-10-29 18:34:16 -0400449 mRadioInterface->driveTransmitRadio(bursts, zeros);
450
Thomas Tsou15d743e2014-01-25 02:34:03 -0500451 for (size_t i = 0; i < mChans; i++) {
452 if (!filler[i])
453 delete bursts[i];
454 }
dburgessb3a0ca42011-10-12 07:44:40 +0000455}
456
Thomas Tsou204a9f12013-10-29 18:34:16 -0400457void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000458{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400459 TransceiverState *state = &mStates[chan];
460
461 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000462 case NONE:
463 case I:
464 case II:
465 case III:
466 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400467 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000468 break;
469 case IV:
470 case VI:
471 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400472 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000473 break;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200474 //case V:
dburgessb3a0ca42011-10-12 07:44:40 +0000475 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400476 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000477 break;
ttsoufc40a842013-06-09 22:38:18 +0000478 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400479 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000480 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000481 default:
482 break;
483 }
484}
485
486
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700487CorrType Transceiver::expectedCorrType(GSM::Time currTime,
488 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000489{
Alexander Chemeris5a068062015-06-20 01:38:47 +0300490 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 };
491 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,
492 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 };
493 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,
494 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 -0400495 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000496 unsigned burstTN = currTime.TN();
497 unsigned burstFN = currTime.FN();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300498 int subch;
dburgessb3a0ca42011-10-12 07:44:40 +0000499
Thomas Tsou204a9f12013-10-29 18:34:16 -0400500 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000501 case NONE:
502 return OFF;
503 break;
504 case FILL:
505 return IDLE;
506 break;
507 case I:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300508 // TODO: Are we expecting RACH on an IDLE frame?
509/* if (burstFN % 26 == 25)
510 return IDLE;*/
511 if (mHandover[burstTN][0])
512 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000513 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000514 break;
515 case II:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300516 subch = tchh_subslot[burstFN % 26];
517 if (subch == 1)
518 return IDLE;
519 if (mHandover[burstTN][0])
520 return RACH;
ttsou20642972013-03-27 22:00:25 +0000521 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000522 break;
523 case III:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300524 subch = tchh_subslot[burstFN % 26];
525 if (mHandover[burstTN][subch])
526 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000527 return TSC;
528 break;
529 case IV:
530 case VI:
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200531 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000532 break;
533 case V: {
534 int mod51 = burstFN % 51;
535 if ((mod51 <= 36) && (mod51 >= 14))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200536 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000537 else if ((mod51 == 4) || (mod51 == 5))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200538 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000539 else if ((mod51 == 45) || (mod51 == 46))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200540 return mExtRACH ? EXT_RACH : RACH;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300541 else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
542 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000543 else
544 return TSC;
545 break;
546 }
547 case VII:
548 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
549 return IDLE;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300550 else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
551 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000552 else
553 return TSC;
554 break;
ttsoufc40a842013-06-09 22:38:18 +0000555 case XIII: {
556 int mod52 = burstFN % 52;
557 if ((mod52 == 12) || (mod52 == 38))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200558 return mExtRACH ? EXT_RACH : RACH;
ttsoufc40a842013-06-09 22:38:18 +0000559 else if ((mod52 == 25) || (mod52 == 51))
560 return IDLE;
Pau Espin Pedrolbfc1d0b2019-08-26 16:34:45 +0200561 else /* Enable 8-PSK burst detection if EDGE is enabled */
562 return mEdge ? EDGE : TSC;
ttsoufc40a842013-06-09 22:38:18 +0000563 break;
564 }
dburgessb3a0ca42011-10-12 07:44:40 +0000565 case LOOPBACK:
566 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
567 return IDLE;
568 else
569 return TSC;
570 break;
571 default:
572 return OFF;
573 break;
574 }
dburgessb3a0ca42011-10-12 07:44:40 +0000575}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400576
Alexander Chemerise692ce92015-06-12 00:15:31 -0400577void writeToFile(radioVector *radio_burst, size_t chan)
578{
579 GSM::Time time = radio_burst->getTime();
580 std::ostringstream fname;
581 fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
582 std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
583 outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
584 outfile.close();
585}
586
Thomas Tsou30421a72013-11-13 23:14:48 -0500587/*
588 * Pull bursts from the FIFO and handle according to the slot
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200589 * and burst correlation type. Equalzation is currently disabled.
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200590 * returns 0 on success (bi filled), negative on error (bi content undefined):
591 * -ENOENT: timeslot is off (fn and tn in bi are filled),
592 * -EIO: read error
Thomas Tsou30421a72013-11-13 23:14:48 -0500593 */
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200594int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
dburgessb3a0ca42011-10-12 07:44:40 +0000595{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800596 int rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200597 struct estim_burst_params ebp;
598 float max = -1.0, avg = 0.0;
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200599 unsigned max_toa;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500600 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500601 signalVector *burst;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200602 GSM::Time burstTime;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200603 SoftVector *rxBurst;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500604 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000605
Thomas Tsou30421a72013-11-13 23:14:48 -0500606 /* Blocking FIFO read */
607 radioVector *radio_burst = mReceiveFIFO[chan]->read();
Pau Espin Pedrol94c52412019-09-06 13:48:35 +0200608 if (!radio_burst) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100609 LOGCHAN(chan, DTRXDUL, ERROR) << "ReceiveFIFO->read() returned no burst";
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200610 return -EIO;
Pau Espin Pedrol94c52412019-09-06 13:48:35 +0200611 }
dburgessb3a0ca42011-10-12 07:44:40 +0000612
Thomas Tsou30421a72013-11-13 23:14:48 -0500613 /* Set time and determine correlation type */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200614 burstTime = radio_burst->getTime();
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200615 CorrType type = expectedCorrType(burstTime, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000616
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200617 /* Initialize struct bi */
618 bi->nbits = 0;
619 bi->fn = burstTime.FN();
620 bi->tn = burstTime.TN();
621 bi->rssi = 0.0;
622 bi->toa = 0.0;
623 bi->noise = 0.0;
624 bi->idle = false;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200625 bi->modulation = MODULATION_GMSK;
626 bi->tss = 0; /* TODO: we only support tss 0 right now */
627 bi->tsc = 0;
628 bi->ci = 0.0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200629
Pau Espin Pedrol0d56d752019-09-09 10:20:58 +0200630 /* Debug: dump bursts to disk */
631 /* bits 0-7 - chan 0 timeslots
632 * bits 8-15 - chan 1 timeslots */
633 if (mWriteBurstToDiskMask & ((1<<bi->tn) << (8*chan)))
634 writeToFile(radio_burst, chan);
635
636 /* No processing if the timeslot is off.
637 * Not even power level or noise calculation. */
638 if (type == OFF) {
639 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200640 return -ENOENT;
Pau Espin Pedrol0d56d752019-09-09 10:20:58 +0200641 }
642
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500643 /* Select the diversity channel with highest energy */
644 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300645 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500646 if (pow > max) {
647 max = pow;
648 max_i = i;
649 }
650 avg += pow;
651 }
652
653 if (max_i < 0) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100654 LOGCHAN(chan, DTRXDUL, FATAL) << "Received empty burst";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200655 goto ret_idle;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500656 }
657
Thomas Tsou30421a72013-11-13 23:14:48 -0500658 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500659 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500660 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400661
Alexander Chemeris2b542102015-06-08 22:46:38 -0400662 if (type == IDLE) {
663 /* Update noise levels */
664 state->mNoises.insert(avg);
665 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200666 }
Alexander Chemeris2b542102015-06-08 22:46:38 -0400667
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200668 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
669 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
670
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200671 if (type == IDLE)
672 goto ret_idle;
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400673
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200674 max_toa = (type == RACH || type == EXT_RACH) ?
675 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200676
Thomas Tsou30421a72013-11-13 23:14:48 -0500677 /* Detect normal or RACH bursts */
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200678 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, max_toa, &ebp);
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200679 if (rc <= 0) {
680 if (rc == -SIGERR_CLIP)
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100681 LOGCHAN(chan, DTRXDUL, NOTICE) << "Clipping detected on received RACH or Normal Burst";
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200682 else if (rc != SIGERR_NONE)
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100683 LOGCHAN(chan, DTRXDUL, NOTICE) << "Unhandled RACH or Normal Burst detection error";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200684 goto ret_idle;
dburgessb3a0ca42011-10-12 07:44:40 +0000685 }
dburgessb3a0ca42011-10-12 07:44:40 +0000686
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200687 type = (CorrType) rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200688 bi->toa = ebp.toa;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200689 bi->tsc = ebp.tsc;
690 bi->ci = ebp.ci;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200691 rxBurst = demodAnyBurst(*burst, mSPSRx, ebp.amp, ebp.toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500692
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200693 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200694 if (rxBurst->size() == EDGE_BURST_NBITS) {
695 bi->modulation = MODULATION_8PSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200696 bi->nbits = EDGE_BURST_NBITS;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200697 } else { /* size() here is actually gSlotLen + 8, due to guard periods */
698 bi->modulation = MODULATION_GMSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200699 bi->nbits = gSlotLen;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200700 }
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200701
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200702 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200703 vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200704
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200705 delete rxBurst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500706 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200707 return 0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200708
709ret_idle:
710 bi->idle = true;
711 delete radio_burst;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +0200712 return 0;
dburgessb3a0ca42011-10-12 07:44:40 +0000713}
714
dburgessb3a0ca42011-10-12 07:44:40 +0000715void Transceiver::reset()
716{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400717 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
718 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000719}
720
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200721
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700722#define MAX_PACKET_LENGTH 100
723
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700724/**
725 * Matches a buffer with a command.
726 * @param buf a buffer to look command in
727 * @param cmd a command to look in buffer
728 * @param params pointer to arguments, or NULL
729 * @return true if command matches, otherwise false
730 */
731static bool match_cmd(char *buf,
732 const char *cmd, char **params)
733{
734 size_t cmd_len = strlen(cmd);
735
736 /* Check a command itself */
737 if (strncmp(buf, cmd, cmd_len))
738 return false;
739
740 /* A command has arguments */
741 if (params != NULL) {
742 /* Make sure there is a space */
743 if (buf[cmd_len] != ' ')
744 return false;
745
746 /* Update external pointer */
747 *params = buf + cmd_len + 1;
748 }
749
750 return true;
751}
752
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +0200753bool Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000754{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700755 char buffer[MAX_PACKET_LENGTH + 1];
756 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700757 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700758 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400759
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700760 /* Attempt to read from control socket */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200761 msgLen = read(mCtrlSockets[chan], buffer, MAX_PACKET_LENGTH);
762 if (msgLen <= 0) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100763 LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +0200764 return false;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200765 }
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700766
767 /* Zero-terminate received string */
768 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000769
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700770 /* Verify a command signature */
771 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100772 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +0200773 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000774 }
dburgessb3a0ca42011-10-12 07:44:40 +0000775
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700776 /* Set command pointer */
777 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200778 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700779
780 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800781 stop();
782 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700783 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700784 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000785 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700786 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000787 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300788 for (int i = 0; i < 8; i++) {
789 for (int j = 0; j < 8; j++)
790 mHandover[i][j] = false;
791 }
Tom Tsou365bc382016-10-19 15:26:04 -0700792 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700793 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700794 unsigned ts = 0, ss = 0;
795 sscanf(params, "%u %u", &ts, &ss);
796 if (ts > 7 || ss > 7) {
Pau Espin Pedrol0fafe032019-11-27 13:17:59 +0100797 sprintf(response, "RSP HANDOVER 1 %u %u", ts, ss);
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700798 } else {
799 mHandover[ts][ss] = true;
800 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
801 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700802 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700803 unsigned ts = 0, ss = 0;
804 sscanf(params, "%u %u", &ts, &ss);
805 if (ts > 7 || ss > 7) {
806 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
807 } else {
808 mHandover[ts][ss] = false;
809 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
810 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700811 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000812 //set expected maximum time-of-arrival
813 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700814 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300815 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000816 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700817 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300818 //set expected maximum time-of-arrival
819 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700820 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300821 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
822 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700823 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000824 //set expected maximum time-of-arrival
825 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700826 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400827 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000828 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700829 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000830 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500831 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000832 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500833 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000834 }
835 else {
836 sprintf(response,"RSP NOISELEV 1 0");
837 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700838 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800839 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700840 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800841 power = mRadioInterface->setPowerAttenuation(power, chan);
842 mStates[chan].mPower = power;
843 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700844 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800845 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700846 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800847 power = mStates[chan].mPower + step;
848 power = mRadioInterface->setPowerAttenuation(power, chan);
849 mStates[chan].mPower = power;
850 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700851 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000852 // tune receiver
853 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700854 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500855 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400856 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100857 LOGCHAN(chan, DTRXCTRL, FATAL) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000858 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
859 }
860 else
861 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700862 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000863 // tune txmtr
864 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700865 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500866 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400867 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100868 LOGCHAN(chan, DTRXCTRL, FATAL) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000869 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
870 }
871 else
872 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700873 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000874 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500875 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700876 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700877 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500878 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700879 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100880 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000881 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400882 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000883 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700884 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700885 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000886 int corrCode;
887 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700888 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000889 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100890 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000891 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +0200892 return true;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200893 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400894 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
895 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000896 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200897 } else if (match_cmd(command, "SETFORMAT", &params)) {
898 // set TRXD protocol version
899 unsigned version_recv;
900 sscanf(params, "%u", &version_recv);
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200901 LOGCHAN(chan, DTRXCTRL, INFO) << "BTS requests TRXD version switch: " << version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200902 if (version_recv > TRX_DATA_FORMAT_VER) {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200903 LOGCHAN(chan, DTRXCTRL, INFO) << "rejecting TRXD version " << version_recv
904 << "in favor of " << TRX_DATA_FORMAT_VER;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200905 sprintf(response, "RSP SETFORMAT %u %u", TRX_DATA_FORMAT_VER, version_recv);
906 } else {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200907 LOGCHAN(chan, DTRXCTRL, NOTICE) << "switching to TRXD version " << version_recv;
908 mVersionTRXD[chan] = version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200909 sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
910 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700911 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Martin Hauke066fd042019-10-13 19:08:00 +0200912 // debug command! may change or disappear without notice
Alexander Chemerise692ce92015-06-12 00:15:31 -0400913 // set a mask which bursts to dump to disk
914 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700915 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400916 mWriteBurstToDiskMask = mask;
917 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700918 } else {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100919 LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200920 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000921 }
922
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200923 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200924 msgLen = write(mCtrlSockets[chan], response, strlen(response) + 1);
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +0200925 if (msgLen <= 0) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100926 LOGCHAN(chan, DTRXCTRL, NOTICE) << "mCtrlSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +0200927 return false;
928 }
929 return true;
dburgessb3a0ca42011-10-12 07:44:40 +0000930}
931
Thomas Tsou204a9f12013-10-29 18:34:16 -0400932bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000933{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200934 int msgLen;
Tom Tsoue8871082016-07-01 02:46:04 -0700935 int burstLen;
Pau Espin Pedrole4166be2019-08-26 11:21:55 +0200936 struct trxd_hdr_v01_dl *dl;
937 char buffer[sizeof(*dl) + EDGE_BURST_NBITS];
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700938 uint32_t fn;
dburgessb3a0ca42011-10-12 07:44:40 +0000939
940 // check data socket
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200941 msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
942 if (msgLen <= 0) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100943 LOGCHAN(chan, DTRXDDL, NOTICE) << "mDataSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200944 return false;
945 }
dburgessb3a0ca42011-10-12 07:44:40 +0000946
Pau Espin Pedrole4166be2019-08-26 11:21:55 +0200947 switch (msgLen) {
948 case sizeof(*dl) + gSlotLen: /* GSM burst */
949 burstLen = gSlotLen;
950 break;
951 case sizeof(*dl) + EDGE_BURST_NBITS: /* EDGE burst */
952 if (mSPSTx != 4) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100953 LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << mSPSTx;
Pau Espin Pedrole4166be2019-08-26 11:21:55 +0200954 return false;
955 }
956 burstLen = EDGE_BURST_NBITS;
957 break;
958 default:
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100959 LOGCHAN(chan, DTRXDDL, ERROR) << "badly formatted packet on GSM->TRX interface (len="<< msgLen << ")";
Tom Tsoue8871082016-07-01 02:46:04 -0700960 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000961 }
962
Pau Espin Pedrole4166be2019-08-26 11:21:55 +0200963 dl = (struct trxd_hdr_v01_dl *) buffer;
dburgessb3a0ca42011-10-12 07:44:40 +0000964
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700965 /* Convert TDMA FN to the host endianness */
Pau Espin Pedrole4166be2019-08-26 11:21:55 +0200966 fn = osmo_load32be(&dl->common.fn);
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700967
Vadim Yanitskiydd571c62019-07-15 23:56:56 +0700968 /* Make sure we support the received header format */
Pau Espin Pedrole4166be2019-08-26 11:21:55 +0200969 switch (dl->common.version) {
Vadim Yanitskiydd571c62019-07-15 23:56:56 +0700970 case 0:
971 /* Version 1 has the same format */
972 case 1:
973 break;
Vadim Yanitskiydd571c62019-07-15 23:56:56 +0700974 default:
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100975 LOGCHAN(chan, DTRXDDL, ERROR) << "Rx TRXD message with unknown header version " << unsigned(dl->common.version);
Vadim Yanitskiydd571c62019-07-15 23:56:56 +0700976 return false;
977 }
978
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +0100979 LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version)
980 << "): fn=" << fn << ", tn=" << unsigned(dl->common.tn) << ", burst_len=" << burstLen;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200981
Tom Tsou7c741ec2016-07-19 11:20:59 -0700982 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000983 BitVector::iterator itr = newBurst.begin();
Pau Espin Pedrole4166be2019-08-26 11:21:55 +0200984 uint8_t *bufferItr = dl->soft_bits;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200985 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000986 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200987
Pau Espin Pedrole4166be2019-08-26 11:21:55 +0200988 GSM::Time currTime = GSM::Time(fn, dl->common.tn);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400989
Pau Espin Pedrole4166be2019-08-26 11:21:55 +0200990 addRadioVector(chan, newBurst, dl->tx_att, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000991
992 return true;
dburgessb3a0ca42011-10-12 07:44:40 +0000993}
dburgessb3a0ca42011-10-12 07:44:40 +0000994
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +0200995bool Transceiver::driveReceiveRadio()
Thomas Tsou204a9f12013-10-29 18:34:16 -0400996{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200997 int rc = mRadioInterface->driveReceiveRadio();
998 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400999 usleep(100000);
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001000 return true;
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -04001001 }
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001002 if (rc < 0)
1003 return false;
1004
1005 if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
1006 mForceClockInterface = false;
1007 return writeClockInterface();
1008 }
1009 return true;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001010}
1011
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001012void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001013{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001014 std::ostringstream os;
1015 for (size_t i=0; i < bi->nbits; i++) {
1016 if (bi->rx_burst[i] > 0.5) os << "1";
1017 else if (bi->rx_burst[i] > 0.25) os << "|";
1018 else if (bi->rx_burst[i] > 0.0) os << "'";
1019 else os << "-";
1020 }
1021
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001022 LOGCHAN(chan, DTRXDUL, DEBUG) << std::fixed << std::right
Pau Espin Pedrol1fba1042019-09-06 14:22:31 +02001023 << " time: " << unsigned(bi->tn) << ":" << bi->fn
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001024 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
1025 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
1026 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
1027 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001028 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001029 << " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB"
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001030 << " bits: " << os;
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001031}
1032
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001033bool Transceiver::driveReceiveFIFO(size_t chan)
Thomas Tsou204a9f12013-10-29 18:34:16 -04001034{
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001035 struct trx_ul_burst_ind bi;
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001036 int rc;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001037
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001038 if ((rc = pullRadioVector(chan, &bi)) < 0) {
1039 if (rc == -ENOENT) { /* timeslot off, continue processing */
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001040 LOGCHAN(chan, DTRXDUL, DEBUG) << unsigned(bi.tn) << ":" << bi.fn << " timeslot is off";
Pau Espin Pedrol923b4bc2019-09-06 15:01:26 +02001041 return true;
1042 }
1043 return false; /* other errors: we want to stop the process */
1044 }
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001045
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001046 if (!bi.idle)
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001047 logRxBurst(chan, &bi);
dburgessb3a0ca42011-10-12 07:44:40 +00001048
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001049 switch (mVersionTRXD[chan]) {
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001050 case 0:
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001051 return trxd_send_burst_ind_v0(chan, mDataSockets[chan], &bi);
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001052 case 1:
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001053 return trxd_send_burst_ind_v1(chan, mDataSockets[chan], &bi);
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001054 default:
1055 OSMO_ASSERT(false);
1056 }
dburgessb3a0ca42011-10-12 07:44:40 +00001057}
1058
Thomas Tsou204a9f12013-10-29 18:34:16 -04001059void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +00001060{
1061
1062 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001063 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +00001064 assure that transmit packets arrive at the radio/USRP
1065 before they need to be transmitted.
1066
1067 Deadline clock indicates the burst that needs to be
1068 pushed into the FIFO right NOW. If transmit queue does
1069 not have a burst, stick in filler data.
1070 */
1071
1072
1073 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001074
dburgessb3a0ca42011-10-12 07:44:40 +00001075 if (mOn) {
1076 //radioClock->wait(); // wait until clock updates
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001077 LOGC(DTRXCLK, DEBUG) << "radio clock " << radioClock->get();
dburgessb3a0ca42011-10-12 07:44:40 +00001078 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1079 // if underrun, then we're not providing bursts to radio/USRP fast
1080 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001081 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001082 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001083 // only update latency at the defined frame interval
1084 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001085 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001086 LOGC(DTRXCLK, INFO) << "new latency: " << mTransmitLatency << " (underrun "
1087 << radioClock->get() << " vs "
1088 << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001089 mLatencyUpdateTime = radioClock->get();
1090 }
1091 }
1092 else {
1093 // if underrun hasn't occurred in the last sec (216 frames) drop
1094 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001095 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001096 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1097 mTransmitLatency.decTN();
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001098 LOGC(DTRXCLK, INFO) << "reduced latency: " << mTransmitLatency;
kurtis.heimerle380af32011-11-26 03:18:55 +00001099 mLatencyUpdateTime = radioClock->get();
1100 }
1101 }
1102 }
dburgessb3a0ca42011-10-12 07:44:40 +00001103 }
dburgessb3a0ca42011-10-12 07:44:40 +00001104 // time to push burst to transmit FIFO
1105 pushRadioVector(mTransmitDeadlineClock);
1106 mTransmitDeadlineClock.incTN();
1107 }
dburgessb3a0ca42011-10-12 07:44:40 +00001108 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001109
1110 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001111}
1112
1113
1114
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001115bool Transceiver::writeClockInterface()
dburgessb3a0ca42011-10-12 07:44:40 +00001116{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001117 int msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001118 char command[50];
1119 // FIXME -- This should be adaptive.
1120 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1121
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001122 LOGC(DTRXCLK, INFO) << "sending " << command;
dburgessb3a0ca42011-10-12 07:44:40 +00001123
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001124 msgLen = write(mClockSocket, command, strlen(command) + 1);
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001125 if (msgLen <= 0) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001126 LOGC(DTRXCLK, ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001127 return false;
1128 }
dburgessb3a0ca42011-10-12 07:44:40 +00001129
1130 mLastClockUpdateTime = mTransmitDeadlineClock;
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001131 return true;
Thomas Tsou92c16df2013-09-28 18:04:19 -04001132}
dburgessb3a0ca42011-10-12 07:44:40 +00001133
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001134void *RxUpperLoopAdapter(TrxChanThParams *params)
Thomas Tsou204a9f12013-10-29 18:34:16 -04001135{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001136 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001137 Transceiver *trx = params->trx;
1138 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001139
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001140 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001141
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001142 snprintf(thread_name, 16, "RxUpper%zu", num);
1143 set_selfthread_name(thread_name);
1144
Thomas Tsou204a9f12013-10-29 18:34:16 -04001145 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001146 if (!trx->driveReceiveFIFO(num)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001147 LOGCHAN(num, DTRXDUL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001148 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1149 break;
1150 }
Thomas Tsou204a9f12013-10-29 18:34:16 -04001151 pthread_testcancel();
1152 }
1153 return NULL;
1154}
1155
1156void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001157{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001158 set_selfthread_name("RxLower");
1159
dburgessb3a0ca42011-10-12 07:44:40 +00001160 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001161 if (!transceiver->driveReceiveRadio()) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001162 LOGC(DTRXDUL, FATAL) << "Something went wrong in thread RxLower, requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001163 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1164 break;
1165 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001166 pthread_testcancel();
1167 }
1168 return NULL;
1169}
1170
Thomas Tsou204a9f12013-10-29 18:34:16 -04001171void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001172{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001173 set_selfthread_name("TxLower");
1174
Thomas Tsou92c16df2013-09-28 18:04:19 -04001175 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001176 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001177 pthread_testcancel();
1178 }
1179 return NULL;
1180}
1181
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001182void *ControlServiceLoopAdapter(TrxChanThParams *params)
dburgessb3a0ca42011-10-12 07:44:40 +00001183{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001184 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001185 Transceiver *trx = params->trx;
1186 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001187
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001188 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001189
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001190 snprintf(thread_name, 16, "CtrlService%zu", num);
1191 set_selfthread_name(thread_name);
1192
dburgessb3a0ca42011-10-12 07:44:40 +00001193 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001194 if (!trx->driveControl(num)) {
1195 LOGCHAN(num, DTRXCTRL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
1196 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1197 break;
1198 }
dburgessb3a0ca42011-10-12 07:44:40 +00001199 pthread_testcancel();
1200 }
1201 return NULL;
1202}
1203
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001204void *TxUpperLoopAdapter(TrxChanThParams *params)
dburgessb3a0ca42011-10-12 07:44:40 +00001205{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001206 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001207 Transceiver *trx = params->trx;
1208 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001209
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001210 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001211
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001212 snprintf(thread_name, 16, "TxUpper%zu", num);
1213 set_selfthread_name(thread_name);
1214
dburgessb3a0ca42011-10-12 07:44:40 +00001215 while (1) {
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001216 if (!trx->driveTxPriorityQueue(num)) {
Pau Espin Pedrol9f2baf32019-12-19 21:03:03 +01001217 LOGCHAN(num, DTRXDDL, FATAL) << "Something went wrong in thread " << thread_name << ", requesting stop";
Pau Espin Pedrol76ff96e2019-08-26 12:05:48 +02001218 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
1219 break;
1220 }
dburgessb3a0ca42011-10-12 07:44:40 +00001221 pthread_testcancel();
1222 }
1223 return NULL;
1224}