blob: a12c5bc1aec13e779823ae6bd5c6055e6effafb6 [file] [log] [blame]
dburgessb3a0ca42011-10-12 07:44:40 +00001/*
2* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
3*
4* This software is distributed under the terms of the GNU Public License.
5* See the COPYING file in the main directory for details.
6*
7* This use of this software may be subject to additional restrictions.
8* See the LEGAL file in the main directory for details.
9
10 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22*/
23
dburgessb3a0ca42011-10-12 07:44:40 +000024#include <stdio.h>
Alexander Chemerise8905a02015-06-03 23:47:56 -040025#include <iomanip> // std::setprecision
Alexander Chemerise692ce92015-06-12 00:15:31 -040026#include <fstream>
dburgessb3a0ca42011-10-12 07:44:40 +000027#include "Transceiver.h"
28#include <Logger.h>
29
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020030extern "C" {
31#include "osmo_signal.h"
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +020032#include "proto_trxd.h"
33
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +020034#include <osmocom/core/utils.h>
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +020035#include <osmocom/core/socket.h>
Vadim Yanitskiyb3123252019-07-15 23:53:08 +070036#include <osmocom/core/bits.h>
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020037}
38
ttsou2173abf2012-08-08 00:51:31 +000039#ifdef HAVE_CONFIG_H
40#include "config.h"
41#endif
dburgessb3a0ca42011-10-12 07:44:40 +000042
Alexander Chemerisd734e2d2013-06-16 14:30:58 +040043using namespace GSM;
44
kurtis.heimerlec842de2012-11-23 08:37:32 +000045#define USB_LATENCY_INTRVL 10,0
ttsou2173abf2012-08-08 00:51:31 +000046
Thomas Tsoufa3a7872013-10-17 21:23:34 -040047/* Number of running values use in noise average */
48#define NOISE_CNT 20
ttsoue8dde022012-12-06 15:43:55 +000049
Thomas Tsouf0782732013-10-29 15:55:47 -040050TransceiverState::TransceiverState()
Tom Tsoua4d1a412014-11-25 15:46:56 -080051 : mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
Thomas Tsouf0782732013-10-29 15:55:47 -040052{
53 for (int i = 0; i < 8; i++) {
54 chanType[i] = Transceiver::NONE;
55 fillerModulus[i] = 26;
56 chanResponse[i] = NULL;
57 DFEForward[i] = NULL;
58 DFEFeedback[i] = NULL;
59
60 for (int n = 0; n < 102; n++)
61 fillerTable[n][i] = NULL;
62 }
63}
64
65TransceiverState::~TransceiverState()
66{
67 for (int i = 0; i < 8; i++) {
68 delete chanResponse[i];
69 delete DFEForward[i];
70 delete DFEFeedback[i];
71
72 for (int n = 0; n < 102; n++)
73 delete fillerTable[n][i];
74 }
75}
76
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010077bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
Tom Tsou64ad7122015-05-19 18:26:31 -070078{
Tom Tsou64ad7122015-05-19 18:26:31 -070079 signalVector *burst;
80
81 if ((sps != 1) && (sps != 4))
82 return false;
83
84 for (size_t n = 0; n < 8; n++) {
Tom Tsou64ad7122015-05-19 18:26:31 -070085 for (size_t i = 0; i < 102; i++) {
86 switch (filler) {
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010087 case FILLER_DUMMY:
Tom Tsou8ee2f382016-03-06 20:57:34 -080088 burst = generateDummyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070089 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010090 case FILLER_NORM_RAND:
Tom Tsou8ee2f382016-03-06 20:57:34 -080091 burst = genRandNormalBurst(rtsc, sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070092 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010093 case FILLER_EDGE_RAND:
Tom Tsouaf717b22016-03-06 22:19:15 -080094 burst = generateEdgeBurst(rtsc);
95 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010096 case FILLER_ACCESS_RAND:
Alexander Chemeris37c52c72016-03-25 18:28:34 +030097 burst = genRandAccessBurst(rach_delay, sps, n);
Alexander Chemeris5efe0502016-03-23 17:06:32 +030098 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010099 case FILLER_ZERO:
Tom Tsou64ad7122015-05-19 18:26:31 -0700100 default:
Tom Tsou8ee2f382016-03-06 20:57:34 -0800101 burst = generateEmptyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -0700102 }
103
104 scaleVector(*burst, scale);
105 fillerTable[i][n] = burst;
106 }
107
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100108 if ((filler == FILLER_NORM_RAND) ||
109 (filler == FILLER_EDGE_RAND)) {
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700110 chanType[n] = TSC;
Tom Tsouaf717b22016-03-06 22:19:15 -0800111 }
Thomas Tsou15d743e2014-01-25 02:34:03 -0500112 }
Tom Tsou64ad7122015-05-19 18:26:31 -0700113
114 return false;
Thomas Tsouf0782732013-10-29 15:55:47 -0400115}
116
dburgessb3a0ca42011-10-12 07:44:40 +0000117Transceiver::Transceiver(int wBasePort,
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200118 const char *TRXAddress,
119 const char *GSMcoreAddress,
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800120 size_t tx_sps, size_t rx_sps, size_t chans,
Alexander Chemerise8905a02015-06-03 23:47:56 -0400121 GSM::Time wTransmitLatency,
122 RadioInterface *wRadioInterface,
Eric Wildac0487e2019-06-17 13:02:44 +0200123 double wRssiOffset, int wStackSize)
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200124 : mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200125 mClockSocket(-1), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
Eric Wildac0487e2019-06-17 13:02:44 +0200126 rssiOffset(wRssiOffset), stackSize(wStackSize),
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200127 mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false),
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300128 mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +0200129 mWriteBurstToDiskMask(0), mVersionTRXD(0)
dburgessb3a0ca42011-10-12 07:44:40 +0000130{
dburgessb3a0ca42011-10-12 07:44:40 +0000131 txFullScale = mRadioInterface->fullScaleInputValue();
132 rxFullScale = mRadioInterface->fullScaleOutputValue();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300133
134 for (int i = 0; i < 8; i++) {
135 for (int j = 0; j < 8; j++)
136 mHandover[i][j] = false;
137 }
dburgessb3a0ca42011-10-12 07:44:40 +0000138}
139
140Transceiver::~Transceiver()
141{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800142 stop();
143
dburgessb3a0ca42011-10-12 07:44:40 +0000144 sigProcLibDestroy();
Thomas Tsoud647ec52013-10-29 15:17:34 -0400145
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200146 if (mClockSocket >= 0)
147 close(mClockSocket);
148
Thomas Tsou204a9f12013-10-29 18:34:16 -0400149 for (size_t i = 0; i < mChans; i++) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800150 mControlServiceLoopThreads[i]->cancel();
151 mControlServiceLoopThreads[i]->join();
152 delete mControlServiceLoopThreads[i];
153
Thomas Tsou204a9f12013-10-29 18:34:16 -0400154 mTxPriorityQueues[i].clear();
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200155 if (mCtrlSockets[i] >= 0)
156 close(mCtrlSockets[i]);
157 if (mDataSockets[i] >= 0)
158 close(mDataSockets[i]);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400159 }
dburgessb3a0ca42011-10-12 07:44:40 +0000160}
Thomas Tsou83e06892013-08-20 16:10:01 -0400161
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800162/*
163 * Initialize transceiver
164 *
165 * Start or restart the control loop. Any further control is handled through the
166 * socket API. Randomize the central radio clock set the downlink burst
167 * counters. Note that the clock will not update until the radio starts, but we
168 * are still expected to report clock indications through control channel
169 * activity.
170 */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200171bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
172 bool edge, bool ext_rach)
Thomas Tsou83e06892013-08-20 16:10:01 -0400173{
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500174 int d_srcport, d_dstport, c_srcport, c_dstport;
Thomas Tsouf0782732013-10-29 15:55:47 -0400175
Thomas Tsou204a9f12013-10-29 18:34:16 -0400176 if (!mChans) {
177 LOG(ALERT) << "No channels assigned";
178 return false;
179 }
180
Tom Tsou2079a3c2016-03-06 00:58:56 -0800181 if (!sigProcLibSetup()) {
Thomas Tsou83e06892013-08-20 16:10:01 -0400182 LOG(ALERT) << "Failed to initialize signal processing library";
183 return false;
184 }
185
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200186 mExtRACH = ext_rach;
Tom Tsou64464e62016-07-01 03:46:46 -0700187 mEdge = edge;
188
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200189 mDataSockets.resize(mChans, -1);
190 mCtrlSockets.resize(mChans, -1);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400191 mControlServiceLoopThreads.resize(mChans);
192 mTxPriorityQueueServiceLoopThreads.resize(mChans);
193 mRxServiceLoopThreads.resize(mChans);
194
195 mTxPriorityQueues.resize(mChans);
196 mReceiveFIFO.resize(mChans);
197 mStates.resize(mChans);
198
Thomas Tsouccb73e12014-04-15 17:41:28 -0400199 /* Filler table retransmissions - support only on channel 0 */
Tom Tsou64ad7122015-05-19 18:26:31 -0700200 if (filler == FILLER_DUMMY)
Thomas Tsouccb73e12014-04-15 17:41:28 -0400201 mStates[0].mRetrans = true;
202
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800203 /* Setup sockets */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200204 mClockSocket = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
205 mLocalAddr.c_str(), mBasePort,
206 mRemoteAddr.c_str(), mBasePort + 100,
207 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
208
Thomas Tsou204a9f12013-10-29 18:34:16 -0400209 for (size_t i = 0; i < mChans; i++) {
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500210 c_srcport = mBasePort + 2 * i + 1;
211 c_dstport = mBasePort + 2 * i + 101;
212 d_srcport = mBasePort + 2 * i + 2;
213 d_dstport = mBasePort + 2 * i + 102;
214
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200215 mCtrlSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
216 mLocalAddr.c_str(), c_srcport,
217 mRemoteAddr.c_str(), c_dstport,
218 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
219 if (mCtrlSockets[i] < 0)
220 return false;
221
222 mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
223 mLocalAddr.c_str(), d_srcport,
224 mRemoteAddr.c_str(), d_dstport,
225 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
226 if (mCtrlSockets[i] < 0)
227 return false;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400228 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400229
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800230 /* Randomize the central clock */
231 GSM::Time startTime(random() % gHyperframe, 0);
232 mRadioInterface->getClock()->set(startTime);
233 mTransmitDeadlineClock = startTime;
234 mLastClockUpdateTime = startTime;
235 mLatencyUpdateTime = startTime;
236
237 /* Start control threads */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400238 for (size_t i = 0; i < mChans; i++) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800239 TransceiverChannel *chan = new TransceiverChannel(this, i);
Eric Wildac0487e2019-06-17 13:02:44 +0200240 mControlServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800241 mControlServiceLoopThreads[i]->start((void * (*)(void*))
242 ControlServiceLoopAdapter, (void*) chan);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400243
Tom Tsou64ad7122015-05-19 18:26:31 -0700244 if (i && filler == FILLER_DUMMY)
245 filler = FILLER_ZERO;
246
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300247 mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
Thomas Tsou83e06892013-08-20 16:10:01 -0400248 }
249
250 return true;
251}
dburgessb3a0ca42011-10-12 07:44:40 +0000252
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800253/*
254 * Start the transceiver
255 *
256 * Submit command(s) to the radio device to commence streaming samples and
257 * launch threads to handle sample I/O. Re-synchronize the transmit burst
258 * counters to the central radio clock here as well.
259 */
260bool Transceiver::start()
261{
262 ScopedLock lock(mLock);
263
264 if (mOn) {
265 LOG(ERR) << "Transceiver already running";
Ivan Kluchnikov194a9b12015-04-23 17:08:27 +0300266 return true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800267 }
268
269 LOG(NOTICE) << "Starting the transceiver";
270
271 GSM::Time time = mRadioInterface->getClock()->get();
272 mTransmitDeadlineClock = time;
273 mLastClockUpdateTime = time;
274 mLatencyUpdateTime = time;
275
276 if (!mRadioInterface->start()) {
277 LOG(ALERT) << "Device failed to start";
278 return false;
279 }
280
281 /* Device is running - launch I/O threads */
Eric Wildac0487e2019-06-17 13:02:44 +0200282 mRxLowerLoopThread = new Thread(stackSize);
283 mTxLowerLoopThread = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800284 mTxLowerLoopThread->start((void * (*)(void*))
285 TxLowerLoopAdapter,(void*) this);
286 mRxLowerLoopThread->start((void * (*)(void*))
287 RxLowerLoopAdapter,(void*) this);
288
289 /* Launch uplink and downlink burst processing threads */
290 for (size_t i = 0; i < mChans; i++) {
291 TransceiverChannel *chan = new TransceiverChannel(this, i);
Eric Wildac0487e2019-06-17 13:02:44 +0200292 mRxServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800293 mRxServiceLoopThreads[i]->start((void * (*)(void*))
294 RxUpperLoopAdapter, (void*) chan);
295
296 chan = new TransceiverChannel(this, i);
Eric Wildac0487e2019-06-17 13:02:44 +0200297 mTxPriorityQueueServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800298 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
299 TxUpperLoopAdapter, (void*) chan);
300 }
301
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200302 mForceClockInterface = true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800303 mOn = true;
304 return true;
305}
306
307/*
308 * Stop the transceiver
309 *
310 * Perform stopping by disabling receive streaming and issuing cancellation
311 * requests to running threads. Most threads will timeout and terminate once
312 * device is disabled, but the transmit loop may block waiting on the central
313 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
314 * makes it to the thread cancellation point.
315 */
316void Transceiver::stop()
317{
318 ScopedLock lock(mLock);
319
320 if (!mOn)
321 return;
322
323 LOG(NOTICE) << "Stopping the transceiver";
324 mTxLowerLoopThread->cancel();
325 mRxLowerLoopThread->cancel();
Tom Tsoud67bd602017-06-15 15:35:02 -0700326 mTxLowerLoopThread->join();
327 mRxLowerLoopThread->join();
328 delete mTxLowerLoopThread;
329 delete mRxLowerLoopThread;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800330
331 for (size_t i = 0; i < mChans; i++) {
332 mRxServiceLoopThreads[i]->cancel();
333 mTxPriorityQueueServiceLoopThreads[i]->cancel();
334 }
335
336 LOG(INFO) << "Stopping the device";
337 mRadioInterface->stop();
338
339 for (size_t i = 0; i < mChans; i++) {
340 mRxServiceLoopThreads[i]->join();
341 mTxPriorityQueueServiceLoopThreads[i]->join();
342 delete mRxServiceLoopThreads[i];
343 delete mTxPriorityQueueServiceLoopThreads[i];
344
345 mTxPriorityQueues[i].clear();
346 }
347
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800348 mOn = false;
349 LOG(NOTICE) << "Transceiver stopped";
350}
351
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500352void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400353 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000354{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500355 signalVector *burst;
356 radioVector *radio_burst;
357
Thomas Tsou204a9f12013-10-29 18:34:16 -0400358 if (chan >= mTxPriorityQueues.size()) {
359 LOG(ALERT) << "Invalid channel " << chan;
360 return;
361 }
362
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500363 if (wTime.TN() > 7) {
364 LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
365 return;
366 }
367
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800368 /* Use the number of bits as the EDGE burst indicator */
369 if (bits.size() == EDGE_BURST_NBITS)
370 burst = modulateEdgeBurst(bits, mSPSTx);
371 else
372 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
373
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500374 scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
dburgessb3a0ca42011-10-12 07:44:40 +0000375
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500376 radio_burst = new radioVector(wTime, burst);
377
378 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000379}
380
Thomas Tsou15d743e2014-01-25 02:34:03 -0500381void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
382{
383 int TN, modFN;
384 TransceiverState *state = &mStates[chan];
385
386 TN = burst->getTime().TN();
387 modFN = burst->getTime().FN() % state->fillerModulus[TN];
388
389 delete state->fillerTable[modFN][TN];
390 state->fillerTable[modFN][TN] = burst->getVector();
391 burst->setVector(NULL);
392}
393
dburgessb3a0ca42011-10-12 07:44:40 +0000394void Transceiver::pushRadioVector(GSM::Time &nowTime)
395{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400396 int TN, modFN;
397 radioVector *burst;
398 TransceiverState *state;
399 std::vector<signalVector *> bursts(mChans);
400 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500401 std::vector<bool> filler(mChans, true);
dburgessb3a0ca42011-10-12 07:44:40 +0000402
Thomas Tsou204a9f12013-10-29 18:34:16 -0400403 for (size_t i = 0; i < mChans; i ++) {
404 state = &mStates[i];
dburgessb3a0ca42011-10-12 07:44:40 +0000405
Thomas Tsou204a9f12013-10-29 18:34:16 -0400406 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200407 LOGCHAN(i, DMAIN, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
Pau Espin Pedrolf37b0ad2018-04-25 18:01:27 +0200408 << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500409 if (state->mRetrans)
410 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500411 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400412 }
413
414 TN = nowTime.TN();
415 modFN = nowTime.FN() % state->fillerModulus[TN];
416
417 bursts[i] = state->fillerTable[modFN][TN];
418 zeros[i] = state->chanType[TN] == NONE;
419
420 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500421 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500422
423 if (state->mRetrans) {
424 updateFillerTable(i, burst);
425 } else {
426 burst->setVector(NULL);
427 filler[i] = false;
428 }
429
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500430 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400431 }
dburgessb3a0ca42011-10-12 07:44:40 +0000432 }
433
Thomas Tsou204a9f12013-10-29 18:34:16 -0400434 mRadioInterface->driveTransmitRadio(bursts, zeros);
435
Thomas Tsou15d743e2014-01-25 02:34:03 -0500436 for (size_t i = 0; i < mChans; i++) {
437 if (!filler[i])
438 delete bursts[i];
439 }
dburgessb3a0ca42011-10-12 07:44:40 +0000440}
441
Thomas Tsou204a9f12013-10-29 18:34:16 -0400442void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000443{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400444 TransceiverState *state = &mStates[chan];
445
446 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000447 case NONE:
448 case I:
449 case II:
450 case III:
451 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400452 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000453 break;
454 case IV:
455 case VI:
456 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400457 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000458 break;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200459 //case V:
dburgessb3a0ca42011-10-12 07:44:40 +0000460 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400461 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000462 break;
ttsoufc40a842013-06-09 22:38:18 +0000463 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400464 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000465 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000466 default:
467 break;
468 }
469}
470
471
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700472CorrType Transceiver::expectedCorrType(GSM::Time currTime,
473 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000474{
Alexander Chemeris5a068062015-06-20 01:38:47 +0300475 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 };
476 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,
477 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 };
478 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,
479 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 -0400480 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000481 unsigned burstTN = currTime.TN();
482 unsigned burstFN = currTime.FN();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300483 int subch;
dburgessb3a0ca42011-10-12 07:44:40 +0000484
Thomas Tsou204a9f12013-10-29 18:34:16 -0400485 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000486 case NONE:
487 return OFF;
488 break;
489 case FILL:
490 return IDLE;
491 break;
492 case I:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300493 // TODO: Are we expecting RACH on an IDLE frame?
494/* if (burstFN % 26 == 25)
495 return IDLE;*/
496 if (mHandover[burstTN][0])
497 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000498 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000499 break;
500 case II:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300501 subch = tchh_subslot[burstFN % 26];
502 if (subch == 1)
503 return IDLE;
504 if (mHandover[burstTN][0])
505 return RACH;
ttsou20642972013-03-27 22:00:25 +0000506 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000507 break;
508 case III:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300509 subch = tchh_subslot[burstFN % 26];
510 if (mHandover[burstTN][subch])
511 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000512 return TSC;
513 break;
514 case IV:
515 case VI:
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200516 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000517 break;
518 case V: {
519 int mod51 = burstFN % 51;
520 if ((mod51 <= 36) && (mod51 >= 14))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200521 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000522 else if ((mod51 == 4) || (mod51 == 5))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200523 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000524 else if ((mod51 == 45) || (mod51 == 46))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200525 return mExtRACH ? EXT_RACH : RACH;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300526 else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
527 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000528 else
529 return TSC;
530 break;
531 }
532 case VII:
533 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
534 return IDLE;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300535 else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
536 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000537 else
538 return TSC;
539 break;
ttsoufc40a842013-06-09 22:38:18 +0000540 case XIII: {
541 int mod52 = burstFN % 52;
542 if ((mod52 == 12) || (mod52 == 38))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200543 return mExtRACH ? EXT_RACH : RACH;
ttsoufc40a842013-06-09 22:38:18 +0000544 else if ((mod52 == 25) || (mod52 == 51))
545 return IDLE;
546 else
547 return TSC;
548 break;
549 }
dburgessb3a0ca42011-10-12 07:44:40 +0000550 case LOOPBACK:
551 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
552 return IDLE;
553 else
554 return TSC;
555 break;
556 default:
557 return OFF;
558 break;
559 }
dburgessb3a0ca42011-10-12 07:44:40 +0000560}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400561
Alexander Chemerise692ce92015-06-12 00:15:31 -0400562void writeToFile(radioVector *radio_burst, size_t chan)
563{
564 GSM::Time time = radio_burst->getTime();
565 std::ostringstream fname;
566 fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
567 std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
568 outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
569 outfile.close();
570}
571
Thomas Tsou30421a72013-11-13 23:14:48 -0500572/*
573 * Pull bursts from the FIFO and handle according to the slot
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200574 * and burst correlation type. Equalzation is currently disabled.
Thomas Tsou30421a72013-11-13 23:14:48 -0500575 */
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200576bool Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
dburgessb3a0ca42011-10-12 07:44:40 +0000577{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800578 int rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200579 struct estim_burst_params ebp;
580 float max = -1.0, avg = 0.0;
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200581 unsigned max_toa;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500582 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500583 signalVector *burst;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200584 GSM::Time burstTime;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200585 SoftVector *rxBurst;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500586 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000587
Thomas Tsou30421a72013-11-13 23:14:48 -0500588 /* Blocking FIFO read */
589 radioVector *radio_burst = mReceiveFIFO[chan]->read();
590 if (!radio_burst)
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200591 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000592
Thomas Tsou30421a72013-11-13 23:14:48 -0500593 /* Set time and determine correlation type */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200594 burstTime = radio_burst->getTime();
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200595 CorrType type = expectedCorrType(burstTime, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000596
Tom Tsou64464e62016-07-01 03:46:46 -0700597 /* Enable 8-PSK burst detection if EDGE is enabled */
598 if (mEdge && (type == TSC))
599 type = EDGE;
600
Alexander Chemerise692ce92015-06-12 00:15:31 -0400601 /* Debug: dump bursts to disk */
602 /* bits 0-7 - chan 0 timeslots
603 * bits 8-15 - chan 1 timeslots */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200604 if (mWriteBurstToDiskMask & ((1<<bi->tn) << (8*chan)))
Alexander Chemerise692ce92015-06-12 00:15:31 -0400605 writeToFile(radio_burst, chan);
606
Alexander Chemeris2b542102015-06-08 22:46:38 -0400607 /* No processing if the timeslot is off.
608 * Not even power level or noise calculation. */
609 if (type == OFF) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500610 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200611 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000612 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000613
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200614 /* Initialize struct bi */
615 bi->nbits = 0;
616 bi->fn = burstTime.FN();
617 bi->tn = burstTime.TN();
618 bi->rssi = 0.0;
619 bi->toa = 0.0;
620 bi->noise = 0.0;
621 bi->idle = false;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200622 bi->modulation = MODULATION_GMSK;
623 bi->tss = 0; /* TODO: we only support tss 0 right now */
624 bi->tsc = 0;
625 bi->ci = 0.0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200626
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500627 /* Select the diversity channel with highest energy */
628 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300629 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500630 if (pow > max) {
631 max = pow;
632 max_i = i;
633 }
634 avg += pow;
635 }
636
637 if (max_i < 0) {
638 LOG(ALERT) << "Received empty burst";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200639 goto ret_idle;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500640 }
641
Thomas Tsou30421a72013-11-13 23:14:48 -0500642 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500643 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500644 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400645
Alexander Chemeris2b542102015-06-08 22:46:38 -0400646 if (type == IDLE) {
647 /* Update noise levels */
648 state->mNoises.insert(avg);
649 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200650 }
Alexander Chemeris2b542102015-06-08 22:46:38 -0400651
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200652 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
653 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
654
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200655 if (type == IDLE)
656 goto ret_idle;
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400657
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200658 max_toa = (type == RACH || type == EXT_RACH) ?
659 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200660
Thomas Tsou30421a72013-11-13 23:14:48 -0500661 /* Detect normal or RACH bursts */
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200662 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, max_toa, &ebp);
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200663 if (rc <= 0) {
664 if (rc == -SIGERR_CLIP)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400665 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200666 else if (rc != SIGERR_NONE)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400667 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200668 goto ret_idle;
dburgessb3a0ca42011-10-12 07:44:40 +0000669 }
dburgessb3a0ca42011-10-12 07:44:40 +0000670
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200671 type = (CorrType) rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200672 bi->toa = ebp.toa;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200673 bi->tsc = ebp.tsc;
674 bi->ci = ebp.ci;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200675 rxBurst = demodAnyBurst(*burst, mSPSRx, ebp.amp, ebp.toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500676
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200677 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200678 if (rxBurst->size() == EDGE_BURST_NBITS) {
679 bi->modulation = MODULATION_8PSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200680 bi->nbits = EDGE_BURST_NBITS;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200681 } else { /* size() here is actually gSlotLen + 8, due to guard periods */
682 bi->modulation = MODULATION_GMSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200683 bi->nbits = gSlotLen;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200684 }
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200685
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200686 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200687 vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200688
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200689 delete rxBurst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500690 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200691 return true;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200692
693ret_idle:
694 bi->idle = true;
695 delete radio_burst;
696 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000697}
698
dburgessb3a0ca42011-10-12 07:44:40 +0000699void Transceiver::reset()
700{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400701 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
702 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000703}
704
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200705
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700706#define MAX_PACKET_LENGTH 100
707
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700708/**
709 * Matches a buffer with a command.
710 * @param buf a buffer to look command in
711 * @param cmd a command to look in buffer
712 * @param params pointer to arguments, or NULL
713 * @return true if command matches, otherwise false
714 */
715static bool match_cmd(char *buf,
716 const char *cmd, char **params)
717{
718 size_t cmd_len = strlen(cmd);
719
720 /* Check a command itself */
721 if (strncmp(buf, cmd, cmd_len))
722 return false;
723
724 /* A command has arguments */
725 if (params != NULL) {
726 /* Make sure there is a space */
727 if (buf[cmd_len] != ' ')
728 return false;
729
730 /* Update external pointer */
731 *params = buf + cmd_len + 1;
732 }
733
734 return true;
735}
736
Thomas Tsou204a9f12013-10-29 18:34:16 -0400737void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000738{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700739 char buffer[MAX_PACKET_LENGTH + 1];
740 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700741 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700742 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400743
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700744 /* Attempt to read from control socket */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200745 msgLen = read(mCtrlSockets[chan], buffer, MAX_PACKET_LENGTH);
746 if (msgLen <= 0) {
747 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000748 return;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200749 }
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700750
751 /* Zero-terminate received string */
752 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000753
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700754 /* Verify a command signature */
755 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100756 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000757 return;
758 }
dburgessb3a0ca42011-10-12 07:44:40 +0000759
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700760 /* Set command pointer */
761 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200762 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700763
764 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800765 stop();
766 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700767 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700768 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000769 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700770 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000771 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300772 for (int i = 0; i < 8; i++) {
773 for (int j = 0; j < 8; j++)
774 mHandover[i][j] = false;
775 }
Tom Tsou365bc382016-10-19 15:26:04 -0700776 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700777 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700778 unsigned ts = 0, ss = 0;
779 sscanf(params, "%u %u", &ts, &ss);
780 if (ts > 7 || ss > 7) {
781 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
782 } else {
783 mHandover[ts][ss] = true;
784 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
785 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700786 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700787 unsigned ts = 0, ss = 0;
788 sscanf(params, "%u %u", &ts, &ss);
789 if (ts > 7 || ss > 7) {
790 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
791 } else {
792 mHandover[ts][ss] = false;
793 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
794 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700795 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000796 //set expected maximum time-of-arrival
797 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700798 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300799 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000800 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700801 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300802 //set expected maximum time-of-arrival
803 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700804 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300805 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
806 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700807 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000808 //set expected maximum time-of-arrival
809 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700810 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400811 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000812 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700813 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000814 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500815 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000816 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500817 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000818 }
819 else {
820 sprintf(response,"RSP NOISELEV 1 0");
821 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700822 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800823 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700824 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800825 power = mRadioInterface->setPowerAttenuation(power, chan);
826 mStates[chan].mPower = power;
827 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700828 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800829 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700830 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800831 power = mStates[chan].mPower + step;
832 power = mRadioInterface->setPowerAttenuation(power, chan);
833 mStates[chan].mPower = power;
834 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700835 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000836 // tune receiver
837 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700838 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500839 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400840 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100841 LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000842 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
843 }
844 else
845 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700846 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000847 // tune txmtr
848 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700849 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500850 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400851 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100852 LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000853 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
854 }
855 else
856 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700857 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000858 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500859 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700860 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700861 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500862 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700863 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100864 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000865 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400866 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000867 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700868 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700869 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000870 int corrCode;
871 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700872 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000873 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100874 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000875 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
876 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200877 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400878 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
879 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000880 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200881 } else if (match_cmd(command, "SETFORMAT", &params)) {
882 // set TRXD protocol version
883 unsigned version_recv;
884 sscanf(params, "%u", &version_recv);
885 LOGC(DTRXCTRL, INFO) << "BTS requests TRXD version switch: " << version_recv;
886 if (version_recv > TRX_DATA_FORMAT_VER) {
887 LOGC(DTRXCTRL, INFO) << "rejecting TRXD version " << version_recv
888 << "in favor of " << TRX_DATA_FORMAT_VER;
889 sprintf(response, "RSP SETFORMAT %u %u", TRX_DATA_FORMAT_VER, version_recv);
890 } else {
891 LOGC(DTRXCTRL, NOTICE) << "switching to TRXD version " << version_recv;
892 mVersionTRXD = version_recv;
893 sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
894 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700895 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400896 // debug command! may change or disapear without notice
897 // set a mask which bursts to dump to disk
898 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700899 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400900 mWriteBurstToDiskMask = mask;
901 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700902 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100903 LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200904 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000905 }
906
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200907 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200908 msgLen = write(mCtrlSockets[chan], response, strlen(response) + 1);
909 if (msgLen <= 0)
910 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000911}
912
Thomas Tsou204a9f12013-10-29 18:34:16 -0400913bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000914{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200915 int msgLen;
Tom Tsoue8871082016-07-01 02:46:04 -0700916 int burstLen;
917 char buffer[EDGE_BURST_NBITS + 50];
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700918 struct trxd_hdr_common *chdr;
919 uint32_t fn;
dburgessb3a0ca42011-10-12 07:44:40 +0000920
921 // check data socket
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200922 msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
923 if (msgLen <= 0) {
924 LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
925 return false;
926 }
dburgessb3a0ca42011-10-12 07:44:40 +0000927
Tom Tsoue8871082016-07-01 02:46:04 -0700928 if (msgLen == gSlotLen + 1 + 4 + 1) {
929 burstLen = gSlotLen;
930 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
931 if (mSPSTx != 4)
932 return false;
933
934 burstLen = EDGE_BURST_NBITS;
935 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000936 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
937 return false;
938 }
939
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700940 /* Common header part: HDR version, TDMA TN & FN */
941 chdr = (struct trxd_hdr_common *) buffer;
dburgessb3a0ca42011-10-12 07:44:40 +0000942
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700943 /* Convert TDMA FN to the host endianness */
944 fn = osmo_load32be(&chdr->fn);
945
Vadim Yanitskiydd571c62019-07-15 23:56:56 +0700946 /* Make sure we support the received header format */
947 switch (chdr->version) {
948 case 0:
949 /* Version 1 has the same format */
950 case 1:
951 break;
952
953 default:
954 LOG(ERR) << "Rx TRXD message with unknown header version " << chdr->version;
955 return false;
956 }
957
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700958 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(fn, chdr->tn);
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200959
dburgessb3a0ca42011-10-12 07:44:40 +0000960 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700961 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000962 BitVector::iterator itr = newBurst.begin();
963 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200964 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000965 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200966
Vadim Yanitskiyb3123252019-07-15 23:53:08 +0700967 GSM::Time currTime = GSM::Time(fn, chdr->tn);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400968
969 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000970
971 return true;
972
973
974}
dburgessb3a0ca42011-10-12 07:44:40 +0000975
Thomas Tsou204a9f12013-10-29 18:34:16 -0400976void Transceiver::driveReceiveRadio()
977{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200978 int rc = mRadioInterface->driveReceiveRadio();
979 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400980 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200981 } else if (rc < 0) {
982 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
Pau Espin Pedrolb426e4a2019-06-04 12:39:28 +0200983 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200984 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
985 mForceClockInterface = false;
986 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -0400987 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400988}
989
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200990void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800991{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200992 std::ostringstream os;
993 for (size_t i=0; i < bi->nbits; i++) {
994 if (bi->rx_burst[i] > 0.5) os << "1";
995 else if (bi->rx_burst[i] > 0.25) os << "|";
996 else if (bi->rx_burst[i] > 0.0) os << "'";
997 else os << "-";
998 }
999
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001000 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +03001001 << " chan: " << chan
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +02001002 << " time: " << bi->tn << ":" << bi->fn
Pau Espin Pedrol607a4142019-07-01 13:56:17 +02001003 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
1004 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
1005 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
1006 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001007 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001008 << " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB"
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +02001009 << " bits: " << os;
Tom Tsoub0aefcb2016-03-06 03:44:34 -08001010}
1011
Thomas Tsou204a9f12013-10-29 18:34:16 -04001012void Transceiver::driveReceiveFIFO(size_t chan)
1013{
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001014 struct trx_ul_burst_ind bi;
1015
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001016 if (!pullRadioVector(chan, &bi))
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001017 return;
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001018 if (!bi.idle)
1019 logRxBurst(chan, &bi);
dburgessb3a0ca42011-10-12 07:44:40 +00001020
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001021 switch (mVersionTRXD) {
1022 case 0:
1023 trxd_send_burst_ind_v0(chan, mDataSockets[chan], &bi);
1024 break;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001025 case 1:
1026 trxd_send_burst_ind_v1(chan, mDataSockets[chan], &bi);
1027 break;
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001028 default:
1029 OSMO_ASSERT(false);
1030 }
dburgessb3a0ca42011-10-12 07:44:40 +00001031}
1032
Thomas Tsou204a9f12013-10-29 18:34:16 -04001033void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +00001034{
1035
1036 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001037 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +00001038 assure that transmit packets arrive at the radio/USRP
1039 before they need to be transmitted.
1040
1041 Deadline clock indicates the burst that needs to be
1042 pushed into the FIFO right NOW. If transmit queue does
1043 not have a burst, stick in filler data.
1044 */
1045
1046
1047 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001048
dburgessb3a0ca42011-10-12 07:44:40 +00001049 if (mOn) {
1050 //radioClock->wait(); // wait until clock updates
1051 LOG(DEBUG) << "radio clock " << radioClock->get();
1052 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1053 // if underrun, then we're not providing bursts to radio/USRP fast
1054 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001055 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001056 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001057 // only update latency at the defined frame interval
1058 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001059 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol74bcc562018-10-02 17:30:28 +02001060 LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
1061 << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001062 mLatencyUpdateTime = radioClock->get();
1063 }
1064 }
1065 else {
1066 // if underrun hasn't occurred in the last sec (216 frames) drop
1067 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001068 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001069 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1070 mTransmitLatency.decTN();
1071 LOG(INFO) << "reduced latency: " << mTransmitLatency;
1072 mLatencyUpdateTime = radioClock->get();
1073 }
1074 }
1075 }
dburgessb3a0ca42011-10-12 07:44:40 +00001076 }
dburgessb3a0ca42011-10-12 07:44:40 +00001077 // time to push burst to transmit FIFO
1078 pushRadioVector(mTransmitDeadlineClock);
1079 mTransmitDeadlineClock.incTN();
1080 }
dburgessb3a0ca42011-10-12 07:44:40 +00001081 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001082
1083 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001084}
1085
1086
1087
1088void Transceiver::writeClockInterface()
1089{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001090 int msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001091 char command[50];
1092 // FIXME -- This should be adaptive.
1093 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1094
1095 LOG(INFO) << "ClockInterface: sending " << command;
1096
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001097 msgLen = write(mClockSocket, command, strlen(command) + 1);
1098 if (msgLen <= 0)
1099 LOG(WARNING) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001100
1101 mLastClockUpdateTime = mTransmitDeadlineClock;
1102
Thomas Tsou92c16df2013-09-28 18:04:19 -04001103}
dburgessb3a0ca42011-10-12 07:44:40 +00001104
Thomas Tsou204a9f12013-10-29 18:34:16 -04001105void *RxUpperLoopAdapter(TransceiverChannel *chan)
1106{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001107 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001108 Transceiver *trx = chan->trx;
1109 size_t num = chan->num;
1110
1111 delete chan;
1112
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001113 snprintf(thread_name, 16, "RxUpper%zu", num);
1114 set_selfthread_name(thread_name);
1115
Thomas Tsou7553aa92013-11-08 12:50:03 -05001116 trx->setPriority(0.42);
1117
Thomas Tsou204a9f12013-10-29 18:34:16 -04001118 while (1) {
1119 trx->driveReceiveFIFO(num);
1120 pthread_testcancel();
1121 }
1122 return NULL;
1123}
1124
1125void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001126{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001127 set_selfthread_name("RxLower");
1128
Thomas Tsou7553aa92013-11-08 12:50:03 -05001129 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +00001130
dburgessb3a0ca42011-10-12 07:44:40 +00001131 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001132 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001133 pthread_testcancel();
1134 }
1135 return NULL;
1136}
1137
Thomas Tsou204a9f12013-10-29 18:34:16 -04001138void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001139{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001140 set_selfthread_name("TxLower");
1141
Thomas Tsou7553aa92013-11-08 12:50:03 -05001142 transceiver->setPriority(0.44);
1143
Thomas Tsou92c16df2013-09-28 18:04:19 -04001144 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001145 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001146 pthread_testcancel();
1147 }
1148 return NULL;
1149}
1150
Thomas Tsou204a9f12013-10-29 18:34:16 -04001151void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001152{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001153 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001154 Transceiver *trx = chan->trx;
1155 size_t num = chan->num;
1156
1157 delete chan;
1158
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001159 snprintf(thread_name, 16, "CtrlService%zu", num);
1160 set_selfthread_name(thread_name);
1161
dburgessb3a0ca42011-10-12 07:44:40 +00001162 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001163 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001164 pthread_testcancel();
1165 }
1166 return NULL;
1167}
1168
Thomas Tsou204a9f12013-10-29 18:34:16 -04001169void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001170{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001171 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001172 Transceiver *trx = chan->trx;
1173 size_t num = chan->num;
1174
1175 delete chan;
1176
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001177 snprintf(thread_name, 16, "TxUpper%zu", num);
1178 set_selfthread_name(thread_name);
1179
Thomas Tsoua4cf48c2013-11-09 21:44:26 -05001180 trx->setPriority(0.40);
1181
dburgessb3a0ca42011-10-12 07:44:40 +00001182 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001183 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001184 pthread_testcancel();
1185 }
1186 return NULL;
1187}