blob: 58e8719e846947f66370c9ede3f9b1d16d6d9d74 [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;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500580 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500581 signalVector *burst;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200582 GSM::Time burstTime;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200583 SoftVector *rxBurst;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500584 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000585
Thomas Tsou30421a72013-11-13 23:14:48 -0500586 /* Blocking FIFO read */
587 radioVector *radio_burst = mReceiveFIFO[chan]->read();
588 if (!radio_burst)
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200589 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000590
Thomas Tsou30421a72013-11-13 23:14:48 -0500591 /* Set time and determine correlation type */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200592 burstTime = radio_burst->getTime();
593 bi->fn = burstTime.FN();
594 bi->tn = burstTime.TN();
595 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
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500614 /* Select the diversity channel with highest energy */
615 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300616 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500617 if (pow > max) {
618 max = pow;
619 max_i = i;
620 }
621 avg += pow;
622 }
623
624 if (max_i < 0) {
625 LOG(ALERT) << "Received empty burst";
626 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200627 return false;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500628 }
629
Thomas Tsou30421a72013-11-13 23:14:48 -0500630 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500631 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500632 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400633
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200634 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400635
Alexander Chemeris2b542102015-06-08 22:46:38 -0400636 if (type == IDLE) {
637 /* Update noise levels */
638 state->mNoises.insert(avg);
639 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200640 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400641
642 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200643 return false;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400644 } else {
645 /* Do not update noise levels */
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200646 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400647 }
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400648
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200649 unsigned max_toa = (type == RACH || type == EXT_RACH) ?
650 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
651
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);
Thomas Tsouf0782732013-10-29 15:55:47 -0400654
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800655 if (rc > 0) {
656 type = (CorrType) rc;
657 } else if (rc <= 0) {
658 if (rc == -SIGERR_CLIP) {
Alexander Chemeris954b1182015-06-04 15:39:41 -0400659 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800660 } else if (rc != SIGERR_NONE) {
Alexander Chemeris954b1182015-06-04 15:39:41 -0400661 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Tom Tsou577cd022015-05-18 13:57:54 -0700662 }
663
Thomas Tsou30421a72013-11-13 23:14:48 -0500664 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200665 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000666 }
dburgessb3a0ca42011-10-12 07:44:40 +0000667
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200668 bi->toa = toa;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200669 rxBurst = demodAnyBurst(*burst, mSPSRx, amp, toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500670
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200671 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200672 if (rxBurst->size() == EDGE_BURST_NBITS)
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200673 bi->nbits = EDGE_BURST_NBITS;
674 else /* size() here is actually gSlotLen + 8, due to guard periods */
675 bi->nbits = gSlotLen;
676
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200677 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200678 vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200679
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200680 delete rxBurst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500681 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200682 return true;
dburgessb3a0ca42011-10-12 07:44:40 +0000683}
684
dburgessb3a0ca42011-10-12 07:44:40 +0000685void Transceiver::reset()
686{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400687 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
688 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000689}
690
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200691
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700692#define MAX_PACKET_LENGTH 100
693
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700694/**
695 * Matches a buffer with a command.
696 * @param buf a buffer to look command in
697 * @param cmd a command to look in buffer
698 * @param params pointer to arguments, or NULL
699 * @return true if command matches, otherwise false
700 */
701static bool match_cmd(char *buf,
702 const char *cmd, char **params)
703{
704 size_t cmd_len = strlen(cmd);
705
706 /* Check a command itself */
707 if (strncmp(buf, cmd, cmd_len))
708 return false;
709
710 /* A command has arguments */
711 if (params != NULL) {
712 /* Make sure there is a space */
713 if (buf[cmd_len] != ' ')
714 return false;
715
716 /* Update external pointer */
717 *params = buf + cmd_len + 1;
718 }
719
720 return true;
721}
722
Thomas Tsou204a9f12013-10-29 18:34:16 -0400723void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000724{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700725 char buffer[MAX_PACKET_LENGTH + 1];
726 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700727 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700728 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400729
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700730 /* Attempt to read from control socket */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200731 msgLen = read(mCtrlSockets[chan], buffer, MAX_PACKET_LENGTH);
732 if (msgLen <= 0) {
733 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000734 return;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200735 }
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700736
737 /* Zero-terminate received string */
738 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000739
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700740 /* Verify a command signature */
741 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100742 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000743 return;
744 }
dburgessb3a0ca42011-10-12 07:44:40 +0000745
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700746 /* Set command pointer */
747 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200748 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700749
750 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800751 stop();
752 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700753 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700754 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000755 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700756 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000757 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300758 for (int i = 0; i < 8; i++) {
759 for (int j = 0; j < 8; j++)
760 mHandover[i][j] = false;
761 }
Tom Tsou365bc382016-10-19 15:26:04 -0700762 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700763 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700764 unsigned ts = 0, ss = 0;
765 sscanf(params, "%u %u", &ts, &ss);
766 if (ts > 7 || ss > 7) {
767 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
768 } else {
769 mHandover[ts][ss] = true;
770 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
771 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700772 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700773 unsigned ts = 0, ss = 0;
774 sscanf(params, "%u %u", &ts, &ss);
775 if (ts > 7 || ss > 7) {
776 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
777 } else {
778 mHandover[ts][ss] = false;
779 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
780 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700781 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000782 //set expected maximum time-of-arrival
783 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700784 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300785 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000786 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700787 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300788 //set expected maximum time-of-arrival
789 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700790 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300791 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
792 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700793 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000794 //set expected maximum time-of-arrival
795 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700796 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400797 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000798 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700799 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000800 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500801 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000802 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500803 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000804 }
805 else {
806 sprintf(response,"RSP NOISELEV 1 0");
807 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700808 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800809 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700810 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800811 power = mRadioInterface->setPowerAttenuation(power, chan);
812 mStates[chan].mPower = power;
813 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700814 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800815 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700816 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800817 power = mStates[chan].mPower + step;
818 power = mRadioInterface->setPowerAttenuation(power, chan);
819 mStates[chan].mPower = power;
820 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700821 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000822 // tune receiver
823 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700824 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500825 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400826 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100827 LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000828 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
829 }
830 else
831 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700832 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000833 // tune txmtr
834 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700835 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500836 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400837 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100838 LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000839 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
840 }
841 else
842 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700843 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000844 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500845 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700846 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700847 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500848 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700849 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100850 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000851 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400852 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000853 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700854 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700855 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000856 int corrCode;
857 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700858 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000859 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100860 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000861 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
862 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200863 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400864 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
865 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000866 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700867 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400868 // debug command! may change or disapear without notice
869 // set a mask which bursts to dump to disk
870 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700871 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400872 mWriteBurstToDiskMask = mask;
873 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700874 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100875 LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200876 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000877 }
878
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200879 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200880 msgLen = write(mCtrlSockets[chan], response, strlen(response) + 1);
881 if (msgLen <= 0)
882 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000883}
884
Thomas Tsou204a9f12013-10-29 18:34:16 -0400885bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000886{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200887 int msgLen;
Tom Tsoue8871082016-07-01 02:46:04 -0700888 int burstLen;
889 char buffer[EDGE_BURST_NBITS + 50];
dburgessb3a0ca42011-10-12 07:44:40 +0000890
891 // check data socket
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200892 msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
893 if (msgLen <= 0) {
894 LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
895 return false;
896 }
dburgessb3a0ca42011-10-12 07:44:40 +0000897
Tom Tsoue8871082016-07-01 02:46:04 -0700898 if (msgLen == gSlotLen + 1 + 4 + 1) {
899 burstLen = gSlotLen;
900 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
901 if (mSPSTx != 4)
902 return false;
903
904 burstLen = EDGE_BURST_NBITS;
905 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000906 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
907 return false;
908 }
909
910 int timeSlot = (int) buffer[0];
911 uint64_t frameNum = 0;
912 for (int i = 0; i < 4; i++)
913 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
dburgessb3a0ca42011-10-12 07:44:40 +0000914
dburgessb3a0ca42011-10-12 07:44:40 +0000915 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200916
dburgessb3a0ca42011-10-12 07:44:40 +0000917 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700918 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000919 BitVector::iterator itr = newBurst.begin();
920 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200921 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000922 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200923
dburgessb3a0ca42011-10-12 07:44:40 +0000924 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400925
926 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000927
928 return true;
929
930
931}
dburgessb3a0ca42011-10-12 07:44:40 +0000932
Thomas Tsou204a9f12013-10-29 18:34:16 -0400933void Transceiver::driveReceiveRadio()
934{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200935 int rc = mRadioInterface->driveReceiveRadio();
936 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400937 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200938 } else if (rc < 0) {
939 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
Pau Espin Pedrolb426e4a2019-06-04 12:39:28 +0200940 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200941 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
942 mForceClockInterface = false;
943 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -0400944 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400945}
946
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200947void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800948{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200949 std::ostringstream os;
950 for (size_t i=0; i < bi->nbits; i++) {
951 if (bi->rx_burst[i] > 0.5) os << "1";
952 else if (bi->rx_burst[i] > 0.25) os << "|";
953 else if (bi->rx_burst[i] > 0.0) os << "'";
954 else os << "-";
955 }
956
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800957 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +0300958 << " chan: " << chan
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200959 << " time: " << bi->tn << ":" << bi->fn
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200960 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
961 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
962 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
963 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200964 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200965 << " bits: " << os;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800966}
967
Thomas Tsou204a9f12013-10-29 18:34:16 -0400968void Transceiver::driveReceiveFIFO(size_t chan)
969{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200970 int msgLen;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400971 int TOAint; // in 1/256 symbols
dburgessb3a0ca42011-10-12 07:44:40 +0000972
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200973 struct trx_ul_burst_ind bi;
974
975 if (!pullRadioVector(chan, &bi))
976 return;
dburgessb3a0ca42011-10-12 07:44:40 +0000977
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200978 logRxBurst(chan, &bi);
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400979
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200980 TOAint = (int) (bi.toa * 256.0 + 0.5); // round to closest integer
dburgessb3a0ca42011-10-12 07:44:40 +0000981
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200982 char burstString[sizeof(struct trxd_hdr_v0) + bi.nbits + 2];
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200983 struct trxd_hdr_v0* pkt = (struct trxd_hdr_v0*)burstString;
984 pkt->common.version = 0;
985 pkt->common.reserved = 0;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200986 pkt->common.tn = bi.tn;
987 osmo_store32be(bi.fn, &pkt->common.fn);
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200988 pkt->v0.rssi = bi.rssi;
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200989 osmo_store16be(TOAint, &pkt->v0.toa);
dburgessb3a0ca42011-10-12 07:44:40 +0000990
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200991 for (unsigned i = 0; i < bi.nbits; i++)
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200992 pkt->soft_bits[i] = (char) round(bi.rx_burst[i] * 255.0);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800993
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200994 /* +1: Historical reason. There's an uninitizalied byte in there: pkt->soft_bits[bi.nbits] */
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200995 pkt->soft_bits[bi.nbits + 1] = '\0';
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800996
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200997 msgLen = write(mDataSockets[chan], burstString, sizeof(struct trxd_hdr_v0) + bi.nbits + 2);
998 if (msgLen <= 0)
999 LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001000}
1001
Thomas Tsou204a9f12013-10-29 18:34:16 -04001002void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +00001003{
1004
1005 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001006 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +00001007 assure that transmit packets arrive at the radio/USRP
1008 before they need to be transmitted.
1009
1010 Deadline clock indicates the burst that needs to be
1011 pushed into the FIFO right NOW. If transmit queue does
1012 not have a burst, stick in filler data.
1013 */
1014
1015
1016 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001017
dburgessb3a0ca42011-10-12 07:44:40 +00001018 if (mOn) {
1019 //radioClock->wait(); // wait until clock updates
1020 LOG(DEBUG) << "radio clock " << radioClock->get();
1021 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1022 // if underrun, then we're not providing bursts to radio/USRP fast
1023 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001024 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001025 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001026 // only update latency at the defined frame interval
1027 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001028 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol74bcc562018-10-02 17:30:28 +02001029 LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
1030 << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001031 mLatencyUpdateTime = radioClock->get();
1032 }
1033 }
1034 else {
1035 // if underrun hasn't occurred in the last sec (216 frames) drop
1036 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001037 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001038 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1039 mTransmitLatency.decTN();
1040 LOG(INFO) << "reduced latency: " << mTransmitLatency;
1041 mLatencyUpdateTime = radioClock->get();
1042 }
1043 }
1044 }
dburgessb3a0ca42011-10-12 07:44:40 +00001045 }
dburgessb3a0ca42011-10-12 07:44:40 +00001046 // time to push burst to transmit FIFO
1047 pushRadioVector(mTransmitDeadlineClock);
1048 mTransmitDeadlineClock.incTN();
1049 }
dburgessb3a0ca42011-10-12 07:44:40 +00001050 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001051
1052 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001053}
1054
1055
1056
1057void Transceiver::writeClockInterface()
1058{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001059 int msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001060 char command[50];
1061 // FIXME -- This should be adaptive.
1062 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1063
1064 LOG(INFO) << "ClockInterface: sending " << command;
1065
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001066 msgLen = write(mClockSocket, command, strlen(command) + 1);
1067 if (msgLen <= 0)
1068 LOG(WARNING) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001069
1070 mLastClockUpdateTime = mTransmitDeadlineClock;
1071
Thomas Tsou92c16df2013-09-28 18:04:19 -04001072}
dburgessb3a0ca42011-10-12 07:44:40 +00001073
Thomas Tsou204a9f12013-10-29 18:34:16 -04001074void *RxUpperLoopAdapter(TransceiverChannel *chan)
1075{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001076 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001077 Transceiver *trx = chan->trx;
1078 size_t num = chan->num;
1079
1080 delete chan;
1081
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001082 snprintf(thread_name, 16, "RxUpper%zu", num);
1083 set_selfthread_name(thread_name);
1084
Thomas Tsou7553aa92013-11-08 12:50:03 -05001085 trx->setPriority(0.42);
1086
Thomas Tsou204a9f12013-10-29 18:34:16 -04001087 while (1) {
1088 trx->driveReceiveFIFO(num);
1089 pthread_testcancel();
1090 }
1091 return NULL;
1092}
1093
1094void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001095{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001096 set_selfthread_name("RxLower");
1097
Thomas Tsou7553aa92013-11-08 12:50:03 -05001098 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +00001099
dburgessb3a0ca42011-10-12 07:44:40 +00001100 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001101 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001102 pthread_testcancel();
1103 }
1104 return NULL;
1105}
1106
Thomas Tsou204a9f12013-10-29 18:34:16 -04001107void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001108{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001109 set_selfthread_name("TxLower");
1110
Thomas Tsou7553aa92013-11-08 12:50:03 -05001111 transceiver->setPriority(0.44);
1112
Thomas Tsou92c16df2013-09-28 18:04:19 -04001113 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001114 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001115 pthread_testcancel();
1116 }
1117 return NULL;
1118}
1119
Thomas Tsou204a9f12013-10-29 18:34:16 -04001120void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001121{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001122 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001123 Transceiver *trx = chan->trx;
1124 size_t num = chan->num;
1125
1126 delete chan;
1127
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001128 snprintf(thread_name, 16, "CtrlService%zu", num);
1129 set_selfthread_name(thread_name);
1130
dburgessb3a0ca42011-10-12 07:44:40 +00001131 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001132 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001133 pthread_testcancel();
1134 }
1135 return NULL;
1136}
1137
Thomas Tsou204a9f12013-10-29 18:34:16 -04001138void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001139{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001140 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001141 Transceiver *trx = chan->trx;
1142 size_t num = chan->num;
1143
1144 delete chan;
1145
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001146 snprintf(thread_name, 16, "TxUpper%zu", num);
1147 set_selfthread_name(thread_name);
1148
Thomas Tsoua4cf48c2013-11-09 21:44:26 -05001149 trx->setPriority(0.40);
1150
dburgessb3a0ca42011-10-12 07:44:40 +00001151 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001152 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001153 pthread_testcancel();
1154 }
1155 return NULL;
1156}