blob: 92d40619d665314efbe924d49be0237b2a932ff0 [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
34#include <osmocom/core/bits.h>
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +020035#include <osmocom/core/socket.h>
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020036}
37
ttsou2173abf2012-08-08 00:51:31 +000038#ifdef HAVE_CONFIG_H
39#include "config.h"
40#endif
dburgessb3a0ca42011-10-12 07:44:40 +000041
Alexander Chemerisd734e2d2013-06-16 14:30:58 +040042using namespace GSM;
43
kurtis.heimerlec842de2012-11-23 08:37:32 +000044#define USB_LATENCY_INTRVL 10,0
ttsou2173abf2012-08-08 00:51:31 +000045
Thomas Tsoufa3a7872013-10-17 21:23:34 -040046/* Number of running values use in noise average */
47#define NOISE_CNT 20
ttsoue8dde022012-12-06 15:43:55 +000048
Thomas Tsouf0782732013-10-29 15:55:47 -040049TransceiverState::TransceiverState()
Tom Tsoua4d1a412014-11-25 15:46:56 -080050 : mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
Thomas Tsouf0782732013-10-29 15:55:47 -040051{
52 for (int i = 0; i < 8; i++) {
53 chanType[i] = Transceiver::NONE;
54 fillerModulus[i] = 26;
55 chanResponse[i] = NULL;
56 DFEForward[i] = NULL;
57 DFEFeedback[i] = NULL;
58
59 for (int n = 0; n < 102; n++)
60 fillerTable[n][i] = NULL;
61 }
62}
63
64TransceiverState::~TransceiverState()
65{
66 for (int i = 0; i < 8; i++) {
67 delete chanResponse[i];
68 delete DFEForward[i];
69 delete DFEFeedback[i];
70
71 for (int n = 0; n < 102; n++)
72 delete fillerTable[n][i];
73 }
74}
75
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010076bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
Tom Tsou64ad7122015-05-19 18:26:31 -070077{
Tom Tsou64ad7122015-05-19 18:26:31 -070078 signalVector *burst;
79
80 if ((sps != 1) && (sps != 4))
81 return false;
82
83 for (size_t n = 0; n < 8; n++) {
Tom Tsou64ad7122015-05-19 18:26:31 -070084 for (size_t i = 0; i < 102; i++) {
85 switch (filler) {
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010086 case FILLER_DUMMY:
Tom Tsou8ee2f382016-03-06 20:57:34 -080087 burst = generateDummyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070088 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010089 case FILLER_NORM_RAND:
Tom Tsou8ee2f382016-03-06 20:57:34 -080090 burst = genRandNormalBurst(rtsc, sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070091 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010092 case FILLER_EDGE_RAND:
Tom Tsouaf717b22016-03-06 22:19:15 -080093 burst = generateEdgeBurst(rtsc);
94 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010095 case FILLER_ACCESS_RAND:
Alexander Chemeris37c52c72016-03-25 18:28:34 +030096 burst = genRandAccessBurst(rach_delay, sps, n);
Alexander Chemeris5efe0502016-03-23 17:06:32 +030097 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010098 case FILLER_ZERO:
Tom Tsou64ad7122015-05-19 18:26:31 -070099 default:
Tom Tsou8ee2f382016-03-06 20:57:34 -0800100 burst = generateEmptyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -0700101 }
102
103 scaleVector(*burst, scale);
104 fillerTable[i][n] = burst;
105 }
106
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100107 if ((filler == FILLER_NORM_RAND) ||
108 (filler == FILLER_EDGE_RAND)) {
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700109 chanType[n] = TSC;
Tom Tsouaf717b22016-03-06 22:19:15 -0800110 }
Thomas Tsou15d743e2014-01-25 02:34:03 -0500111 }
Tom Tsou64ad7122015-05-19 18:26:31 -0700112
113 return false;
Thomas Tsouf0782732013-10-29 15:55:47 -0400114}
115
dburgessb3a0ca42011-10-12 07:44:40 +0000116Transceiver::Transceiver(int wBasePort,
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200117 const char *TRXAddress,
118 const char *GSMcoreAddress,
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800119 size_t tx_sps, size_t rx_sps, size_t chans,
Alexander Chemerise8905a02015-06-03 23:47:56 -0400120 GSM::Time wTransmitLatency,
121 RadioInterface *wRadioInterface,
Eric Wildac0487e2019-06-17 13:02:44 +0200122 double wRssiOffset, int wStackSize)
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200123 : mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200124 mClockSocket(-1), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
Eric Wildac0487e2019-06-17 13:02:44 +0200125 rssiOffset(wRssiOffset), stackSize(wStackSize),
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200126 mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false),
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300127 mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
128 mWriteBurstToDiskMask(0)
dburgessb3a0ca42011-10-12 07:44:40 +0000129{
dburgessb3a0ca42011-10-12 07:44:40 +0000130 txFullScale = mRadioInterface->fullScaleInputValue();
131 rxFullScale = mRadioInterface->fullScaleOutputValue();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300132
133 for (int i = 0; i < 8; i++) {
134 for (int j = 0; j < 8; j++)
135 mHandover[i][j] = false;
136 }
dburgessb3a0ca42011-10-12 07:44:40 +0000137}
138
139Transceiver::~Transceiver()
140{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800141 stop();
142
dburgessb3a0ca42011-10-12 07:44:40 +0000143 sigProcLibDestroy();
Thomas Tsoud647ec52013-10-29 15:17:34 -0400144
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200145 if (mClockSocket >= 0)
146 close(mClockSocket);
147
Thomas Tsou204a9f12013-10-29 18:34:16 -0400148 for (size_t i = 0; i < mChans; i++) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800149 mControlServiceLoopThreads[i]->cancel();
150 mControlServiceLoopThreads[i]->join();
151 delete mControlServiceLoopThreads[i];
152
Thomas Tsou204a9f12013-10-29 18:34:16 -0400153 mTxPriorityQueues[i].clear();
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200154 if (mCtrlSockets[i] >= 0)
155 close(mCtrlSockets[i]);
156 if (mDataSockets[i] >= 0)
157 close(mDataSockets[i]);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400158 }
dburgessb3a0ca42011-10-12 07:44:40 +0000159}
Thomas Tsou83e06892013-08-20 16:10:01 -0400160
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800161/*
162 * Initialize transceiver
163 *
164 * Start or restart the control loop. Any further control is handled through the
165 * socket API. Randomize the central radio clock set the downlink burst
166 * counters. Note that the clock will not update until the radio starts, but we
167 * are still expected to report clock indications through control channel
168 * activity.
169 */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200170bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
171 bool edge, bool ext_rach)
Thomas Tsou83e06892013-08-20 16:10:01 -0400172{
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500173 int d_srcport, d_dstport, c_srcport, c_dstport;
Thomas Tsouf0782732013-10-29 15:55:47 -0400174
Thomas Tsou204a9f12013-10-29 18:34:16 -0400175 if (!mChans) {
176 LOG(ALERT) << "No channels assigned";
177 return false;
178 }
179
Tom Tsou2079a3c2016-03-06 00:58:56 -0800180 if (!sigProcLibSetup()) {
Thomas Tsou83e06892013-08-20 16:10:01 -0400181 LOG(ALERT) << "Failed to initialize signal processing library";
182 return false;
183 }
184
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200185 mExtRACH = ext_rach;
Tom Tsou64464e62016-07-01 03:46:46 -0700186 mEdge = edge;
187
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200188 mDataSockets.resize(mChans, -1);
189 mCtrlSockets.resize(mChans, -1);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400190 mControlServiceLoopThreads.resize(mChans);
191 mTxPriorityQueueServiceLoopThreads.resize(mChans);
192 mRxServiceLoopThreads.resize(mChans);
193
194 mTxPriorityQueues.resize(mChans);
195 mReceiveFIFO.resize(mChans);
196 mStates.resize(mChans);
197
Thomas Tsouccb73e12014-04-15 17:41:28 -0400198 /* Filler table retransmissions - support only on channel 0 */
Tom Tsou64ad7122015-05-19 18:26:31 -0700199 if (filler == FILLER_DUMMY)
Thomas Tsouccb73e12014-04-15 17:41:28 -0400200 mStates[0].mRetrans = true;
201
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800202 /* Setup sockets */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200203 mClockSocket = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
204 mLocalAddr.c_str(), mBasePort,
205 mRemoteAddr.c_str(), mBasePort + 100,
206 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
207
Thomas Tsou204a9f12013-10-29 18:34:16 -0400208 for (size_t i = 0; i < mChans; i++) {
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500209 c_srcport = mBasePort + 2 * i + 1;
210 c_dstport = mBasePort + 2 * i + 101;
211 d_srcport = mBasePort + 2 * i + 2;
212 d_dstport = mBasePort + 2 * i + 102;
213
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200214 mCtrlSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
215 mLocalAddr.c_str(), c_srcport,
216 mRemoteAddr.c_str(), c_dstport,
217 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
218 if (mCtrlSockets[i] < 0)
219 return false;
220
221 mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
222 mLocalAddr.c_str(), d_srcport,
223 mRemoteAddr.c_str(), d_dstport,
224 OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
225 if (mCtrlSockets[i] < 0)
226 return false;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400227 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400228
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800229 /* Randomize the central clock */
230 GSM::Time startTime(random() % gHyperframe, 0);
231 mRadioInterface->getClock()->set(startTime);
232 mTransmitDeadlineClock = startTime;
233 mLastClockUpdateTime = startTime;
234 mLatencyUpdateTime = startTime;
235
236 /* Start control threads */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400237 for (size_t i = 0; i < mChans; i++) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800238 TransceiverChannel *chan = new TransceiverChannel(this, i);
Eric Wildac0487e2019-06-17 13:02:44 +0200239 mControlServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800240 mControlServiceLoopThreads[i]->start((void * (*)(void*))
241 ControlServiceLoopAdapter, (void*) chan);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400242
Tom Tsou64ad7122015-05-19 18:26:31 -0700243 if (i && filler == FILLER_DUMMY)
244 filler = FILLER_ZERO;
245
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300246 mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
Thomas Tsou83e06892013-08-20 16:10:01 -0400247 }
248
249 return true;
250}
dburgessb3a0ca42011-10-12 07:44:40 +0000251
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800252/*
253 * Start the transceiver
254 *
255 * Submit command(s) to the radio device to commence streaming samples and
256 * launch threads to handle sample I/O. Re-synchronize the transmit burst
257 * counters to the central radio clock here as well.
258 */
259bool Transceiver::start()
260{
261 ScopedLock lock(mLock);
262
263 if (mOn) {
264 LOG(ERR) << "Transceiver already running";
Ivan Kluchnikov194a9b12015-04-23 17:08:27 +0300265 return true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800266 }
267
268 LOG(NOTICE) << "Starting the transceiver";
269
270 GSM::Time time = mRadioInterface->getClock()->get();
271 mTransmitDeadlineClock = time;
272 mLastClockUpdateTime = time;
273 mLatencyUpdateTime = time;
274
275 if (!mRadioInterface->start()) {
276 LOG(ALERT) << "Device failed to start";
277 return false;
278 }
279
280 /* Device is running - launch I/O threads */
Eric Wildac0487e2019-06-17 13:02:44 +0200281 mRxLowerLoopThread = new Thread(stackSize);
282 mTxLowerLoopThread = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800283 mTxLowerLoopThread->start((void * (*)(void*))
284 TxLowerLoopAdapter,(void*) this);
285 mRxLowerLoopThread->start((void * (*)(void*))
286 RxLowerLoopAdapter,(void*) this);
287
288 /* Launch uplink and downlink burst processing threads */
289 for (size_t i = 0; i < mChans; i++) {
290 TransceiverChannel *chan = new TransceiverChannel(this, i);
Eric Wildac0487e2019-06-17 13:02:44 +0200291 mRxServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800292 mRxServiceLoopThreads[i]->start((void * (*)(void*))
293 RxUpperLoopAdapter, (void*) chan);
294
295 chan = new TransceiverChannel(this, i);
Eric Wildac0487e2019-06-17 13:02:44 +0200296 mTxPriorityQueueServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800297 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
298 TxUpperLoopAdapter, (void*) chan);
299 }
300
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200301 mForceClockInterface = true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800302 mOn = true;
303 return true;
304}
305
306/*
307 * Stop the transceiver
308 *
309 * Perform stopping by disabling receive streaming and issuing cancellation
310 * requests to running threads. Most threads will timeout and terminate once
311 * device is disabled, but the transmit loop may block waiting on the central
312 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
313 * makes it to the thread cancellation point.
314 */
315void Transceiver::stop()
316{
317 ScopedLock lock(mLock);
318
319 if (!mOn)
320 return;
321
322 LOG(NOTICE) << "Stopping the transceiver";
323 mTxLowerLoopThread->cancel();
324 mRxLowerLoopThread->cancel();
Tom Tsoud67bd602017-06-15 15:35:02 -0700325 mTxLowerLoopThread->join();
326 mRxLowerLoopThread->join();
327 delete mTxLowerLoopThread;
328 delete mRxLowerLoopThread;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800329
330 for (size_t i = 0; i < mChans; i++) {
331 mRxServiceLoopThreads[i]->cancel();
332 mTxPriorityQueueServiceLoopThreads[i]->cancel();
333 }
334
335 LOG(INFO) << "Stopping the device";
336 mRadioInterface->stop();
337
338 for (size_t i = 0; i < mChans; i++) {
339 mRxServiceLoopThreads[i]->join();
340 mTxPriorityQueueServiceLoopThreads[i]->join();
341 delete mRxServiceLoopThreads[i];
342 delete mTxPriorityQueueServiceLoopThreads[i];
343
344 mTxPriorityQueues[i].clear();
345 }
346
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800347 mOn = false;
348 LOG(NOTICE) << "Transceiver stopped";
349}
350
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500351void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400352 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000353{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500354 signalVector *burst;
355 radioVector *radio_burst;
356
Thomas Tsou204a9f12013-10-29 18:34:16 -0400357 if (chan >= mTxPriorityQueues.size()) {
358 LOG(ALERT) << "Invalid channel " << chan;
359 return;
360 }
361
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500362 if (wTime.TN() > 7) {
363 LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
364 return;
365 }
366
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800367 /* Use the number of bits as the EDGE burst indicator */
368 if (bits.size() == EDGE_BURST_NBITS)
369 burst = modulateEdgeBurst(bits, mSPSTx);
370 else
371 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
372
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500373 scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
dburgessb3a0ca42011-10-12 07:44:40 +0000374
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500375 radio_burst = new radioVector(wTime, burst);
376
377 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000378}
379
Thomas Tsou15d743e2014-01-25 02:34:03 -0500380void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
381{
382 int TN, modFN;
383 TransceiverState *state = &mStates[chan];
384
385 TN = burst->getTime().TN();
386 modFN = burst->getTime().FN() % state->fillerModulus[TN];
387
388 delete state->fillerTable[modFN][TN];
389 state->fillerTable[modFN][TN] = burst->getVector();
390 burst->setVector(NULL);
391}
392
dburgessb3a0ca42011-10-12 07:44:40 +0000393void Transceiver::pushRadioVector(GSM::Time &nowTime)
394{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400395 int TN, modFN;
396 radioVector *burst;
397 TransceiverState *state;
398 std::vector<signalVector *> bursts(mChans);
399 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500400 std::vector<bool> filler(mChans, true);
dburgessb3a0ca42011-10-12 07:44:40 +0000401
Thomas Tsou204a9f12013-10-29 18:34:16 -0400402 for (size_t i = 0; i < mChans; i ++) {
403 state = &mStates[i];
dburgessb3a0ca42011-10-12 07:44:40 +0000404
Thomas Tsou204a9f12013-10-29 18:34:16 -0400405 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200406 LOGCHAN(i, DMAIN, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
Pau Espin Pedrolf37b0ad2018-04-25 18:01:27 +0200407 << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500408 if (state->mRetrans)
409 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500410 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400411 }
412
413 TN = nowTime.TN();
414 modFN = nowTime.FN() % state->fillerModulus[TN];
415
416 bursts[i] = state->fillerTable[modFN][TN];
417 zeros[i] = state->chanType[TN] == NONE;
418
419 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500420 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500421
422 if (state->mRetrans) {
423 updateFillerTable(i, burst);
424 } else {
425 burst->setVector(NULL);
426 filler[i] = false;
427 }
428
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500429 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400430 }
dburgessb3a0ca42011-10-12 07:44:40 +0000431 }
432
Thomas Tsou204a9f12013-10-29 18:34:16 -0400433 mRadioInterface->driveTransmitRadio(bursts, zeros);
434
Thomas Tsou15d743e2014-01-25 02:34:03 -0500435 for (size_t i = 0; i < mChans; i++) {
436 if (!filler[i])
437 delete bursts[i];
438 }
dburgessb3a0ca42011-10-12 07:44:40 +0000439}
440
Thomas Tsou204a9f12013-10-29 18:34:16 -0400441void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000442{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400443 TransceiverState *state = &mStates[chan];
444
445 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000446 case NONE:
447 case I:
448 case II:
449 case III:
450 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400451 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000452 break;
453 case IV:
454 case VI:
455 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400456 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000457 break;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200458 //case V:
dburgessb3a0ca42011-10-12 07:44:40 +0000459 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400460 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000461 break;
ttsoufc40a842013-06-09 22:38:18 +0000462 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400463 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000464 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000465 default:
466 break;
467 }
468}
469
470
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700471CorrType Transceiver::expectedCorrType(GSM::Time currTime,
472 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000473{
Alexander Chemeris5a068062015-06-20 01:38:47 +0300474 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 };
475 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,
476 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 };
477 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,
478 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 -0400479 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000480 unsigned burstTN = currTime.TN();
481 unsigned burstFN = currTime.FN();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300482 int subch;
dburgessb3a0ca42011-10-12 07:44:40 +0000483
Thomas Tsou204a9f12013-10-29 18:34:16 -0400484 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000485 case NONE:
486 return OFF;
487 break;
488 case FILL:
489 return IDLE;
490 break;
491 case I:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300492 // TODO: Are we expecting RACH on an IDLE frame?
493/* if (burstFN % 26 == 25)
494 return IDLE;*/
495 if (mHandover[burstTN][0])
496 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000497 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000498 break;
499 case II:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300500 subch = tchh_subslot[burstFN % 26];
501 if (subch == 1)
502 return IDLE;
503 if (mHandover[burstTN][0])
504 return RACH;
ttsou20642972013-03-27 22:00:25 +0000505 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000506 break;
507 case III:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300508 subch = tchh_subslot[burstFN % 26];
509 if (mHandover[burstTN][subch])
510 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000511 return TSC;
512 break;
513 case IV:
514 case VI:
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200515 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000516 break;
517 case V: {
518 int mod51 = burstFN % 51;
519 if ((mod51 <= 36) && (mod51 >= 14))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200520 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000521 else if ((mod51 == 4) || (mod51 == 5))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200522 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000523 else if ((mod51 == 45) || (mod51 == 46))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200524 return mExtRACH ? EXT_RACH : RACH;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300525 else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
526 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000527 else
528 return TSC;
529 break;
530 }
531 case VII:
532 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
533 return IDLE;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300534 else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
535 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000536 else
537 return TSC;
538 break;
ttsoufc40a842013-06-09 22:38:18 +0000539 case XIII: {
540 int mod52 = burstFN % 52;
541 if ((mod52 == 12) || (mod52 == 38))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200542 return mExtRACH ? EXT_RACH : RACH;
ttsoufc40a842013-06-09 22:38:18 +0000543 else if ((mod52 == 25) || (mod52 == 51))
544 return IDLE;
545 else
546 return TSC;
547 break;
548 }
dburgessb3a0ca42011-10-12 07:44:40 +0000549 case LOOPBACK:
550 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
551 return IDLE;
552 else
553 return TSC;
554 break;
555 default:
556 return OFF;
557 break;
558 }
dburgessb3a0ca42011-10-12 07:44:40 +0000559}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400560
Alexander Chemerise692ce92015-06-12 00:15:31 -0400561void writeToFile(radioVector *radio_burst, size_t chan)
562{
563 GSM::Time time = radio_burst->getTime();
564 std::ostringstream fname;
565 fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
566 std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
567 outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
568 outfile.close();
569}
570
Thomas Tsou30421a72013-11-13 23:14:48 -0500571/*
572 * Pull bursts from the FIFO and handle according to the slot
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200573 * and burst correlation type. Equalzation is currently disabled.
Thomas Tsou30421a72013-11-13 23:14:48 -0500574 */
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200575bool Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
dburgessb3a0ca42011-10-12 07:44:40 +0000576{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800577 int rc;
Thomas Tsou30421a72013-11-13 23:14:48 -0500578 complex amp;
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300579 float toa, max = -1.0, avg = 0.0;
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200580 unsigned max_toa;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500581 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500582 signalVector *burst;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200583 GSM::Time burstTime;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200584 SoftVector *rxBurst;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500585 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000586
Thomas Tsou30421a72013-11-13 23:14:48 -0500587 /* Blocking FIFO read */
588 radioVector *radio_burst = mReceiveFIFO[chan]->read();
589 if (!radio_burst)
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200590 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000591
Thomas Tsou30421a72013-11-13 23:14:48 -0500592 /* Set time and determine correlation type */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200593 burstTime = radio_burst->getTime();
594 bi->fn = burstTime.FN();
595 bi->tn = burstTime.TN();
596 CorrType type = expectedCorrType(burstTime, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000597
Tom Tsou64464e62016-07-01 03:46:46 -0700598 /* Enable 8-PSK burst detection if EDGE is enabled */
599 if (mEdge && (type == TSC))
600 type = EDGE;
601
Alexander Chemerise692ce92015-06-12 00:15:31 -0400602 /* Debug: dump bursts to disk */
603 /* bits 0-7 - chan 0 timeslots
604 * bits 8-15 - chan 1 timeslots */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200605 if (mWriteBurstToDiskMask & ((1<<bi->tn) << (8*chan)))
Alexander Chemerise692ce92015-06-12 00:15:31 -0400606 writeToFile(radio_burst, chan);
607
Alexander Chemeris2b542102015-06-08 22:46:38 -0400608 /* No processing if the timeslot is off.
609 * Not even power level or noise calculation. */
610 if (type == OFF) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500611 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200612 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000613 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000614
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500615 /* Select the diversity channel with highest energy */
616 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300617 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500618 if (pow > max) {
619 max = pow;
620 max_i = i;
621 }
622 avg += pow;
623 }
624
625 if (max_i < 0) {
626 LOG(ALERT) << "Received empty burst";
627 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200628 return false;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500629 }
630
Thomas Tsou30421a72013-11-13 23:14:48 -0500631 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500632 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500633 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400634
Alexander Chemeris2b542102015-06-08 22:46:38 -0400635 if (type == IDLE) {
636 /* Update noise levels */
637 state->mNoises.insert(avg);
638 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200639 }
Alexander Chemeris2b542102015-06-08 22:46:38 -0400640
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200641 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
642 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
643
644 if (type == IDLE) {
Alexander Chemeris2b542102015-06-08 22:46:38 -0400645 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200646 return false;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400647 }
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400648
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200649 max_toa = (type == RACH || type == EXT_RACH) ?
650 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200651
Thomas Tsou30421a72013-11-13 23:14:48 -0500652 /* Detect normal or RACH bursts */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200653 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa, max_toa);
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200654 if (rc <= 0) {
655 if (rc == -SIGERR_CLIP)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400656 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200657 else if (rc != SIGERR_NONE)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400658 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Thomas Tsou30421a72013-11-13 23:14:48 -0500659 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200660 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000661 }
dburgessb3a0ca42011-10-12 07:44:40 +0000662
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200663 type = (CorrType) rc;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200664 bi->toa = toa;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200665 rxBurst = demodAnyBurst(*burst, mSPSRx, amp, toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500666
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200667 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200668 if (rxBurst->size() == EDGE_BURST_NBITS)
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200669 bi->nbits = EDGE_BURST_NBITS;
670 else /* size() here is actually gSlotLen + 8, due to guard periods */
671 bi->nbits = gSlotLen;
672
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200673 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200674 vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200675
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200676 delete rxBurst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500677 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200678 return true;
dburgessb3a0ca42011-10-12 07:44:40 +0000679}
680
dburgessb3a0ca42011-10-12 07:44:40 +0000681void Transceiver::reset()
682{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400683 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
684 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000685}
686
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200687
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700688#define MAX_PACKET_LENGTH 100
689
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700690/**
691 * Matches a buffer with a command.
692 * @param buf a buffer to look command in
693 * @param cmd a command to look in buffer
694 * @param params pointer to arguments, or NULL
695 * @return true if command matches, otherwise false
696 */
697static bool match_cmd(char *buf,
698 const char *cmd, char **params)
699{
700 size_t cmd_len = strlen(cmd);
701
702 /* Check a command itself */
703 if (strncmp(buf, cmd, cmd_len))
704 return false;
705
706 /* A command has arguments */
707 if (params != NULL) {
708 /* Make sure there is a space */
709 if (buf[cmd_len] != ' ')
710 return false;
711
712 /* Update external pointer */
713 *params = buf + cmd_len + 1;
714 }
715
716 return true;
717}
718
Thomas Tsou204a9f12013-10-29 18:34:16 -0400719void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000720{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700721 char buffer[MAX_PACKET_LENGTH + 1];
722 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700723 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700724 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400725
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700726 /* Attempt to read from control socket */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200727 msgLen = read(mCtrlSockets[chan], buffer, MAX_PACKET_LENGTH);
728 if (msgLen <= 0) {
729 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000730 return;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200731 }
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700732
733 /* Zero-terminate received string */
734 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000735
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700736 /* Verify a command signature */
737 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100738 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000739 return;
740 }
dburgessb3a0ca42011-10-12 07:44:40 +0000741
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700742 /* Set command pointer */
743 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200744 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700745
746 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800747 stop();
748 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700749 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700750 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000751 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700752 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000753 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300754 for (int i = 0; i < 8; i++) {
755 for (int j = 0; j < 8; j++)
756 mHandover[i][j] = false;
757 }
Tom Tsou365bc382016-10-19 15:26:04 -0700758 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700759 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700760 unsigned ts = 0, ss = 0;
761 sscanf(params, "%u %u", &ts, &ss);
762 if (ts > 7 || ss > 7) {
763 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
764 } else {
765 mHandover[ts][ss] = true;
766 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
767 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700768 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700769 unsigned ts = 0, ss = 0;
770 sscanf(params, "%u %u", &ts, &ss);
771 if (ts > 7 || ss > 7) {
772 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
773 } else {
774 mHandover[ts][ss] = false;
775 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
776 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700777 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000778 //set expected maximum time-of-arrival
779 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700780 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300781 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000782 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700783 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300784 //set expected maximum time-of-arrival
785 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700786 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300787 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
788 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700789 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000790 //set expected maximum time-of-arrival
791 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700792 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400793 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000794 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700795 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000796 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500797 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000798 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500799 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000800 }
801 else {
802 sprintf(response,"RSP NOISELEV 1 0");
803 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700804 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800805 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700806 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800807 power = mRadioInterface->setPowerAttenuation(power, chan);
808 mStates[chan].mPower = power;
809 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700810 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800811 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700812 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800813 power = mStates[chan].mPower + step;
814 power = mRadioInterface->setPowerAttenuation(power, chan);
815 mStates[chan].mPower = power;
816 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700817 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000818 // tune receiver
819 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700820 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500821 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400822 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100823 LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000824 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
825 }
826 else
827 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700828 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000829 // tune txmtr
830 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700831 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500832 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400833 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100834 LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000835 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
836 }
837 else
838 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700839 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000840 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500841 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700842 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700843 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500844 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700845 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100846 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000847 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400848 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000849 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700850 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700851 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000852 int corrCode;
853 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700854 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000855 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100856 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000857 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
858 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200859 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400860 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
861 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000862 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700863 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400864 // debug command! may change or disapear without notice
865 // set a mask which bursts to dump to disk
866 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700867 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400868 mWriteBurstToDiskMask = mask;
869 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700870 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100871 LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200872 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000873 }
874
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200875 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200876 msgLen = write(mCtrlSockets[chan], response, strlen(response) + 1);
877 if (msgLen <= 0)
878 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000879}
880
Thomas Tsou204a9f12013-10-29 18:34:16 -0400881bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000882{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200883 int msgLen;
Tom Tsoue8871082016-07-01 02:46:04 -0700884 int burstLen;
885 char buffer[EDGE_BURST_NBITS + 50];
dburgessb3a0ca42011-10-12 07:44:40 +0000886
887 // check data socket
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200888 msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
889 if (msgLen <= 0) {
890 LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
891 return false;
892 }
dburgessb3a0ca42011-10-12 07:44:40 +0000893
Tom Tsoue8871082016-07-01 02:46:04 -0700894 if (msgLen == gSlotLen + 1 + 4 + 1) {
895 burstLen = gSlotLen;
896 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
897 if (mSPSTx != 4)
898 return false;
899
900 burstLen = EDGE_BURST_NBITS;
901 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000902 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
903 return false;
904 }
905
906 int timeSlot = (int) buffer[0];
907 uint64_t frameNum = 0;
908 for (int i = 0; i < 4; i++)
909 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
dburgessb3a0ca42011-10-12 07:44:40 +0000910
dburgessb3a0ca42011-10-12 07:44:40 +0000911 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200912
dburgessb3a0ca42011-10-12 07:44:40 +0000913 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700914 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000915 BitVector::iterator itr = newBurst.begin();
916 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200917 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000918 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200919
dburgessb3a0ca42011-10-12 07:44:40 +0000920 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400921
922 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000923
924 return true;
925
926
927}
dburgessb3a0ca42011-10-12 07:44:40 +0000928
Thomas Tsou204a9f12013-10-29 18:34:16 -0400929void Transceiver::driveReceiveRadio()
930{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200931 int rc = mRadioInterface->driveReceiveRadio();
932 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400933 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200934 } else if (rc < 0) {
935 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
Pau Espin Pedrolb426e4a2019-06-04 12:39:28 +0200936 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200937 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
938 mForceClockInterface = false;
939 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -0400940 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400941}
942
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200943void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800944{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200945 std::ostringstream os;
946 for (size_t i=0; i < bi->nbits; i++) {
947 if (bi->rx_burst[i] > 0.5) os << "1";
948 else if (bi->rx_burst[i] > 0.25) os << "|";
949 else if (bi->rx_burst[i] > 0.0) os << "'";
950 else os << "-";
951 }
952
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800953 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +0300954 << " chan: " << chan
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200955 << " time: " << bi->tn << ":" << bi->fn
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200956 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
957 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
958 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
959 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200960 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200961 << " bits: " << os;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800962}
963
Thomas Tsou204a9f12013-10-29 18:34:16 -0400964void Transceiver::driveReceiveFIFO(size_t chan)
965{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200966 int msgLen;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400967 int TOAint; // in 1/256 symbols
dburgessb3a0ca42011-10-12 07:44:40 +0000968
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200969 struct trx_ul_burst_ind bi;
970
971 if (!pullRadioVector(chan, &bi))
972 return;
dburgessb3a0ca42011-10-12 07:44:40 +0000973
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200974 logRxBurst(chan, &bi);
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400975
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200976 TOAint = (int) (bi.toa * 256.0 + 0.5); // round to closest integer
dburgessb3a0ca42011-10-12 07:44:40 +0000977
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200978 char burstString[sizeof(struct trxd_hdr_v0) + bi.nbits + 2];
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200979 struct trxd_hdr_v0* pkt = (struct trxd_hdr_v0*)burstString;
980 pkt->common.version = 0;
981 pkt->common.reserved = 0;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200982 pkt->common.tn = bi.tn;
983 osmo_store32be(bi.fn, &pkt->common.fn);
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200984 pkt->v0.rssi = bi.rssi;
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200985 osmo_store16be(TOAint, &pkt->v0.toa);
dburgessb3a0ca42011-10-12 07:44:40 +0000986
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200987 for (unsigned i = 0; i < bi.nbits; i++)
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200988 pkt->soft_bits[i] = (char) round(bi.rx_burst[i] * 255.0);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800989
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200990 /* +1: Historical reason. There's an uninitizalied byte in there: pkt->soft_bits[bi.nbits] */
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200991 pkt->soft_bits[bi.nbits + 1] = '\0';
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800992
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200993 msgLen = write(mDataSockets[chan], burstString, sizeof(struct trxd_hdr_v0) + bi.nbits + 2);
994 if (msgLen <= 0)
995 LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000996}
997
Thomas Tsou204a9f12013-10-29 18:34:16 -0400998void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +0000999{
1000
1001 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001002 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +00001003 assure that transmit packets arrive at the radio/USRP
1004 before they need to be transmitted.
1005
1006 Deadline clock indicates the burst that needs to be
1007 pushed into the FIFO right NOW. If transmit queue does
1008 not have a burst, stick in filler data.
1009 */
1010
1011
1012 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001013
dburgessb3a0ca42011-10-12 07:44:40 +00001014 if (mOn) {
1015 //radioClock->wait(); // wait until clock updates
1016 LOG(DEBUG) << "radio clock " << radioClock->get();
1017 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1018 // if underrun, then we're not providing bursts to radio/USRP fast
1019 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001020 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001021 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001022 // only update latency at the defined frame interval
1023 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001024 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol74bcc562018-10-02 17:30:28 +02001025 LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
1026 << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001027 mLatencyUpdateTime = radioClock->get();
1028 }
1029 }
1030 else {
1031 // if underrun hasn't occurred in the last sec (216 frames) drop
1032 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001033 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001034 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1035 mTransmitLatency.decTN();
1036 LOG(INFO) << "reduced latency: " << mTransmitLatency;
1037 mLatencyUpdateTime = radioClock->get();
1038 }
1039 }
1040 }
dburgessb3a0ca42011-10-12 07:44:40 +00001041 }
dburgessb3a0ca42011-10-12 07:44:40 +00001042 // time to push burst to transmit FIFO
1043 pushRadioVector(mTransmitDeadlineClock);
1044 mTransmitDeadlineClock.incTN();
1045 }
dburgessb3a0ca42011-10-12 07:44:40 +00001046 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001047
1048 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001049}
1050
1051
1052
1053void Transceiver::writeClockInterface()
1054{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001055 int msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001056 char command[50];
1057 // FIXME -- This should be adaptive.
1058 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1059
1060 LOG(INFO) << "ClockInterface: sending " << command;
1061
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001062 msgLen = write(mClockSocket, command, strlen(command) + 1);
1063 if (msgLen <= 0)
1064 LOG(WARNING) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001065
1066 mLastClockUpdateTime = mTransmitDeadlineClock;
1067
Thomas Tsou92c16df2013-09-28 18:04:19 -04001068}
dburgessb3a0ca42011-10-12 07:44:40 +00001069
Thomas Tsou204a9f12013-10-29 18:34:16 -04001070void *RxUpperLoopAdapter(TransceiverChannel *chan)
1071{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001072 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001073 Transceiver *trx = chan->trx;
1074 size_t num = chan->num;
1075
1076 delete chan;
1077
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001078 snprintf(thread_name, 16, "RxUpper%zu", num);
1079 set_selfthread_name(thread_name);
1080
Thomas Tsou7553aa92013-11-08 12:50:03 -05001081 trx->setPriority(0.42);
1082
Thomas Tsou204a9f12013-10-29 18:34:16 -04001083 while (1) {
1084 trx->driveReceiveFIFO(num);
1085 pthread_testcancel();
1086 }
1087 return NULL;
1088}
1089
1090void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001091{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001092 set_selfthread_name("RxLower");
1093
Thomas Tsou7553aa92013-11-08 12:50:03 -05001094 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +00001095
dburgessb3a0ca42011-10-12 07:44:40 +00001096 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001097 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001098 pthread_testcancel();
1099 }
1100 return NULL;
1101}
1102
Thomas Tsou204a9f12013-10-29 18:34:16 -04001103void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001104{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001105 set_selfthread_name("TxLower");
1106
Thomas Tsou7553aa92013-11-08 12:50:03 -05001107 transceiver->setPriority(0.44);
1108
Thomas Tsou92c16df2013-09-28 18:04:19 -04001109 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001110 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001111 pthread_testcancel();
1112 }
1113 return NULL;
1114}
1115
Thomas Tsou204a9f12013-10-29 18:34:16 -04001116void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001117{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001118 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001119 Transceiver *trx = chan->trx;
1120 size_t num = chan->num;
1121
1122 delete chan;
1123
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001124 snprintf(thread_name, 16, "CtrlService%zu", num);
1125 set_selfthread_name(thread_name);
1126
dburgessb3a0ca42011-10-12 07:44:40 +00001127 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001128 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001129 pthread_testcancel();
1130 }
1131 return NULL;
1132}
1133
Thomas Tsou204a9f12013-10-29 18:34:16 -04001134void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001135{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001136 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001137 Transceiver *trx = chan->trx;
1138 size_t num = chan->num;
1139
1140 delete chan;
1141
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001142 snprintf(thread_name, 16, "TxUpper%zu", num);
1143 set_selfthread_name(thread_name);
1144
Thomas Tsoua4cf48c2013-11-09 21:44:26 -05001145 trx->setPriority(0.40);
1146
dburgessb3a0ca42011-10-12 07:44:40 +00001147 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001148 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001149 pthread_testcancel();
1150 }
1151 return NULL;
1152}