blob: e946aac6f4b1937c5001677ff38c6a12a992a718 [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) {
183 LOG(ALERT) << "No channels assigned";
184 return false;
185 }
186
Tom Tsou2079a3c2016-03-06 00:58:56 -0800187 if (!sigProcLibSetup()) {
Thomas Tsou83e06892013-08-20 16:10:01 -0400188 LOG(ALERT) << "Failed to initialize signal processing library";
189 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);
215
Thomas Tsou204a9f12013-10-29 18:34:16 -0400216 for (size_t i = 0; i < mChans; i++) {
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500217 c_srcport = mBasePort + 2 * i + 1;
218 c_dstport = mBasePort + 2 * i + 101;
219 d_srcport = mBasePort + 2 * i + 2;
220 d_dstport = mBasePort + 2 * i + 102;
221
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200222 mCtrlSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
223 mLocalAddr.c_str(), c_srcport,
224 mRemoteAddr.c_str(), c_dstport,
225 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
226 if (mCtrlSockets[i] < 0)
227 return false;
228
229 mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
230 mLocalAddr.c_str(), d_srcport,
231 mRemoteAddr.c_str(), d_dstport,
232 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
233 if (mCtrlSockets[i] < 0)
234 return false;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400235 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400236
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800237 /* Randomize the central clock */
238 GSM::Time startTime(random() % gHyperframe, 0);
239 mRadioInterface->getClock()->set(startTime);
240 mTransmitDeadlineClock = startTime;
241 mLastClockUpdateTime = startTime;
242 mLatencyUpdateTime = startTime;
243
244 /* Start control threads */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400245 for (size_t i = 0; i < mChans; i++) {
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200246 TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
247 params->trx = this;
248 params->num = i;
Eric Wildac0487e2019-06-17 13:02:44 +0200249 mControlServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800250 mControlServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200251 ControlServiceLoopAdapter, (void*) params);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400252
Tom Tsou64ad7122015-05-19 18:26:31 -0700253 if (i && filler == FILLER_DUMMY)
254 filler = FILLER_ZERO;
255
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300256 mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
Thomas Tsou83e06892013-08-20 16:10:01 -0400257 }
258
259 return true;
260}
dburgessb3a0ca42011-10-12 07:44:40 +0000261
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800262/*
263 * Start the transceiver
264 *
265 * Submit command(s) to the radio device to commence streaming samples and
266 * launch threads to handle sample I/O. Re-synchronize the transmit burst
267 * counters to the central radio clock here as well.
268 */
269bool Transceiver::start()
270{
271 ScopedLock lock(mLock);
272
273 if (mOn) {
274 LOG(ERR) << "Transceiver already running";
Ivan Kluchnikov194a9b12015-04-23 17:08:27 +0300275 return true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800276 }
277
278 LOG(NOTICE) << "Starting the transceiver";
279
280 GSM::Time time = mRadioInterface->getClock()->get();
281 mTransmitDeadlineClock = time;
282 mLastClockUpdateTime = time;
283 mLatencyUpdateTime = time;
284
285 if (!mRadioInterface->start()) {
286 LOG(ALERT) << "Device failed to start";
287 return false;
288 }
289
290 /* Device is running - launch I/O threads */
Eric Wildac0487e2019-06-17 13:02:44 +0200291 mRxLowerLoopThread = new Thread(stackSize);
292 mTxLowerLoopThread = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800293 mTxLowerLoopThread->start((void * (*)(void*))
294 TxLowerLoopAdapter,(void*) this);
295 mRxLowerLoopThread->start((void * (*)(void*))
296 RxLowerLoopAdapter,(void*) this);
297
298 /* Launch uplink and downlink burst processing threads */
299 for (size_t i = 0; i < mChans; i++) {
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200300 TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
301 params->trx = this;
302 params->num = i;
Eric Wildac0487e2019-06-17 13:02:44 +0200303 mRxServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800304 mRxServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200305 RxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800306
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200307 params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
308 params->trx = this;
309 params->num = i;
Eric Wildac0487e2019-06-17 13:02:44 +0200310 mTxPriorityQueueServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800311 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
Pau Espin Pedrol720b9122019-07-22 18:10:52 +0200312 TxUpperLoopAdapter, (void*) params);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800313 }
314
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200315 mForceClockInterface = true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800316 mOn = true;
317 return true;
318}
319
320/*
321 * Stop the transceiver
322 *
323 * Perform stopping by disabling receive streaming and issuing cancellation
324 * requests to running threads. Most threads will timeout and terminate once
325 * device is disabled, but the transmit loop may block waiting on the central
326 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
327 * makes it to the thread cancellation point.
328 */
329void Transceiver::stop()
330{
331 ScopedLock lock(mLock);
332
333 if (!mOn)
334 return;
335
336 LOG(NOTICE) << "Stopping the transceiver";
337 mTxLowerLoopThread->cancel();
338 mRxLowerLoopThread->cancel();
Tom Tsoud67bd602017-06-15 15:35:02 -0700339 mTxLowerLoopThread->join();
340 mRxLowerLoopThread->join();
341 delete mTxLowerLoopThread;
342 delete mRxLowerLoopThread;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800343
344 for (size_t i = 0; i < mChans; i++) {
345 mRxServiceLoopThreads[i]->cancel();
346 mTxPriorityQueueServiceLoopThreads[i]->cancel();
347 }
348
349 LOG(INFO) << "Stopping the device";
350 mRadioInterface->stop();
351
352 for (size_t i = 0; i < mChans; i++) {
353 mRxServiceLoopThreads[i]->join();
354 mTxPriorityQueueServiceLoopThreads[i]->join();
355 delete mRxServiceLoopThreads[i];
356 delete mTxPriorityQueueServiceLoopThreads[i];
357
358 mTxPriorityQueues[i].clear();
359 }
360
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800361 mOn = false;
362 LOG(NOTICE) << "Transceiver stopped";
363}
364
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500365void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400366 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000367{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500368 signalVector *burst;
369 radioVector *radio_burst;
370
Thomas Tsou204a9f12013-10-29 18:34:16 -0400371 if (chan >= mTxPriorityQueues.size()) {
372 LOG(ALERT) << "Invalid channel " << chan;
373 return;
374 }
375
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500376 if (wTime.TN() > 7) {
377 LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
378 return;
379 }
380
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800381 /* Use the number of bits as the EDGE burst indicator */
382 if (bits.size() == EDGE_BURST_NBITS)
383 burst = modulateEdgeBurst(bits, mSPSTx);
384 else
385 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
386
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500387 scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
dburgessb3a0ca42011-10-12 07:44:40 +0000388
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500389 radio_burst = new radioVector(wTime, burst);
390
391 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000392}
393
Thomas Tsou15d743e2014-01-25 02:34:03 -0500394void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
395{
396 int TN, modFN;
397 TransceiverState *state = &mStates[chan];
398
399 TN = burst->getTime().TN();
400 modFN = burst->getTime().FN() % state->fillerModulus[TN];
401
402 delete state->fillerTable[modFN][TN];
403 state->fillerTable[modFN][TN] = burst->getVector();
404 burst->setVector(NULL);
405}
406
dburgessb3a0ca42011-10-12 07:44:40 +0000407void Transceiver::pushRadioVector(GSM::Time &nowTime)
408{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400409 int TN, modFN;
410 radioVector *burst;
411 TransceiverState *state;
412 std::vector<signalVector *> bursts(mChans);
413 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500414 std::vector<bool> filler(mChans, true);
dburgessb3a0ca42011-10-12 07:44:40 +0000415
Thomas Tsou204a9f12013-10-29 18:34:16 -0400416 for (size_t i = 0; i < mChans; i ++) {
417 state = &mStates[i];
dburgessb3a0ca42011-10-12 07:44:40 +0000418
Thomas Tsou204a9f12013-10-29 18:34:16 -0400419 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200420 LOGCHAN(i, DMAIN, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
Pau Espin Pedrolf37b0ad2018-04-25 18:01:27 +0200421 << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500422 if (state->mRetrans)
423 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500424 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400425 }
426
427 TN = nowTime.TN();
428 modFN = nowTime.FN() % state->fillerModulus[TN];
429
430 bursts[i] = state->fillerTable[modFN][TN];
431 zeros[i] = state->chanType[TN] == NONE;
432
433 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500434 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500435
436 if (state->mRetrans) {
437 updateFillerTable(i, burst);
438 } else {
439 burst->setVector(NULL);
440 filler[i] = false;
441 }
442
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500443 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400444 }
dburgessb3a0ca42011-10-12 07:44:40 +0000445 }
446
Thomas Tsou204a9f12013-10-29 18:34:16 -0400447 mRadioInterface->driveTransmitRadio(bursts, zeros);
448
Thomas Tsou15d743e2014-01-25 02:34:03 -0500449 for (size_t i = 0; i < mChans; i++) {
450 if (!filler[i])
451 delete bursts[i];
452 }
dburgessb3a0ca42011-10-12 07:44:40 +0000453}
454
Thomas Tsou204a9f12013-10-29 18:34:16 -0400455void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000456{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400457 TransceiverState *state = &mStates[chan];
458
459 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000460 case NONE:
461 case I:
462 case II:
463 case III:
464 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400465 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000466 break;
467 case IV:
468 case VI:
469 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400470 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000471 break;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200472 //case V:
dburgessb3a0ca42011-10-12 07:44:40 +0000473 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400474 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000475 break;
ttsoufc40a842013-06-09 22:38:18 +0000476 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400477 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000478 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000479 default:
480 break;
481 }
482}
483
484
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700485CorrType Transceiver::expectedCorrType(GSM::Time currTime,
486 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000487{
Alexander Chemeris5a068062015-06-20 01:38:47 +0300488 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 };
489 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,
490 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 };
491 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,
492 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 -0400493 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000494 unsigned burstTN = currTime.TN();
495 unsigned burstFN = currTime.FN();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300496 int subch;
dburgessb3a0ca42011-10-12 07:44:40 +0000497
Thomas Tsou204a9f12013-10-29 18:34:16 -0400498 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000499 case NONE:
500 return OFF;
501 break;
502 case FILL:
503 return IDLE;
504 break;
505 case I:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300506 // TODO: Are we expecting RACH on an IDLE frame?
507/* if (burstFN % 26 == 25)
508 return IDLE;*/
509 if (mHandover[burstTN][0])
510 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000511 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000512 break;
513 case II:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300514 subch = tchh_subslot[burstFN % 26];
515 if (subch == 1)
516 return IDLE;
517 if (mHandover[burstTN][0])
518 return RACH;
ttsou20642972013-03-27 22:00:25 +0000519 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000520 break;
521 case III:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300522 subch = tchh_subslot[burstFN % 26];
523 if (mHandover[burstTN][subch])
524 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000525 return TSC;
526 break;
527 case IV:
528 case VI:
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200529 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000530 break;
531 case V: {
532 int mod51 = burstFN % 51;
533 if ((mod51 <= 36) && (mod51 >= 14))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200534 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000535 else if ((mod51 == 4) || (mod51 == 5))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200536 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000537 else if ((mod51 == 45) || (mod51 == 46))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200538 return mExtRACH ? EXT_RACH : RACH;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300539 else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
540 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000541 else
542 return TSC;
543 break;
544 }
545 case VII:
546 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
547 return IDLE;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300548 else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
549 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000550 else
551 return TSC;
552 break;
ttsoufc40a842013-06-09 22:38:18 +0000553 case XIII: {
554 int mod52 = burstFN % 52;
555 if ((mod52 == 12) || (mod52 == 38))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200556 return mExtRACH ? EXT_RACH : RACH;
ttsoufc40a842013-06-09 22:38:18 +0000557 else if ((mod52 == 25) || (mod52 == 51))
558 return IDLE;
559 else
560 return TSC;
561 break;
562 }
dburgessb3a0ca42011-10-12 07:44:40 +0000563 case LOOPBACK:
564 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
565 return IDLE;
566 else
567 return TSC;
568 break;
569 default:
570 return OFF;
571 break;
572 }
dburgessb3a0ca42011-10-12 07:44:40 +0000573}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400574
Alexander Chemerise692ce92015-06-12 00:15:31 -0400575void writeToFile(radioVector *radio_burst, size_t chan)
576{
577 GSM::Time time = radio_burst->getTime();
578 std::ostringstream fname;
579 fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
580 std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
581 outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
582 outfile.close();
583}
584
Thomas Tsou30421a72013-11-13 23:14:48 -0500585/*
586 * Pull bursts from the FIFO and handle according to the slot
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200587 * and burst correlation type. Equalzation is currently disabled.
Thomas Tsou30421a72013-11-13 23:14:48 -0500588 */
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200589bool Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
dburgessb3a0ca42011-10-12 07:44:40 +0000590{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800591 int rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200592 struct estim_burst_params ebp;
593 float max = -1.0, avg = 0.0;
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200594 unsigned max_toa;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500595 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500596 signalVector *burst;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200597 GSM::Time burstTime;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200598 SoftVector *rxBurst;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500599 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000600
Thomas Tsou30421a72013-11-13 23:14:48 -0500601 /* Blocking FIFO read */
602 radioVector *radio_burst = mReceiveFIFO[chan]->read();
603 if (!radio_burst)
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200604 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000605
Thomas Tsou30421a72013-11-13 23:14:48 -0500606 /* Set time and determine correlation type */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200607 burstTime = radio_burst->getTime();
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200608 CorrType type = expectedCorrType(burstTime, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000609
Tom Tsou64464e62016-07-01 03:46:46 -0700610 /* Enable 8-PSK burst detection if EDGE is enabled */
611 if (mEdge && (type == TSC))
612 type = EDGE;
613
Alexander Chemerise692ce92015-06-12 00:15:31 -0400614 /* Debug: dump bursts to disk */
615 /* bits 0-7 - chan 0 timeslots
616 * bits 8-15 - chan 1 timeslots */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200617 if (mWriteBurstToDiskMask & ((1<<bi->tn) << (8*chan)))
Alexander Chemerise692ce92015-06-12 00:15:31 -0400618 writeToFile(radio_burst, chan);
619
Alexander Chemeris2b542102015-06-08 22:46:38 -0400620 /* No processing if the timeslot is off.
621 * Not even power level or noise calculation. */
622 if (type == OFF) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500623 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200624 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000625 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000626
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200627 /* Initialize struct bi */
628 bi->nbits = 0;
629 bi->fn = burstTime.FN();
630 bi->tn = burstTime.TN();
631 bi->rssi = 0.0;
632 bi->toa = 0.0;
633 bi->noise = 0.0;
634 bi->idle = false;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200635 bi->modulation = MODULATION_GMSK;
636 bi->tss = 0; /* TODO: we only support tss 0 right now */
637 bi->tsc = 0;
638 bi->ci = 0.0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200639
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500640 /* Select the diversity channel with highest energy */
641 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300642 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500643 if (pow > max) {
644 max = pow;
645 max_i = i;
646 }
647 avg += pow;
648 }
649
650 if (max_i < 0) {
651 LOG(ALERT) << "Received empty burst";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200652 goto ret_idle;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500653 }
654
Thomas Tsou30421a72013-11-13 23:14:48 -0500655 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500656 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500657 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400658
Alexander Chemeris2b542102015-06-08 22:46:38 -0400659 if (type == IDLE) {
660 /* Update noise levels */
661 state->mNoises.insert(avg);
662 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200663 }
Alexander Chemeris2b542102015-06-08 22:46:38 -0400664
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200665 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
666 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
667
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200668 if (type == IDLE)
669 goto ret_idle;
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400670
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200671 max_toa = (type == RACH || type == EXT_RACH) ?
672 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200673
Thomas Tsou30421a72013-11-13 23:14:48 -0500674 /* Detect normal or RACH bursts */
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200675 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, max_toa, &ebp);
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200676 if (rc <= 0) {
677 if (rc == -SIGERR_CLIP)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400678 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200679 else if (rc != SIGERR_NONE)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400680 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200681 goto ret_idle;
dburgessb3a0ca42011-10-12 07:44:40 +0000682 }
dburgessb3a0ca42011-10-12 07:44:40 +0000683
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200684 type = (CorrType) rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200685 bi->toa = ebp.toa;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200686 bi->tsc = ebp.tsc;
687 bi->ci = ebp.ci;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200688 rxBurst = demodAnyBurst(*burst, mSPSRx, ebp.amp, ebp.toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500689
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200690 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200691 if (rxBurst->size() == EDGE_BURST_NBITS) {
692 bi->modulation = MODULATION_8PSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200693 bi->nbits = EDGE_BURST_NBITS;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200694 } else { /* size() here is actually gSlotLen + 8, due to guard periods */
695 bi->modulation = MODULATION_GMSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200696 bi->nbits = gSlotLen;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200697 }
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200698
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200699 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200700 vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200701
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200702 delete rxBurst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500703 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200704 return true;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200705
706ret_idle:
707 bi->idle = true;
708 delete radio_burst;
709 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000710}
711
dburgessb3a0ca42011-10-12 07:44:40 +0000712void Transceiver::reset()
713{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400714 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
715 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000716}
717
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200718
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700719#define MAX_PACKET_LENGTH 100
720
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700721/**
722 * Matches a buffer with a command.
723 * @param buf a buffer to look command in
724 * @param cmd a command to look in buffer
725 * @param params pointer to arguments, or NULL
726 * @return true if command matches, otherwise false
727 */
728static bool match_cmd(char *buf,
729 const char *cmd, char **params)
730{
731 size_t cmd_len = strlen(cmd);
732
733 /* Check a command itself */
734 if (strncmp(buf, cmd, cmd_len))
735 return false;
736
737 /* A command has arguments */
738 if (params != NULL) {
739 /* Make sure there is a space */
740 if (buf[cmd_len] != ' ')
741 return false;
742
743 /* Update external pointer */
744 *params = buf + cmd_len + 1;
745 }
746
747 return true;
748}
749
Thomas Tsou204a9f12013-10-29 18:34:16 -0400750void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000751{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700752 char buffer[MAX_PACKET_LENGTH + 1];
753 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700754 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700755 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400756
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700757 /* Attempt to read from control socket */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200758 msgLen = read(mCtrlSockets[chan], buffer, MAX_PACKET_LENGTH);
759 if (msgLen <= 0) {
760 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000761 return;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200762 }
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700763
764 /* Zero-terminate received string */
765 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000766
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700767 /* Verify a command signature */
768 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100769 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000770 return;
771 }
dburgessb3a0ca42011-10-12 07:44:40 +0000772
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700773 /* Set command pointer */
774 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200775 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700776
777 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800778 stop();
779 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700780 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700781 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000782 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700783 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000784 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300785 for (int i = 0; i < 8; i++) {
786 for (int j = 0; j < 8; j++)
787 mHandover[i][j] = false;
788 }
Tom Tsou365bc382016-10-19 15:26:04 -0700789 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700790 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700791 unsigned ts = 0, ss = 0;
792 sscanf(params, "%u %u", &ts, &ss);
793 if (ts > 7 || ss > 7) {
794 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
795 } else {
796 mHandover[ts][ss] = true;
797 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
798 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700799 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700800 unsigned ts = 0, ss = 0;
801 sscanf(params, "%u %u", &ts, &ss);
802 if (ts > 7 || ss > 7) {
803 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
804 } else {
805 mHandover[ts][ss] = false;
806 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
807 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700808 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000809 //set expected maximum time-of-arrival
810 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700811 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300812 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000813 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700814 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300815 //set expected maximum time-of-arrival
816 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700817 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300818 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
819 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700820 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000821 //set expected maximum time-of-arrival
822 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700823 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400824 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000825 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700826 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000827 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500828 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000829 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500830 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000831 }
832 else {
833 sprintf(response,"RSP NOISELEV 1 0");
834 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700835 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800836 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700837 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800838 power = mRadioInterface->setPowerAttenuation(power, chan);
839 mStates[chan].mPower = power;
840 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700841 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800842 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700843 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800844 power = mStates[chan].mPower + step;
845 power = mRadioInterface->setPowerAttenuation(power, chan);
846 mStates[chan].mPower = power;
847 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700848 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000849 // tune receiver
850 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700851 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500852 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400853 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100854 LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000855 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
856 }
857 else
858 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700859 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000860 // tune txmtr
861 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700862 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500863 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400864 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100865 LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000866 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
867 }
868 else
869 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700870 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000871 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500872 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700873 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700874 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500875 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700876 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100877 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000878 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400879 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000880 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700881 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700882 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000883 int corrCode;
884 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700885 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000886 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100887 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000888 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
889 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200890 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400891 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
892 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000893 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200894 } else if (match_cmd(command, "SETFORMAT", &params)) {
895 // set TRXD protocol version
896 unsigned version_recv;
897 sscanf(params, "%u", &version_recv);
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200898 LOGCHAN(chan, DTRXCTRL, INFO) << "BTS requests TRXD version switch: " << version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200899 if (version_recv > TRX_DATA_FORMAT_VER) {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200900 LOGCHAN(chan, DTRXCTRL, INFO) << "rejecting TRXD version " << version_recv
901 << "in favor of " << TRX_DATA_FORMAT_VER;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200902 sprintf(response, "RSP SETFORMAT %u %u", TRX_DATA_FORMAT_VER, version_recv);
903 } else {
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +0200904 LOGCHAN(chan, DTRXCTRL, NOTICE) << "switching to TRXD version " << version_recv;
905 mVersionTRXD[chan] = version_recv;
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200906 sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
907 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700908 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400909 // debug command! may change or disapear without notice
910 // set a mask which bursts to dump to disk
911 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700912 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400913 mWriteBurstToDiskMask = mask;
914 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700915 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100916 LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200917 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000918 }
919
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200920 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200921 msgLen = write(mCtrlSockets[chan], response, strlen(response) + 1);
922 if (msgLen <= 0)
923 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000924}
925
Thomas Tsou204a9f12013-10-29 18:34:16 -0400926bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000927{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200928 int msgLen;
Tom Tsoue8871082016-07-01 02:46:04 -0700929 int burstLen;
930 char buffer[EDGE_BURST_NBITS + 50];
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700931 struct trxd_hdr_common *chdr;
932 uint32_t fn;
dburgessb3a0ca42011-10-12 07:44:40 +0000933
934 // check data socket
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200935 msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
936 if (msgLen <= 0) {
937 LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
938 return false;
939 }
dburgessb3a0ca42011-10-12 07:44:40 +0000940
Tom Tsoue8871082016-07-01 02:46:04 -0700941 if (msgLen == gSlotLen + 1 + 4 + 1) {
942 burstLen = gSlotLen;
943 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
944 if (mSPSTx != 4)
945 return false;
946
947 burstLen = EDGE_BURST_NBITS;
948 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000949 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
950 return false;
951 }
952
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700953 /* Common header part: HDR version, TDMA TN & FN */
954 chdr = (struct trxd_hdr_common *) buffer;
dburgessb3a0ca42011-10-12 07:44:40 +0000955
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700956 /* Convert TDMA FN to the host endianness */
957 fn = osmo_load32be(&chdr->fn);
958
Vadim Yanitskiydd571c62019-07-15 23:56:56 +0700959 /* Make sure we support the received header format */
960 switch (chdr->version) {
961 case 0:
962 /* Version 1 has the same format */
963 case 1:
964 break;
965
966 default:
967 LOG(ERR) << "Rx TRXD message with unknown header version " << chdr->version;
968 return false;
969 }
970
Vadim Yanitskiy56c5f292019-07-16 00:02:56 +0700971 LOG(DEBUG) << "Rx TRXD message (hdr_ver=" << chdr->version << "): "
972 << "fn=" << fn << ", tn=" << chdr->tn << ", "
973 << "burst_len=" << burstLen;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200974
dburgessb3a0ca42011-10-12 07:44:40 +0000975 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700976 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000977 BitVector::iterator itr = newBurst.begin();
978 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200979 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000980 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200981
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700982 GSM::Time currTime = GSM::Time(fn, chdr->tn);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400983
984 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000985
986 return true;
987
988
989}
dburgessb3a0ca42011-10-12 07:44:40 +0000990
Thomas Tsou204a9f12013-10-29 18:34:16 -0400991void Transceiver::driveReceiveRadio()
992{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200993 int rc = mRadioInterface->driveReceiveRadio();
994 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400995 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200996 } else if (rc < 0) {
997 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
Pau Espin Pedrolb426e4a2019-06-04 12:39:28 +0200998 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200999 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
1000 mForceClockInterface = false;
1001 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -04001002 }
Thomas Tsou204a9f12013-10-29 18:34:16 -04001003}
1004
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001005void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001006{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001007 std::ostringstream os;
1008 for (size_t i=0; i < bi->nbits; i++) {
1009 if (bi->rx_burst[i] > 0.5) os << "1";
1010 else if (bi->rx_burst[i] > 0.25) os << "|";
1011 else if (bi->rx_burst[i] > 0.0) os << "'";
1012 else os << "-";
1013 }
1014
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001015 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +03001016 << " chan: " << chan
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +02001017 << " time: " << bi->tn << ":" << bi->fn
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001018 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
1019 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
1020 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
1021 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001022 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001023 << " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB"
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001024 << " bits: " << os;
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001025}
1026
Thomas Tsou204a9f12013-10-29 18:34:16 -04001027void Transceiver::driveReceiveFIFO(size_t chan)
1028{
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001029 struct trx_ul_burst_ind bi;
1030
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001031 if (!pullRadioVector(chan, &bi))
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001032 return;
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001033 if (!bi.idle)
1034 logRxBurst(chan, &bi);
dburgessb3a0ca42011-10-12 07:44:40 +00001035
Pau Espin Pedrolc3325b92019-07-22 17:47:02 +02001036 switch (mVersionTRXD[chan]) {
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001037 case 0:
1038 trxd_send_burst_ind_v0(chan, mDataSockets[chan], &bi);
1039 break;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001040 case 1:
1041 trxd_send_burst_ind_v1(chan, mDataSockets[chan], &bi);
1042 break;
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001043 default:
1044 OSMO_ASSERT(false);
1045 }
dburgessb3a0ca42011-10-12 07:44:40 +00001046}
1047
Thomas Tsou204a9f12013-10-29 18:34:16 -04001048void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +00001049{
1050
1051 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001052 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +00001053 assure that transmit packets arrive at the radio/USRP
1054 before they need to be transmitted.
1055
1056 Deadline clock indicates the burst that needs to be
1057 pushed into the FIFO right NOW. If transmit queue does
1058 not have a burst, stick in filler data.
1059 */
1060
1061
1062 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001063
dburgessb3a0ca42011-10-12 07:44:40 +00001064 if (mOn) {
1065 //radioClock->wait(); // wait until clock updates
1066 LOG(DEBUG) << "radio clock " << radioClock->get();
1067 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1068 // if underrun, then we're not providing bursts to radio/USRP fast
1069 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001070 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001071 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001072 // only update latency at the defined frame interval
1073 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001074 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol74bcc562018-10-02 17:30:28 +02001075 LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
1076 << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001077 mLatencyUpdateTime = radioClock->get();
1078 }
1079 }
1080 else {
1081 // if underrun hasn't occurred in the last sec (216 frames) drop
1082 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001083 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001084 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1085 mTransmitLatency.decTN();
1086 LOG(INFO) << "reduced latency: " << mTransmitLatency;
1087 mLatencyUpdateTime = radioClock->get();
1088 }
1089 }
1090 }
dburgessb3a0ca42011-10-12 07:44:40 +00001091 }
dburgessb3a0ca42011-10-12 07:44:40 +00001092 // time to push burst to transmit FIFO
1093 pushRadioVector(mTransmitDeadlineClock);
1094 mTransmitDeadlineClock.incTN();
1095 }
dburgessb3a0ca42011-10-12 07:44:40 +00001096 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001097
1098 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001099}
1100
1101
1102
1103void Transceiver::writeClockInterface()
1104{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001105 int msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001106 char command[50];
1107 // FIXME -- This should be adaptive.
1108 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1109
1110 LOG(INFO) << "ClockInterface: sending " << command;
1111
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001112 msgLen = write(mClockSocket, command, strlen(command) + 1);
1113 if (msgLen <= 0)
1114 LOG(WARNING) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001115
1116 mLastClockUpdateTime = mTransmitDeadlineClock;
1117
Thomas Tsou92c16df2013-09-28 18:04:19 -04001118}
dburgessb3a0ca42011-10-12 07:44:40 +00001119
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001120void *RxUpperLoopAdapter(TrxChanThParams *params)
Thomas Tsou204a9f12013-10-29 18:34:16 -04001121{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001122 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001123 Transceiver *trx = params->trx;
1124 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001125
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001126 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001127
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001128 snprintf(thread_name, 16, "RxUpper%zu", num);
1129 set_selfthread_name(thread_name);
1130
Thomas Tsou204a9f12013-10-29 18:34:16 -04001131 while (1) {
1132 trx->driveReceiveFIFO(num);
1133 pthread_testcancel();
1134 }
1135 return NULL;
1136}
1137
1138void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001139{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001140 set_selfthread_name("RxLower");
1141
dburgessb3a0ca42011-10-12 07:44:40 +00001142 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001143 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001144 pthread_testcancel();
1145 }
1146 return NULL;
1147}
1148
Thomas Tsou204a9f12013-10-29 18:34:16 -04001149void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001150{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001151 set_selfthread_name("TxLower");
1152
Thomas Tsou92c16df2013-09-28 18:04:19 -04001153 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001154 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001155 pthread_testcancel();
1156 }
1157 return NULL;
1158}
1159
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001160void *ControlServiceLoopAdapter(TrxChanThParams *params)
dburgessb3a0ca42011-10-12 07:44:40 +00001161{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001162 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001163 Transceiver *trx = params->trx;
1164 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001165
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001166 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001167
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001168 snprintf(thread_name, 16, "CtrlService%zu", num);
1169 set_selfthread_name(thread_name);
1170
dburgessb3a0ca42011-10-12 07:44:40 +00001171 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001172 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001173 pthread_testcancel();
1174 }
1175 return NULL;
1176}
1177
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001178void *TxUpperLoopAdapter(TrxChanThParams *params)
dburgessb3a0ca42011-10-12 07:44:40 +00001179{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001180 char thread_name[16];
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001181 Transceiver *trx = params->trx;
1182 size_t num = params->num;
Thomas Tsou204a9f12013-10-29 18:34:16 -04001183
Pau Espin Pedrol720b9122019-07-22 18:10:52 +02001184 free(params);
Thomas Tsou204a9f12013-10-29 18:34:16 -04001185
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001186 snprintf(thread_name, 16, "TxUpper%zu", num);
1187 set_selfthread_name(thread_name);
1188
dburgessb3a0ca42011-10-12 07:44:40 +00001189 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001190 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001191 pthread_testcancel();
1192 }
1193 return NULL;
1194}