blob: 6bbf3a3cf28109c350f8a522d8021c9d83a88a41 [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;
621
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500622 /* Select the diversity channel with highest energy */
623 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300624 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500625 if (pow > max) {
626 max = pow;
627 max_i = i;
628 }
629 avg += pow;
630 }
631
632 if (max_i < 0) {
633 LOG(ALERT) << "Received empty burst";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200634 goto ret_idle;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500635 }
636
Thomas Tsou30421a72013-11-13 23:14:48 -0500637 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500638 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500639 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400640
Alexander Chemeris2b542102015-06-08 22:46:38 -0400641 if (type == IDLE) {
642 /* Update noise levels */
643 state->mNoises.insert(avg);
644 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200645 }
Alexander Chemeris2b542102015-06-08 22:46:38 -0400646
Pau Espin Pedrolbe9cd662019-07-03 15:00:56 +0200647 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
648 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
649
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200650 if (type == IDLE)
651 goto ret_idle;
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400652
Pau Espin Pedrol9bb24a12019-07-03 15:43:03 +0200653 max_toa = (type == RACH || type == EXT_RACH) ?
654 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200655
Thomas Tsou30421a72013-11-13 23:14:48 -0500656 /* Detect normal or RACH bursts */
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200657 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, max_toa, &ebp);
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200658 if (rc <= 0) {
659 if (rc == -SIGERR_CLIP)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400660 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200661 else if (rc != SIGERR_NONE)
Alexander Chemeris954b1182015-06-04 15:39:41 -0400662 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200663 goto ret_idle;
dburgessb3a0ca42011-10-12 07:44:40 +0000664 }
dburgessb3a0ca42011-10-12 07:44:40 +0000665
Pau Espin Pedrold6dbb1b2019-07-03 15:23:56 +0200666 type = (CorrType) rc;
Pau Espin Pedrol7ee2d102019-07-04 13:02:12 +0200667 bi->toa = ebp.toa;
668 rxBurst = demodAnyBurst(*burst, mSPSRx, ebp.amp, ebp.toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500669
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200670 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200671 if (rxBurst->size() == EDGE_BURST_NBITS)
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200672 bi->nbits = EDGE_BURST_NBITS;
673 else /* size() here is actually gSlotLen + 8, due to guard periods */
674 bi->nbits = gSlotLen;
675
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200676 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200677 vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits);
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200678
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200679 delete rxBurst;
Thomas Tsou30421a72013-11-13 23:14:48 -0500680 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200681 return true;
Pau Espin Pedrol95c83182019-07-03 16:01:12 +0200682
683ret_idle:
684 bi->idle = true;
685 delete radio_burst;
686 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000687}
688
dburgessb3a0ca42011-10-12 07:44:40 +0000689void Transceiver::reset()
690{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400691 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
692 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000693}
694
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200695
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700696#define MAX_PACKET_LENGTH 100
697
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700698/**
699 * Matches a buffer with a command.
700 * @param buf a buffer to look command in
701 * @param cmd a command to look in buffer
702 * @param params pointer to arguments, or NULL
703 * @return true if command matches, otherwise false
704 */
705static bool match_cmd(char *buf,
706 const char *cmd, char **params)
707{
708 size_t cmd_len = strlen(cmd);
709
710 /* Check a command itself */
711 if (strncmp(buf, cmd, cmd_len))
712 return false;
713
714 /* A command has arguments */
715 if (params != NULL) {
716 /* Make sure there is a space */
717 if (buf[cmd_len] != ' ')
718 return false;
719
720 /* Update external pointer */
721 *params = buf + cmd_len + 1;
722 }
723
724 return true;
725}
726
Thomas Tsou204a9f12013-10-29 18:34:16 -0400727void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000728{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700729 char buffer[MAX_PACKET_LENGTH + 1];
730 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700731 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700732 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400733
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700734 /* Attempt to read from control socket */
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200735 msgLen = read(mCtrlSockets[chan], buffer, MAX_PACKET_LENGTH);
736 if (msgLen <= 0) {
737 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000738 return;
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200739 }
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700740
741 /* Zero-terminate received string */
742 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000743
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700744 /* Verify a command signature */
745 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100746 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000747 return;
748 }
dburgessb3a0ca42011-10-12 07:44:40 +0000749
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700750 /* Set command pointer */
751 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200752 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700753
754 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800755 stop();
756 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700757 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700758 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000759 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700760 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000761 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300762 for (int i = 0; i < 8; i++) {
763 for (int j = 0; j < 8; j++)
764 mHandover[i][j] = false;
765 }
Tom Tsou365bc382016-10-19 15:26:04 -0700766 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700767 } else if (match_cmd(command, "HANDOVER", &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] = true;
774 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
775 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700776 } else if (match_cmd(command, "NOHANDOVER", &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] = false;
783 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
784 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700785 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000786 //set expected maximum time-of-arrival
787 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700788 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300789 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000790 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700791 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300792 //set expected maximum time-of-arrival
793 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700794 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300795 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
796 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700797 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000798 //set expected maximum time-of-arrival
799 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700800 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400801 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000802 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700803 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000804 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500805 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000806 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500807 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000808 }
809 else {
810 sprintf(response,"RSP NOISELEV 1 0");
811 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700812 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800813 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700814 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800815 power = mRadioInterface->setPowerAttenuation(power, chan);
816 mStates[chan].mPower = power;
817 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700818 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800819 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700820 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800821 power = mStates[chan].mPower + step;
822 power = mRadioInterface->setPowerAttenuation(power, chan);
823 mStates[chan].mPower = power;
824 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700825 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000826 // tune receiver
827 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700828 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500829 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400830 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100831 LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000832 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
833 }
834 else
835 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700836 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000837 // tune txmtr
838 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700839 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500840 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400841 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100842 LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000843 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
844 }
845 else
846 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700847 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000848 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500849 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700850 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700851 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500852 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700853 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100854 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000855 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400856 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000857 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700858 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700859 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000860 int corrCode;
861 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700862 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000863 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100864 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000865 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
866 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200867 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400868 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
869 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000870 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700871 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400872 // debug command! may change or disapear without notice
873 // set a mask which bursts to dump to disk
874 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700875 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400876 mWriteBurstToDiskMask = mask;
877 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700878 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100879 LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200880 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000881 }
882
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200883 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200884 msgLen = write(mCtrlSockets[chan], response, strlen(response) + 1);
885 if (msgLen <= 0)
886 LOGCHAN(chan, DTRXCTRL, WARNING) << "mCtrlSockets write(" << mCtrlSockets[chan] << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000887}
888
Thomas Tsou204a9f12013-10-29 18:34:16 -0400889bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000890{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200891 int msgLen;
Tom Tsoue8871082016-07-01 02:46:04 -0700892 int burstLen;
893 char buffer[EDGE_BURST_NBITS + 50];
dburgessb3a0ca42011-10-12 07:44:40 +0000894
895 // check data socket
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +0200896 msgLen = read(mDataSockets[chan], buffer, sizeof(buffer));
897 if (msgLen <= 0) {
898 LOGCHAN(chan, DTRXCTRL, WARNING) << "mDataSockets read(" << mCtrlSockets[chan] << ") failed: " << msgLen;
899 return false;
900 }
dburgessb3a0ca42011-10-12 07:44:40 +0000901
Tom Tsoue8871082016-07-01 02:46:04 -0700902 if (msgLen == gSlotLen + 1 + 4 + 1) {
903 burstLen = gSlotLen;
904 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
905 if (mSPSTx != 4)
906 return false;
907
908 burstLen = EDGE_BURST_NBITS;
909 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000910 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
911 return false;
912 }
913
914 int timeSlot = (int) buffer[0];
915 uint64_t frameNum = 0;
916 for (int i = 0; i < 4; i++)
917 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
dburgessb3a0ca42011-10-12 07:44:40 +0000918
dburgessb3a0ca42011-10-12 07:44:40 +0000919 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200920
dburgessb3a0ca42011-10-12 07:44:40 +0000921 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700922 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000923 BitVector::iterator itr = newBurst.begin();
924 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200925 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000926 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200927
dburgessb3a0ca42011-10-12 07:44:40 +0000928 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400929
930 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000931
932 return true;
933
934
935}
dburgessb3a0ca42011-10-12 07:44:40 +0000936
Thomas Tsou204a9f12013-10-29 18:34:16 -0400937void Transceiver::driveReceiveRadio()
938{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200939 int rc = mRadioInterface->driveReceiveRadio();
940 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400941 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200942 } else if (rc < 0) {
943 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
Pau Espin Pedrolb426e4a2019-06-04 12:39:28 +0200944 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200945 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
946 mForceClockInterface = false;
947 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -0400948 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400949}
950
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200951void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800952{
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200953 std::ostringstream os;
954 for (size_t i=0; i < bi->nbits; i++) {
955 if (bi->rx_burst[i] > 0.5) os << "1";
956 else if (bi->rx_burst[i] > 0.25) os << "|";
957 else if (bi->rx_burst[i] > 0.0) os << "'";
958 else os << "-";
959 }
960
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800961 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +0300962 << " chan: " << chan
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200963 << " time: " << bi->tn << ":" << bi->fn
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200964 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
965 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
966 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
967 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200968 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
Pau Espin Pedrol7dc07b92019-07-01 17:55:01 +0200969 << " bits: " << os;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800970}
971
Thomas Tsou204a9f12013-10-29 18:34:16 -0400972void Transceiver::driveReceiveFIFO(size_t chan)
973{
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200974 struct trx_ul_burst_ind bi;
975
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +0200976 if (!pullRadioVector(chan, &bi))
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200977 return;
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +0200978 if (!bi.idle)
979 logRxBurst(chan, &bi);
dburgessb3a0ca42011-10-12 07:44:40 +0000980
Pau Espin Pedrol15fa64b2019-07-01 20:41:55 +0200981 switch (mVersionTRXD) {
982 case 0:
983 trxd_send_burst_ind_v0(chan, mDataSockets[chan], &bi);
984 break;
985 default:
986 OSMO_ASSERT(false);
987 }
dburgessb3a0ca42011-10-12 07:44:40 +0000988}
989
Thomas Tsou204a9f12013-10-29 18:34:16 -0400990void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +0000991{
992
993 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200994 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +0000995 assure that transmit packets arrive at the radio/USRP
996 before they need to be transmitted.
997
998 Deadline clock indicates the burst that needs to be
999 pushed into the FIFO right NOW. If transmit queue does
1000 not have a burst, stick in filler data.
1001 */
1002
1003
1004 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +02001005
dburgessb3a0ca42011-10-12 07:44:40 +00001006 if (mOn) {
1007 //radioClock->wait(); // wait until clock updates
1008 LOG(DEBUG) << "radio clock " << radioClock->get();
1009 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
1010 // if underrun, then we're not providing bursts to radio/USRP fast
1011 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -04001012 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001013 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +00001014 // only update latency at the defined frame interval
1015 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001016 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol74bcc562018-10-02 17:30:28 +02001017 LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
1018 << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +00001019 mLatencyUpdateTime = radioClock->get();
1020 }
1021 }
1022 else {
1023 // if underrun hasn't occurred in the last sec (216 frames) drop
1024 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001025 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001026 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1027 mTransmitLatency.decTN();
1028 LOG(INFO) << "reduced latency: " << mTransmitLatency;
1029 mLatencyUpdateTime = radioClock->get();
1030 }
1031 }
1032 }
dburgessb3a0ca42011-10-12 07:44:40 +00001033 }
dburgessb3a0ca42011-10-12 07:44:40 +00001034 // time to push burst to transmit FIFO
1035 pushRadioVector(mTransmitDeadlineClock);
1036 mTransmitDeadlineClock.incTN();
1037 }
dburgessb3a0ca42011-10-12 07:44:40 +00001038 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001039
1040 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001041}
1042
1043
1044
1045void Transceiver::writeClockInterface()
1046{
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001047 int msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001048 char command[50];
1049 // FIXME -- This should be adaptive.
1050 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1051
1052 LOG(INFO) << "ClockInterface: sending " << command;
1053
Pau Espin Pedrolb9d25152019-07-01 19:03:49 +02001054 msgLen = write(mClockSocket, command, strlen(command) + 1);
1055 if (msgLen <= 0)
1056 LOG(WARNING) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen;
dburgessb3a0ca42011-10-12 07:44:40 +00001057
1058 mLastClockUpdateTime = mTransmitDeadlineClock;
1059
Thomas Tsou92c16df2013-09-28 18:04:19 -04001060}
dburgessb3a0ca42011-10-12 07:44:40 +00001061
Thomas Tsou204a9f12013-10-29 18:34:16 -04001062void *RxUpperLoopAdapter(TransceiverChannel *chan)
1063{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001064 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001065 Transceiver *trx = chan->trx;
1066 size_t num = chan->num;
1067
1068 delete chan;
1069
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001070 snprintf(thread_name, 16, "RxUpper%zu", num);
1071 set_selfthread_name(thread_name);
1072
Thomas Tsou7553aa92013-11-08 12:50:03 -05001073 trx->setPriority(0.42);
1074
Thomas Tsou204a9f12013-10-29 18:34:16 -04001075 while (1) {
1076 trx->driveReceiveFIFO(num);
1077 pthread_testcancel();
1078 }
1079 return NULL;
1080}
1081
1082void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001083{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001084 set_selfthread_name("RxLower");
1085
Thomas Tsou7553aa92013-11-08 12:50:03 -05001086 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +00001087
dburgessb3a0ca42011-10-12 07:44:40 +00001088 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001089 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001090 pthread_testcancel();
1091 }
1092 return NULL;
1093}
1094
Thomas Tsou204a9f12013-10-29 18:34:16 -04001095void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001096{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001097 set_selfthread_name("TxLower");
1098
Thomas Tsou7553aa92013-11-08 12:50:03 -05001099 transceiver->setPriority(0.44);
1100
Thomas Tsou92c16df2013-09-28 18:04:19 -04001101 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001102 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001103 pthread_testcancel();
1104 }
1105 return NULL;
1106}
1107
Thomas Tsou204a9f12013-10-29 18:34:16 -04001108void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001109{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001110 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001111 Transceiver *trx = chan->trx;
1112 size_t num = chan->num;
1113
1114 delete chan;
1115
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001116 snprintf(thread_name, 16, "CtrlService%zu", num);
1117 set_selfthread_name(thread_name);
1118
dburgessb3a0ca42011-10-12 07:44:40 +00001119 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001120 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001121 pthread_testcancel();
1122 }
1123 return NULL;
1124}
1125
Thomas Tsou204a9f12013-10-29 18:34:16 -04001126void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001127{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001128 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001129 Transceiver *trx = chan->trx;
1130 size_t num = chan->num;
1131
1132 delete chan;
1133
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001134 snprintf(thread_name, 16, "TxUpper%zu", num);
1135 set_selfthread_name(thread_name);
1136
Thomas Tsoua4cf48c2013-11-09 21:44:26 -05001137 trx->setPriority(0.40);
1138
dburgessb3a0ca42011-10-12 07:44:40 +00001139 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001140 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001141 pthread_testcancel();
1142 }
1143 return NULL;
1144}