blob: b73e6981a1cbc159636db4ea6462b702511ff9fe [file] [log] [blame]
dburgessb3a0ca42011-10-12 07:44:40 +00001/*
2* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
3*
4* This software is distributed under the terms of the GNU Public License.
5* See the COPYING file in the main directory for details.
6*
7* This use of this software may be subject to additional restrictions.
8* See the LEGAL file in the main directory for details.
9
10 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22*/
23
dburgessb3a0ca42011-10-12 07:44:40 +000024#include <stdio.h>
Alexander Chemerise8905a02015-06-03 23:47:56 -040025#include <iomanip> // std::setprecision
Alexander Chemerise692ce92015-06-12 00:15:31 -040026#include <fstream>
dburgessb3a0ca42011-10-12 07:44:40 +000027#include "Transceiver.h"
28#include <Logger.h>
29
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020030extern "C" {
31#include "osmo_signal.h"
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +020032#include "proto_trxd.h"
33
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +020034#include <osmocom/core/utils.h>
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +020035#include <osmocom/core/socket.h>
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),
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +0200128 mWriteBurstToDiskMask(0), mVersionTRXD(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;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200578 struct estim_burst_params ebp;
579 float max = -1.0, avg = 0.0;
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200580 unsigned max_toa;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500581 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500582 signalVector *burst;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200583 GSM::Time burstTime;
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200584 SoftVector *rxBurst;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500585 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000586
Thomas Tsou30421a72013-11-13 23:14:48 -0500587 /* Blocking FIFO read */
588 radioVector *radio_burst = mReceiveFIFO[chan]->read();
589 if (!radio_burst)
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200590 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000591
Thomas Tsou30421a72013-11-13 23:14:48 -0500592 /* Set time and determine correlation type */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200593 burstTime = radio_burst->getTime();
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200594 CorrType type = expectedCorrType(burstTime, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000595
Tom Tsou64464e62016-07-01 03:46:46 -0700596 /* Enable 8-PSK burst detection if EDGE is enabled */
597 if (mEdge && (type == TSC))
598 type = EDGE;
599
Alexander Chemerise692ce92015-06-12 00:15:31 -0400600 /* Debug: dump bursts to disk */
601 /* bits 0-7 - chan 0 timeslots
602 * bits 8-15 - chan 1 timeslots */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200603 if (mWriteBurstToDiskMask & ((1<<bi->tn) << (8*chan)))
Alexander Chemerise692ce92015-06-12 00:15:31 -0400604 writeToFile(radio_burst, chan);
605
Alexander Chemeris2b542102015-06-08 22:46:38 -0400606 /* No processing if the timeslot is off.
607 * Not even power level or noise calculation. */
608 if (type == OFF) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500609 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200610 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000611 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000612
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200613 /* Initialize struct bi */
614 bi->nbits = 0;
615 bi->fn = burstTime.FN();
616 bi->tn = burstTime.TN();
617 bi->rssi = 0.0;
618 bi->toa = 0.0;
619 bi->noise = 0.0;
620 bi->idle = false;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200621 bi->modulation = MODULATION_GMSK;
622 bi->tss = 0; /* TODO: we only support tss 0 right now */
623 bi->tsc = 0;
624 bi->ci = 0.0;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200625
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500626 /* Select the diversity channel with highest energy */
627 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300628 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500629 if (pow > max) {
630 max = pow;
631 max_i = i;
632 }
633 avg += pow;
634 }
635
636 if (max_i < 0) {
637 LOG(ALERT) << "Received empty burst";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200638 goto ret_idle;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500639 }
640
Thomas Tsou30421a72013-11-13 23:14:48 -0500641 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500642 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500643 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400644
Alexander Chemeris2b542102015-06-08 22:46:38 -0400645 if (type == IDLE) {
646 /* Update noise levels */
647 state->mNoises.insert(avg);
648 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200649 }
Alexander Chemeris2b542102015-06-08 22:46:38 -0400650
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200651 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
652 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
653
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200654 if (type == IDLE)
655 goto ret_idle;
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400656
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200657 max_toa = (type == RACH || type == EXT_RACH) ?
658 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200659
Thomas Tsou30421a72013-11-13 23:14:48 -0500660 /* Detect normal or RACH bursts */
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200661 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, max_toa, &ebp);
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200662 if (rc <= 0) {
663 if (rc == -SIGERR_CLIP)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400664 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200665 else if (rc != SIGERR_NONE)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400666 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200667 goto ret_idle;
dburgessb3a0ca42011-10-12 07:44:40 +0000668 }
dburgessb3a0ca42011-10-12 07:44:40 +0000669
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200670 type = (CorrType) rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200671 bi->toa = ebp.toa;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200672 bi->tsc = ebp.tsc;
673 bi->ci = ebp.ci;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200674 rxBurst = demodAnyBurst(*burst, mSPSRx, ebp.amp, ebp.toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500675
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200676 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200677 if (rxBurst->size() == EDGE_BURST_NBITS) {
678 bi->modulation = MODULATION_8PSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200679 bi->nbits = EDGE_BURST_NBITS;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200680 } else { /* size() here is actually gSlotLen + 8, due to guard periods */
681 bi->modulation = MODULATION_GMSK;
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200682 bi->nbits = gSlotLen;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200683 }
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200684
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200685 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200686 vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200687
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200688 delete rxBurst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500689 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200690 return true;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200691
692ret_idle:
693 bi->idle = true;
694 delete radio_burst;
695 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000696}
697
dburgessb3a0ca42011-10-12 07:44:40 +0000698void Transceiver::reset()
699{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400700 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
701 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000702}
703
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200704
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700705#define MAX_PACKET_LENGTH 100
706
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700707/**
708 * Matches a buffer with a command.
709 * @param buf a buffer to look command in
710 * @param cmd a command to look in buffer
711 * @param params pointer to arguments, or NULL
712 * @return true if command matches, otherwise false
713 */
714static bool match_cmd(char *buf,
715 const char *cmd, char **params)
716{
717 size_t cmd_len = strlen(cmd);
718
719 /* Check a command itself */
720 if (strncmp(buf, cmd, cmd_len))
721 return false;
722
723 /* A command has arguments */
724 if (params != NULL) {
725 /* Make sure there is a space */
726 if (buf[cmd_len] != ' ')
727 return false;
728
729 /* Update external pointer */
730 *params = buf + cmd_len + 1;
731 }
732
733 return true;
734}
735
Thomas Tsou204a9f12013-10-29 18:34:16 -0400736void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000737{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700738 char buffer[MAX_PACKET_LENGTH + 1];
739 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700740 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700741 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400742
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700743 /* Attempt to read from control socket */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200744 msgLen = read(mCtrlSockets[chan], buffer, MAX_PACKET_LENGTH);
745 if (msgLen <= 0) {
746 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000747 return;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200748 }
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700749
750 /* Zero-terminate received string */
751 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000752
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700753 /* Verify a command signature */
754 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100755 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000756 return;
757 }
dburgessb3a0ca42011-10-12 07:44:40 +0000758
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700759 /* Set command pointer */
760 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200761 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700762
763 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800764 stop();
765 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700766 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700767 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000768 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700769 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000770 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300771 for (int i = 0; i < 8; i++) {
772 for (int j = 0; j < 8; j++)
773 mHandover[i][j] = false;
774 }
Tom Tsou365bc382016-10-19 15:26:04 -0700775 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700776 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700777 unsigned ts = 0, ss = 0;
778 sscanf(params, "%u %u", &ts, &ss);
779 if (ts > 7 || ss > 7) {
780 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
781 } else {
782 mHandover[ts][ss] = true;
783 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
784 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700785 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700786 unsigned ts = 0, ss = 0;
787 sscanf(params, "%u %u", &ts, &ss);
788 if (ts > 7 || ss > 7) {
789 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
790 } else {
791 mHandover[ts][ss] = false;
792 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
793 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700794 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000795 //set expected maximum time-of-arrival
796 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700797 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300798 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000799 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700800 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300801 //set expected maximum time-of-arrival
802 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700803 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300804 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
805 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700806 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000807 //set expected maximum time-of-arrival
808 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700809 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400810 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000811 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700812 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000813 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500814 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000815 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500816 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000817 }
818 else {
819 sprintf(response,"RSP NOISELEV 1 0");
820 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700821 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800822 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700823 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800824 power = mRadioInterface->setPowerAttenuation(power, chan);
825 mStates[chan].mPower = power;
826 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700827 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800828 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700829 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800830 power = mStates[chan].mPower + step;
831 power = mRadioInterface->setPowerAttenuation(power, chan);
832 mStates[chan].mPower = power;
833 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700834 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000835 // tune receiver
836 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700837 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500838 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400839 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100840 LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000841 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
842 }
843 else
844 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700845 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000846 // tune txmtr
847 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700848 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500849 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400850 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100851 LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000852 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
853 }
854 else
855 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700856 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000857 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500858 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700859 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700860 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500861 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700862 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100863 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000864 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400865 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000866 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700867 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700868 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000869 int corrCode;
870 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700871 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000872 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100873 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000874 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
875 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200876 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400877 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
878 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000879 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Pau Espin Pedrol13c81092019-07-03 16:17:06 +0200880 } else if (match_cmd(command, "SETFORMAT", &params)) {
881 // set TRXD protocol version
882 unsigned version_recv;
883 sscanf(params, "%u", &version_recv);
884 LOGC(DTRXCTRL, INFO) << "BTS requests TRXD version switch: " << version_recv;
885 if (version_recv > TRX_DATA_FORMAT_VER) {
886 LOGC(DTRXCTRL, INFO) << "rejecting TRXD version " << version_recv
887 << "in favor of " << TRX_DATA_FORMAT_VER;
888 sprintf(response, "RSP SETFORMAT %u %u", TRX_DATA_FORMAT_VER, version_recv);
889 } else {
890 LOGC(DTRXCTRL, NOTICE) << "switching to TRXD version " << version_recv;
891 mVersionTRXD = version_recv;
892 sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
893 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700894 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400895 // debug command! may change or disapear without notice
896 // set a mask which bursts to dump to disk
897 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700898 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400899 mWriteBurstToDiskMask = mask;
900 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700901 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100902 LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200903 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000904 }
905
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200906 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200907 msgLen = write(mCtrlSockets[chan], response, strlen(response) + 1);
908 if (msgLen <= 0)
909 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000910}
911
Thomas Tsou204a9f12013-10-29 18:34:16 -0400912bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000913{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200914 int msgLen;
Tom Tsoue8871082016-07-01 02:46:04 -0700915 int burstLen;
916 char buffer[EDGE_BURST_NBITS + 50];
dburgessb3a0ca42011-10-12 07:44:40 +0000917
918 // check data socket
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200919 msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
920 if (msgLen <= 0) {
921 LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
922 return false;
923 }
dburgessb3a0ca42011-10-12 07:44:40 +0000924
Tom Tsoue8871082016-07-01 02:46:04 -0700925 if (msgLen == gSlotLen + 1 + 4 + 1) {
926 burstLen = gSlotLen;
927 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
928 if (mSPSTx != 4)
929 return false;
930
931 burstLen = EDGE_BURST_NBITS;
932 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000933 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
934 return false;
935 }
936
937 int timeSlot = (int) buffer[0];
938 uint64_t frameNum = 0;
939 for (int i = 0; i < 4; i++)
940 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
dburgessb3a0ca42011-10-12 07:44:40 +0000941
dburgessb3a0ca42011-10-12 07:44:40 +0000942 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200943
dburgessb3a0ca42011-10-12 07:44:40 +0000944 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700945 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000946 BitVector::iterator itr = newBurst.begin();
947 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200948 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000949 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200950
dburgessb3a0ca42011-10-12 07:44:40 +0000951 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400952
953 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000954
955 return true;
956
957
958}
dburgessb3a0ca42011-10-12 07:44:40 +0000959
Thomas Tsou204a9f12013-10-29 18:34:16 -0400960void Transceiver::driveReceiveRadio()
961{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200962 int rc = mRadioInterface->driveReceiveRadio();
963 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400964 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200965 } else if (rc < 0) {
966 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
Pau Espin Pedrolb426e4a2019-06-04 12:39:28 +0200967 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200968 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
969 mForceClockInterface = false;
970 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -0400971 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400972}
973
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200974void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800975{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200976 std::ostringstream os;
977 for (size_t i=0; i < bi->nbits; i++) {
978 if (bi->rx_burst[i] > 0.5) os << "1";
979 else if (bi->rx_burst[i] > 0.25) os << "|";
980 else if (bi->rx_burst[i] > 0.0) os << "'";
981 else os << "-";
982 }
983
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800984 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +0300985 << " chan: " << chan
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200986 << " time: " << bi->tn << ":" << bi->fn
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200987 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
988 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
989 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
990 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200991 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +0200992 << " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB"
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200993 << " bits: " << os;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800994}
995
Thomas Tsou204a9f12013-10-29 18:34:16 -0400996void Transceiver::driveReceiveFIFO(size_t chan)
997{
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200998 struct trx_ul_burst_ind bi;
999
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001000 if (!pullRadioVector(chan, &bi))
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +02001001 return;
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001002 if (!bi.idle)
1003 logRxBurst(chan, &bi);
dburgessb3a0ca42011-10-12 07:44:40 +00001004
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001005 switch (mVersionTRXD) {
1006 case 0:
1007 trxd_send_burst_ind_v0(chan, mDataSockets[chan], &bi);
1008 break;
Pau Espin Pedrolcf6113b2019-07-01 20:42:53 +02001009 case 1:
1010 trxd_send_burst_ind_v1(chan, mDataSockets[chan], &bi);
1011 break;
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +02001012 default:
1013 OSMO_ASSERT(false);
1014 }
dburgessb3a0ca42011-10-12 07:44:40 +00001015}
1016
Thomas Tsou204a9f12013-10-29 18:34:16 -04001017void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +00001018{
1019
1020 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001021 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +00001022 assure that transmit packets arrive at the radio/USRP
1023 before they need to be transmitted.
1024
1025 Deadline clock indicates the burst that needs to be
1026 pushed into the FIFO right NOW. If transmit queue does
1027 not have a burst, stick in filler data.
1028 */
1029
1030
1031 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001032
dburgessb3a0ca42011-10-12 07:44:40 +00001033 if (mOn) {
1034 //radioClock->wait(); // wait until clock updates
1035 LOG(DEBUG) << "radio clock " << radioClock->get();
1036 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1037 // if underrun, then we're not providing bursts to radio/USRP fast
1038 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001039 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001040 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001041 // only update latency at the defined frame interval
1042 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001043 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol74bcc562018-10-02 17:30:28 +02001044 LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
1045 << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001046 mLatencyUpdateTime = radioClock->get();
1047 }
1048 }
1049 else {
1050 // if underrun hasn't occurred in the last sec (216 frames) drop
1051 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001052 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001053 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1054 mTransmitLatency.decTN();
1055 LOG(INFO) << "reduced latency: " << mTransmitLatency;
1056 mLatencyUpdateTime = radioClock->get();
1057 }
1058 }
1059 }
dburgessb3a0ca42011-10-12 07:44:40 +00001060 }
dburgessb3a0ca42011-10-12 07:44:40 +00001061 // time to push burst to transmit FIFO
1062 pushRadioVector(mTransmitDeadlineClock);
1063 mTransmitDeadlineClock.incTN();
1064 }
dburgessb3a0ca42011-10-12 07:44:40 +00001065 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001066
1067 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001068}
1069
1070
1071
1072void Transceiver::writeClockInterface()
1073{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001074 int msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001075 char command[50];
1076 // FIXME -- This should be adaptive.
1077 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1078
1079 LOG(INFO) << "ClockInterface: sending " << command;
1080
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001081 msgLen = write(mClockSocket, command, strlen(command) + 1);
1082 if (msgLen <= 0)
1083 LOG(WARNING) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001084
1085 mLastClockUpdateTime = mTransmitDeadlineClock;
1086
Thomas Tsou92c16df2013-09-28 18:04:19 -04001087}
dburgessb3a0ca42011-10-12 07:44:40 +00001088
Thomas Tsou204a9f12013-10-29 18:34:16 -04001089void *RxUpperLoopAdapter(TransceiverChannel *chan)
1090{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001091 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001092 Transceiver *trx = chan->trx;
1093 size_t num = chan->num;
1094
1095 delete chan;
1096
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001097 snprintf(thread_name, 16, "RxUpper%zu", num);
1098 set_selfthread_name(thread_name);
1099
Thomas Tsou7553aa92013-11-08 12:50:03 -05001100 trx->setPriority(0.42);
1101
Thomas Tsou204a9f12013-10-29 18:34:16 -04001102 while (1) {
1103 trx->driveReceiveFIFO(num);
1104 pthread_testcancel();
1105 }
1106 return NULL;
1107}
1108
1109void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001110{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001111 set_selfthread_name("RxLower");
1112
Thomas Tsou7553aa92013-11-08 12:50:03 -05001113 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +00001114
dburgessb3a0ca42011-10-12 07:44:40 +00001115 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001116 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001117 pthread_testcancel();
1118 }
1119 return NULL;
1120}
1121
Thomas Tsou204a9f12013-10-29 18:34:16 -04001122void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001123{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001124 set_selfthread_name("TxLower");
1125
Thomas Tsou7553aa92013-11-08 12:50:03 -05001126 transceiver->setPriority(0.44);
1127
Thomas Tsou92c16df2013-09-28 18:04:19 -04001128 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001129 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001130 pthread_testcancel();
1131 }
1132 return NULL;
1133}
1134
Thomas Tsou204a9f12013-10-29 18:34:16 -04001135void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001136{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001137 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001138 Transceiver *trx = chan->trx;
1139 size_t num = chan->num;
1140
1141 delete chan;
1142
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001143 snprintf(thread_name, 16, "CtrlService%zu", num);
1144 set_selfthread_name(thread_name);
1145
dburgessb3a0ca42011-10-12 07:44:40 +00001146 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001147 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001148 pthread_testcancel();
1149 }
1150 return NULL;
1151}
1152
Thomas Tsou204a9f12013-10-29 18:34:16 -04001153void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001154{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001155 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001156 Transceiver *trx = chan->trx;
1157 size_t num = chan->num;
1158
1159 delete chan;
1160
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001161 snprintf(thread_name, 16, "TxUpper%zu", num);
1162 set_selfthread_name(thread_name);
1163
Thomas Tsoua4cf48c2013-11-09 21:44:26 -05001164 trx->setPriority(0.40);
1165
dburgessb3a0ca42011-10-12 07:44:40 +00001166 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001167 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001168 pthread_testcancel();
1169 }
1170 return NULL;
1171}