blob: 81b428aee33e764f61df235f5608dc8836f01b8a [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
Alexander Chemeris2b542102015-06-08 22:46:38 -0400634 if (type == IDLE) {
635 /* Update noise levels */
636 state->mNoises.insert(avg);
637 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200638 }
Alexander Chemeris2b542102015-06-08 22:46:38 -0400639
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200640 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
641 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
642
643 if (type == IDLE) {
Alexander Chemeris2b542102015-06-08 22:46:38 -0400644 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200645 return false;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400646 }
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400647
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200648 unsigned max_toa = (type == RACH || type == EXT_RACH) ?
649 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
650
Thomas Tsou30421a72013-11-13 23:14:48 -0500651 /* Detect normal or RACH bursts */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200652 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa, max_toa);
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200653 if (rc <= 0) {
654 if (rc == -SIGERR_CLIP)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400655 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200656 else if (rc != SIGERR_NONE)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400657 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Thomas Tsou30421a72013-11-13 23:14:48 -0500658 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200659 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000660 }
dburgessb3a0ca42011-10-12 07:44:40 +0000661
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200662 type = (CorrType) rc;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200663 bi->toa = toa;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200664 rxBurst = demodAnyBurst(*burst, mSPSRx, amp, toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500665
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200666 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200667 if (rxBurst->size() == EDGE_BURST_NBITS)
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200668 bi->nbits = EDGE_BURST_NBITS;
669 else /* size() here is actually gSlotLen + 8, due to guard periods */
670 bi->nbits = gSlotLen;
671
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200672 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200673 vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200674
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200675 delete rxBurst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500676 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200677 return true;
dburgessb3a0ca42011-10-12 07:44:40 +0000678}
679
dburgessb3a0ca42011-10-12 07:44:40 +0000680void Transceiver::reset()
681{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400682 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
683 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000684}
685
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200686
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700687#define MAX_PACKET_LENGTH 100
688
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700689/**
690 * Matches a buffer with a command.
691 * @param buf a buffer to look command in
692 * @param cmd a command to look in buffer
693 * @param params pointer to arguments, or NULL
694 * @return true if command matches, otherwise false
695 */
696static bool match_cmd(char *buf,
697 const char *cmd, char **params)
698{
699 size_t cmd_len = strlen(cmd);
700
701 /* Check a command itself */
702 if (strncmp(buf, cmd, cmd_len))
703 return false;
704
705 /* A command has arguments */
706 if (params != NULL) {
707 /* Make sure there is a space */
708 if (buf[cmd_len] != ' ')
709 return false;
710
711 /* Update external pointer */
712 *params = buf + cmd_len + 1;
713 }
714
715 return true;
716}
717
Thomas Tsou204a9f12013-10-29 18:34:16 -0400718void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000719{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700720 char buffer[MAX_PACKET_LENGTH + 1];
721 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700722 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700723 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400724
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700725 /* Attempt to read from control socket */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200726 msgLen = read(mCtrlSockets[chan], buffer, MAX_PACKET_LENGTH);
727 if (msgLen <= 0) {
728 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000729 return;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200730 }
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700731
732 /* Zero-terminate received string */
733 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000734
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700735 /* Verify a command signature */
736 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100737 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000738 return;
739 }
dburgessb3a0ca42011-10-12 07:44:40 +0000740
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700741 /* Set command pointer */
742 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200743 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700744
745 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800746 stop();
747 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700748 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700749 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000750 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700751 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000752 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300753 for (int i = 0; i < 8; i++) {
754 for (int j = 0; j < 8; j++)
755 mHandover[i][j] = false;
756 }
Tom Tsou365bc382016-10-19 15:26:04 -0700757 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700758 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700759 unsigned ts = 0, ss = 0;
760 sscanf(params, "%u %u", &ts, &ss);
761 if (ts > 7 || ss > 7) {
762 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
763 } else {
764 mHandover[ts][ss] = true;
765 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
766 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700767 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700768 unsigned ts = 0, ss = 0;
769 sscanf(params, "%u %u", &ts, &ss);
770 if (ts > 7 || ss > 7) {
771 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
772 } else {
773 mHandover[ts][ss] = false;
774 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
775 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700776 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000777 //set expected maximum time-of-arrival
778 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700779 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300780 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000781 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700782 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300783 //set expected maximum time-of-arrival
784 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700785 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300786 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
787 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700788 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000789 //set expected maximum time-of-arrival
790 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700791 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400792 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000793 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700794 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000795 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500796 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000797 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500798 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000799 }
800 else {
801 sprintf(response,"RSP NOISELEV 1 0");
802 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700803 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800804 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700805 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800806 power = mRadioInterface->setPowerAttenuation(power, chan);
807 mStates[chan].mPower = power;
808 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700809 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800810 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700811 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800812 power = mStates[chan].mPower + step;
813 power = mRadioInterface->setPowerAttenuation(power, chan);
814 mStates[chan].mPower = power;
815 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700816 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000817 // tune receiver
818 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700819 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500820 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400821 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100822 LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000823 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
824 }
825 else
826 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700827 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000828 // tune txmtr
829 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700830 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500831 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400832 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100833 LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000834 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
835 }
836 else
837 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700838 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000839 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500840 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700841 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700842 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500843 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700844 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100845 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000846 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400847 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000848 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700849 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700850 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000851 int corrCode;
852 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700853 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000854 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100855 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000856 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
857 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200858 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400859 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
860 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000861 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700862 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400863 // debug command! may change or disapear without notice
864 // set a mask which bursts to dump to disk
865 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700866 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400867 mWriteBurstToDiskMask = mask;
868 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700869 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100870 LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200871 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000872 }
873
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200874 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200875 msgLen = write(mCtrlSockets[chan], response, strlen(response) + 1);
876 if (msgLen <= 0)
877 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000878}
879
Thomas Tsou204a9f12013-10-29 18:34:16 -0400880bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000881{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200882 int msgLen;
Tom Tsoue8871082016-07-01 02:46:04 -0700883 int burstLen;
884 char buffer[EDGE_BURST_NBITS + 50];
dburgessb3a0ca42011-10-12 07:44:40 +0000885
886 // check data socket
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200887 msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
888 if (msgLen <= 0) {
889 LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
890 return false;
891 }
dburgessb3a0ca42011-10-12 07:44:40 +0000892
Tom Tsoue8871082016-07-01 02:46:04 -0700893 if (msgLen == gSlotLen + 1 + 4 + 1) {
894 burstLen = gSlotLen;
895 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
896 if (mSPSTx != 4)
897 return false;
898
899 burstLen = EDGE_BURST_NBITS;
900 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000901 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
902 return false;
903 }
904
905 int timeSlot = (int) buffer[0];
906 uint64_t frameNum = 0;
907 for (int i = 0; i < 4; i++)
908 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
dburgessb3a0ca42011-10-12 07:44:40 +0000909
dburgessb3a0ca42011-10-12 07:44:40 +0000910 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200911
dburgessb3a0ca42011-10-12 07:44:40 +0000912 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700913 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000914 BitVector::iterator itr = newBurst.begin();
915 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200916 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000917 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200918
dburgessb3a0ca42011-10-12 07:44:40 +0000919 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400920
921 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000922
923 return true;
924
925
926}
dburgessb3a0ca42011-10-12 07:44:40 +0000927
Thomas Tsou204a9f12013-10-29 18:34:16 -0400928void Transceiver::driveReceiveRadio()
929{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200930 int rc = mRadioInterface->driveReceiveRadio();
931 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400932 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200933 } else if (rc < 0) {
934 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
Pau Espin Pedrolb426e4a2019-06-04 12:39:28 +0200935 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200936 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
937 mForceClockInterface = false;
938 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -0400939 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400940}
941
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200942void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800943{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200944 std::ostringstream os;
945 for (size_t i=0; i < bi->nbits; i++) {
946 if (bi->rx_burst[i] > 0.5) os << "1";
947 else if (bi->rx_burst[i] > 0.25) os << "|";
948 else if (bi->rx_burst[i] > 0.0) os << "'";
949 else os << "-";
950 }
951
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800952 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +0300953 << " chan: " << chan
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200954 << " time: " << bi->tn << ":" << bi->fn
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200955 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
956 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
957 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
958 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200959 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200960 << " bits: " << os;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800961}
962
Thomas Tsou204a9f12013-10-29 18:34:16 -0400963void Transceiver::driveReceiveFIFO(size_t chan)
964{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200965 int msgLen;
Alexander Chemerise8905a02015-06-03 23:47:56 -0400966 int TOAint; // in 1/256 symbols
dburgessb3a0ca42011-10-12 07:44:40 +0000967
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200968 struct trx_ul_burst_ind bi;
969
970 if (!pullRadioVector(chan, &bi))
971 return;
dburgessb3a0ca42011-10-12 07:44:40 +0000972
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200973 logRxBurst(chan, &bi);
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400974
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200975 TOAint = (int) (bi.toa * 256.0 + 0.5); // round to closest integer
dburgessb3a0ca42011-10-12 07:44:40 +0000976
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200977 char burstString[sizeof(struct trxd_hdr_v0) + bi.nbits + 2];
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200978 struct trxd_hdr_v0* pkt = (struct trxd_hdr_v0*)burstString;
979 pkt->common.version = 0;
980 pkt->common.reserved = 0;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200981 pkt->common.tn = bi.tn;
982 osmo_store32be(bi.fn, &pkt->common.fn);
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200983 pkt->v0.rssi = bi.rssi;
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200984 osmo_store16be(TOAint, &pkt->v0.toa);
dburgessb3a0ca42011-10-12 07:44:40 +0000985
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200986 for (unsigned i = 0; i < bi.nbits; i++)
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200987 pkt->soft_bits[i] = (char) round(bi.rx_burst[i] * 255.0);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800988
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200989 /* +1: Historical reason. There's an uninitizalied byte in there: pkt->soft_bits[bi.nbits] */
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200990 pkt->soft_bits[bi.nbits + 1] = '\0';
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800991
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200992 msgLen = write(mDataSockets[chan], burstString, sizeof(struct trxd_hdr_v0) + bi.nbits + 2);
993 if (msgLen <= 0)
994 LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000995}
996
Thomas Tsou204a9f12013-10-29 18:34:16 -0400997void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +0000998{
999
1000 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001001 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +00001002 assure that transmit packets arrive at the radio/USRP
1003 before they need to be transmitted.
1004
1005 Deadline clock indicates the burst that needs to be
1006 pushed into the FIFO right NOW. If transmit queue does
1007 not have a burst, stick in filler data.
1008 */
1009
1010
1011 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001012
dburgessb3a0ca42011-10-12 07:44:40 +00001013 if (mOn) {
1014 //radioClock->wait(); // wait until clock updates
1015 LOG(DEBUG) << "radio clock " << radioClock->get();
1016 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1017 // if underrun, then we're not providing bursts to radio/USRP fast
1018 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001019 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001020 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001021 // only update latency at the defined frame interval
1022 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001023 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol74bcc562018-10-02 17:30:28 +02001024 LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
1025 << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001026 mLatencyUpdateTime = radioClock->get();
1027 }
1028 }
1029 else {
1030 // if underrun hasn't occurred in the last sec (216 frames) drop
1031 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001032 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001033 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1034 mTransmitLatency.decTN();
1035 LOG(INFO) << "reduced latency: " << mTransmitLatency;
1036 mLatencyUpdateTime = radioClock->get();
1037 }
1038 }
1039 }
dburgessb3a0ca42011-10-12 07:44:40 +00001040 }
dburgessb3a0ca42011-10-12 07:44:40 +00001041 // time to push burst to transmit FIFO
1042 pushRadioVector(mTransmitDeadlineClock);
1043 mTransmitDeadlineClock.incTN();
1044 }
dburgessb3a0ca42011-10-12 07:44:40 +00001045 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001046
1047 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001048}
1049
1050
1051
1052void Transceiver::writeClockInterface()
1053{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001054 int msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001055 char command[50];
1056 // FIXME -- This should be adaptive.
1057 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1058
1059 LOG(INFO) << "ClockInterface: sending " << command;
1060
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001061 msgLen = write(mClockSocket, command, strlen(command) + 1);
1062 if (msgLen <= 0)
1063 LOG(WARNING) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001064
1065 mLastClockUpdateTime = mTransmitDeadlineClock;
1066
Thomas Tsou92c16df2013-09-28 18:04:19 -04001067}
dburgessb3a0ca42011-10-12 07:44:40 +00001068
Thomas Tsou204a9f12013-10-29 18:34:16 -04001069void *RxUpperLoopAdapter(TransceiverChannel *chan)
1070{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001071 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001072 Transceiver *trx = chan->trx;
1073 size_t num = chan->num;
1074
1075 delete chan;
1076
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001077 snprintf(thread_name, 16, "RxUpper%zu", num);
1078 set_selfthread_name(thread_name);
1079
Thomas Tsou7553aa92013-11-08 12:50:03 -05001080 trx->setPriority(0.42);
1081
Thomas Tsou204a9f12013-10-29 18:34:16 -04001082 while (1) {
1083 trx->driveReceiveFIFO(num);
1084 pthread_testcancel();
1085 }
1086 return NULL;
1087}
1088
1089void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001090{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001091 set_selfthread_name("RxLower");
1092
Thomas Tsou7553aa92013-11-08 12:50:03 -05001093 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +00001094
dburgessb3a0ca42011-10-12 07:44:40 +00001095 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001096 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001097 pthread_testcancel();
1098 }
1099 return NULL;
1100}
1101
Thomas Tsou204a9f12013-10-29 18:34:16 -04001102void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001103{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001104 set_selfthread_name("TxLower");
1105
Thomas Tsou7553aa92013-11-08 12:50:03 -05001106 transceiver->setPriority(0.44);
1107
Thomas Tsou92c16df2013-09-28 18:04:19 -04001108 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001109 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001110 pthread_testcancel();
1111 }
1112 return NULL;
1113}
1114
Thomas Tsou204a9f12013-10-29 18:34:16 -04001115void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001116{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001117 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001118 Transceiver *trx = chan->trx;
1119 size_t num = chan->num;
1120
1121 delete chan;
1122
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001123 snprintf(thread_name, 16, "CtrlService%zu", num);
1124 set_selfthread_name(thread_name);
1125
dburgessb3a0ca42011-10-12 07:44:40 +00001126 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001127 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001128 pthread_testcancel();
1129 }
1130 return NULL;
1131}
1132
Thomas Tsou204a9f12013-10-29 18:34:16 -04001133void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001134{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001135 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001136 Transceiver *trx = chan->trx;
1137 size_t num = chan->num;
1138
1139 delete chan;
1140
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001141 snprintf(thread_name, 16, "TxUpper%zu", num);
1142 set_selfthread_name(thread_name);
1143
Thomas Tsoua4cf48c2013-11-09 21:44:26 -05001144 trx->setPriority(0.40);
1145
dburgessb3a0ca42011-10-12 07:44:40 +00001146 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001147 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001148 pthread_testcancel();
1149 }
1150 return NULL;
1151}