blob: c3896bd2187e2e40a9c25c06d060877f7647385a [file] [log] [blame]
dburgessb3a0ca42011-10-12 07:44:40 +00001/*
2* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
3*
4* This software is distributed under the terms of the GNU Public License.
5* See the COPYING file in the main directory for details.
6*
7* This use of this software may be subject to additional restrictions.
8* See the LEGAL file in the main directory for details.
9
10 This program is free software: you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation, either version 3 of the License, or
13 (at your option) any later version.
14
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
19
20 You should have received a copy of the GNU General Public License
21 along with this program. If not, see <http://www.gnu.org/licenses/>.
22*/
23
dburgessb3a0ca42011-10-12 07:44:40 +000024#include <stdio.h>
Alexander Chemerise8905a02015-06-03 23:47:56 -040025#include <iomanip> // std::setprecision
Alexander Chemerise692ce92015-06-12 00:15:31 -040026#include <fstream>
dburgessb3a0ca42011-10-12 07:44:40 +000027#include "Transceiver.h"
28#include <Logger.h>
29
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020030extern "C" {
31#include "osmo_signal.h"
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +020032#include "proto_trxd.h"
33
34#include <osmocom/core/bits.h>
Pau Espin Pedroldb936b92018-09-03 16:50:49 +020035}
36
ttsou2173abf2012-08-08 00:51:31 +000037#ifdef HAVE_CONFIG_H
38#include "config.h"
39#endif
dburgessb3a0ca42011-10-12 07:44:40 +000040
Alexander Chemerisd734e2d2013-06-16 14:30:58 +040041using namespace GSM;
42
kurtis.heimerlec842de2012-11-23 08:37:32 +000043#define USB_LATENCY_INTRVL 10,0
ttsou2173abf2012-08-08 00:51:31 +000044
Thomas Tsoufa3a7872013-10-17 21:23:34 -040045/* Number of running values use in noise average */
46#define NOISE_CNT 20
ttsoue8dde022012-12-06 15:43:55 +000047
Thomas Tsouf0782732013-10-29 15:55:47 -040048TransceiverState::TransceiverState()
Tom Tsoua4d1a412014-11-25 15:46:56 -080049 : mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
Thomas Tsouf0782732013-10-29 15:55:47 -040050{
51 for (int i = 0; i < 8; i++) {
52 chanType[i] = Transceiver::NONE;
53 fillerModulus[i] = 26;
54 chanResponse[i] = NULL;
55 DFEForward[i] = NULL;
56 DFEFeedback[i] = NULL;
57
58 for (int n = 0; n < 102; n++)
59 fillerTable[n][i] = NULL;
60 }
61}
62
63TransceiverState::~TransceiverState()
64{
65 for (int i = 0; i < 8; i++) {
66 delete chanResponse[i];
67 delete DFEForward[i];
68 delete DFEFeedback[i];
69
70 for (int n = 0; n < 102; n++)
71 delete fillerTable[n][i];
72 }
73}
74
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010075bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
Tom Tsou64ad7122015-05-19 18:26:31 -070076{
Tom Tsou64ad7122015-05-19 18:26:31 -070077 signalVector *burst;
78
79 if ((sps != 1) && (sps != 4))
80 return false;
81
82 for (size_t n = 0; n < 8; n++) {
Tom Tsou64ad7122015-05-19 18:26:31 -070083 for (size_t i = 0; i < 102; i++) {
84 switch (filler) {
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010085 case FILLER_DUMMY:
Tom Tsou8ee2f382016-03-06 20:57:34 -080086 burst = generateDummyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070087 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010088 case FILLER_NORM_RAND:
Tom Tsou8ee2f382016-03-06 20:57:34 -080089 burst = genRandNormalBurst(rtsc, sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -070090 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010091 case FILLER_EDGE_RAND:
Tom Tsouaf717b22016-03-06 22:19:15 -080092 burst = generateEdgeBurst(rtsc);
93 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010094 case FILLER_ACCESS_RAND:
Alexander Chemeris37c52c72016-03-25 18:28:34 +030095 burst = genRandAccessBurst(rach_delay, sps, n);
Alexander Chemeris5efe0502016-03-23 17:06:32 +030096 break;
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +010097 case FILLER_ZERO:
Tom Tsou64ad7122015-05-19 18:26:31 -070098 default:
Tom Tsou8ee2f382016-03-06 20:57:34 -080099 burst = generateEmptyBurst(sps, n);
Tom Tsou64ad7122015-05-19 18:26:31 -0700100 }
101
102 scaleVector(*burst, scale);
103 fillerTable[i][n] = burst;
104 }
105
Pau Espin Pedrolefac20b2018-02-21 14:59:19 +0100106 if ((filler == FILLER_NORM_RAND) ||
107 (filler == FILLER_EDGE_RAND)) {
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700108 chanType[n] = TSC;
Tom Tsouaf717b22016-03-06 22:19:15 -0800109 }
Thomas Tsou15d743e2014-01-25 02:34:03 -0500110 }
Tom Tsou64ad7122015-05-19 18:26:31 -0700111
112 return false;
Thomas Tsouf0782732013-10-29 15:55:47 -0400113}
114
dburgessb3a0ca42011-10-12 07:44:40 +0000115Transceiver::Transceiver(int wBasePort,
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200116 const char *TRXAddress,
117 const char *GSMcoreAddress,
Tom Tsou5cd70dc2016-03-06 01:28:40 -0800118 size_t tx_sps, size_t rx_sps, size_t chans,
Alexander Chemerise8905a02015-06-03 23:47:56 -0400119 GSM::Time wTransmitLatency,
120 RadioInterface *wRadioInterface,
Eric Wildac0487e2019-06-17 13:02:44 +0200121 double wRssiOffset, int wStackSize)
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200122 : mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
123 mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100),
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800124 mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
Eric Wildac0487e2019-06-17 13:02:44 +0200125 rssiOffset(wRssiOffset), stackSize(wStackSize),
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200126 mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false),
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300127 mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
128 mWriteBurstToDiskMask(0)
dburgessb3a0ca42011-10-12 07:44:40 +0000129{
dburgessb3a0ca42011-10-12 07:44:40 +0000130 txFullScale = mRadioInterface->fullScaleInputValue();
131 rxFullScale = mRadioInterface->fullScaleOutputValue();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300132
133 for (int i = 0; i < 8; i++) {
134 for (int j = 0; j < 8; j++)
135 mHandover[i][j] = false;
136 }
dburgessb3a0ca42011-10-12 07:44:40 +0000137}
138
139Transceiver::~Transceiver()
140{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800141 stop();
142
dburgessb3a0ca42011-10-12 07:44:40 +0000143 sigProcLibDestroy();
Thomas Tsoud647ec52013-10-29 15:17:34 -0400144
Thomas Tsou204a9f12013-10-29 18:34:16 -0400145 for (size_t i = 0; i < mChans; i++) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800146 mControlServiceLoopThreads[i]->cancel();
147 mControlServiceLoopThreads[i]->join();
148 delete mControlServiceLoopThreads[i];
149
Thomas Tsou204a9f12013-10-29 18:34:16 -0400150 mTxPriorityQueues[i].clear();
151 delete mCtrlSockets[i];
152 delete mDataSockets[i];
153 }
dburgessb3a0ca42011-10-12 07:44:40 +0000154}
Thomas Tsou83e06892013-08-20 16:10:01 -0400155
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800156/*
157 * Initialize transceiver
158 *
159 * Start or restart the control loop. Any further control is handled through the
160 * socket API. Randomize the central radio clock set the downlink burst
161 * counters. Note that the clock will not update until the radio starts, but we
162 * are still expected to report clock indications through control channel
163 * activity.
164 */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200165bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
166 bool edge, bool ext_rach)
Thomas Tsou83e06892013-08-20 16:10:01 -0400167{
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500168 int d_srcport, d_dstport, c_srcport, c_dstport;
Thomas Tsouf0782732013-10-29 15:55:47 -0400169
Thomas Tsou204a9f12013-10-29 18:34:16 -0400170 if (!mChans) {
171 LOG(ALERT) << "No channels assigned";
172 return false;
173 }
174
Tom Tsou2079a3c2016-03-06 00:58:56 -0800175 if (!sigProcLibSetup()) {
Thomas Tsou83e06892013-08-20 16:10:01 -0400176 LOG(ALERT) << "Failed to initialize signal processing library";
177 return false;
178 }
179
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200180 mExtRACH = ext_rach;
Tom Tsou64464e62016-07-01 03:46:46 -0700181 mEdge = edge;
182
Thomas Tsou204a9f12013-10-29 18:34:16 -0400183 mDataSockets.resize(mChans);
184 mCtrlSockets.resize(mChans);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400185 mControlServiceLoopThreads.resize(mChans);
186 mTxPriorityQueueServiceLoopThreads.resize(mChans);
187 mRxServiceLoopThreads.resize(mChans);
188
189 mTxPriorityQueues.resize(mChans);
190 mReceiveFIFO.resize(mChans);
191 mStates.resize(mChans);
192
Thomas Tsouccb73e12014-04-15 17:41:28 -0400193 /* Filler table retransmissions - support only on channel 0 */
Tom Tsou64ad7122015-05-19 18:26:31 -0700194 if (filler == FILLER_DUMMY)
Thomas Tsouccb73e12014-04-15 17:41:28 -0400195 mStates[0].mRetrans = true;
196
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800197 /* Setup sockets */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400198 for (size_t i = 0; i < mChans; i++) {
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500199 c_srcport = mBasePort + 2 * i + 1;
200 c_dstport = mBasePort + 2 * i + 101;
201 d_srcport = mBasePort + 2 * i + 2;
202 d_dstport = mBasePort + 2 * i + 102;
203
Pau Espin Pedrol8c800952017-08-16 16:53:23 +0200204 mCtrlSockets[i] = new UDPSocket(mLocalAddr.c_str(), c_srcport, mRemoteAddr.c_str(), c_dstport);
205 mDataSockets[i] = new UDPSocket(mLocalAddr.c_str(), d_srcport, mRemoteAddr.c_str(), d_dstport);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400206 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400207
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800208 /* Randomize the central clock */
209 GSM::Time startTime(random() % gHyperframe, 0);
210 mRadioInterface->getClock()->set(startTime);
211 mTransmitDeadlineClock = startTime;
212 mLastClockUpdateTime = startTime;
213 mLatencyUpdateTime = startTime;
214
215 /* Start control threads */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400216 for (size_t i = 0; i < mChans; i++) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800217 TransceiverChannel *chan = new TransceiverChannel(this, i);
Eric Wildac0487e2019-06-17 13:02:44 +0200218 mControlServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800219 mControlServiceLoopThreads[i]->start((void * (*)(void*))
220 ControlServiceLoopAdapter, (void*) chan);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400221
Tom Tsou64ad7122015-05-19 18:26:31 -0700222 if (i && filler == FILLER_DUMMY)
223 filler = FILLER_ZERO;
224
Alexander Chemeris37c52c72016-03-25 18:28:34 +0300225 mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
Thomas Tsou83e06892013-08-20 16:10:01 -0400226 }
227
228 return true;
229}
dburgessb3a0ca42011-10-12 07:44:40 +0000230
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800231/*
232 * Start the transceiver
233 *
234 * Submit command(s) to the radio device to commence streaming samples and
235 * launch threads to handle sample I/O. Re-synchronize the transmit burst
236 * counters to the central radio clock here as well.
237 */
238bool Transceiver::start()
239{
240 ScopedLock lock(mLock);
241
242 if (mOn) {
243 LOG(ERR) << "Transceiver already running";
Ivan Kluchnikov194a9b12015-04-23 17:08:27 +0300244 return true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800245 }
246
247 LOG(NOTICE) << "Starting the transceiver";
248
249 GSM::Time time = mRadioInterface->getClock()->get();
250 mTransmitDeadlineClock = time;
251 mLastClockUpdateTime = time;
252 mLatencyUpdateTime = time;
253
254 if (!mRadioInterface->start()) {
255 LOG(ALERT) << "Device failed to start";
256 return false;
257 }
258
259 /* Device is running - launch I/O threads */
Eric Wildac0487e2019-06-17 13:02:44 +0200260 mRxLowerLoopThread = new Thread(stackSize);
261 mTxLowerLoopThread = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800262 mTxLowerLoopThread->start((void * (*)(void*))
263 TxLowerLoopAdapter,(void*) this);
264 mRxLowerLoopThread->start((void * (*)(void*))
265 RxLowerLoopAdapter,(void*) this);
266
267 /* Launch uplink and downlink burst processing threads */
268 for (size_t i = 0; i < mChans; i++) {
269 TransceiverChannel *chan = new TransceiverChannel(this, i);
Eric Wildac0487e2019-06-17 13:02:44 +0200270 mRxServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800271 mRxServiceLoopThreads[i]->start((void * (*)(void*))
272 RxUpperLoopAdapter, (void*) chan);
273
274 chan = new TransceiverChannel(this, i);
Eric Wildac0487e2019-06-17 13:02:44 +0200275 mTxPriorityQueueServiceLoopThreads[i] = new Thread(stackSize);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800276 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
277 TxUpperLoopAdapter, (void*) chan);
278 }
279
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200280 mForceClockInterface = true;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800281 mOn = true;
282 return true;
283}
284
285/*
286 * Stop the transceiver
287 *
288 * Perform stopping by disabling receive streaming and issuing cancellation
289 * requests to running threads. Most threads will timeout and terminate once
290 * device is disabled, but the transmit loop may block waiting on the central
291 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
292 * makes it to the thread cancellation point.
293 */
294void Transceiver::stop()
295{
296 ScopedLock lock(mLock);
297
298 if (!mOn)
299 return;
300
301 LOG(NOTICE) << "Stopping the transceiver";
302 mTxLowerLoopThread->cancel();
303 mRxLowerLoopThread->cancel();
Tom Tsoud67bd602017-06-15 15:35:02 -0700304 mTxLowerLoopThread->join();
305 mRxLowerLoopThread->join();
306 delete mTxLowerLoopThread;
307 delete mRxLowerLoopThread;
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800308
309 for (size_t i = 0; i < mChans; i++) {
310 mRxServiceLoopThreads[i]->cancel();
311 mTxPriorityQueueServiceLoopThreads[i]->cancel();
312 }
313
314 LOG(INFO) << "Stopping the device";
315 mRadioInterface->stop();
316
317 for (size_t i = 0; i < mChans; i++) {
318 mRxServiceLoopThreads[i]->join();
319 mTxPriorityQueueServiceLoopThreads[i]->join();
320 delete mRxServiceLoopThreads[i];
321 delete mTxPriorityQueueServiceLoopThreads[i];
322
323 mTxPriorityQueues[i].clear();
324 }
325
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800326 mOn = false;
327 LOG(NOTICE) << "Transceiver stopped";
328}
329
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500330void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400331 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000332{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500333 signalVector *burst;
334 radioVector *radio_burst;
335
Thomas Tsou204a9f12013-10-29 18:34:16 -0400336 if (chan >= mTxPriorityQueues.size()) {
337 LOG(ALERT) << "Invalid channel " << chan;
338 return;
339 }
340
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500341 if (wTime.TN() > 7) {
342 LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
343 return;
344 }
345
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800346 /* Use the number of bits as the EDGE burst indicator */
347 if (bits.size() == EDGE_BURST_NBITS)
348 burst = modulateEdgeBurst(bits, mSPSTx);
349 else
350 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
351
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500352 scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
dburgessb3a0ca42011-10-12 07:44:40 +0000353
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500354 radio_burst = new radioVector(wTime, burst);
355
356 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000357}
358
Thomas Tsou15d743e2014-01-25 02:34:03 -0500359void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
360{
361 int TN, modFN;
362 TransceiverState *state = &mStates[chan];
363
364 TN = burst->getTime().TN();
365 modFN = burst->getTime().FN() % state->fillerModulus[TN];
366
367 delete state->fillerTable[modFN][TN];
368 state->fillerTable[modFN][TN] = burst->getVector();
369 burst->setVector(NULL);
370}
371
dburgessb3a0ca42011-10-12 07:44:40 +0000372void Transceiver::pushRadioVector(GSM::Time &nowTime)
373{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400374 int TN, modFN;
375 radioVector *burst;
376 TransceiverState *state;
377 std::vector<signalVector *> bursts(mChans);
378 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500379 std::vector<bool> filler(mChans, true);
dburgessb3a0ca42011-10-12 07:44:40 +0000380
Thomas Tsou204a9f12013-10-29 18:34:16 -0400381 for (size_t i = 0; i < mChans; i ++) {
382 state = &mStates[i];
dburgessb3a0ca42011-10-12 07:44:40 +0000383
Thomas Tsou204a9f12013-10-29 18:34:16 -0400384 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200385 LOGCHAN(i, DMAIN, NOTICE) << "dumping STALE burst in TRX->SDR interface ("
Pau Espin Pedrolf37b0ad2018-04-25 18:01:27 +0200386 << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans;
Thomas Tsou15d743e2014-01-25 02:34:03 -0500387 if (state->mRetrans)
388 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500389 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400390 }
391
392 TN = nowTime.TN();
393 modFN = nowTime.FN() % state->fillerModulus[TN];
394
395 bursts[i] = state->fillerTable[modFN][TN];
396 zeros[i] = state->chanType[TN] == NONE;
397
398 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500399 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500400
401 if (state->mRetrans) {
402 updateFillerTable(i, burst);
403 } else {
404 burst->setVector(NULL);
405 filler[i] = false;
406 }
407
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500408 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400409 }
dburgessb3a0ca42011-10-12 07:44:40 +0000410 }
411
Thomas Tsou204a9f12013-10-29 18:34:16 -0400412 mRadioInterface->driveTransmitRadio(bursts, zeros);
413
Thomas Tsou15d743e2014-01-25 02:34:03 -0500414 for (size_t i = 0; i < mChans; i++) {
415 if (!filler[i])
416 delete bursts[i];
417 }
dburgessb3a0ca42011-10-12 07:44:40 +0000418}
419
Thomas Tsou204a9f12013-10-29 18:34:16 -0400420void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000421{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400422 TransceiverState *state = &mStates[chan];
423
424 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000425 case NONE:
426 case I:
427 case II:
428 case III:
429 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400430 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000431 break;
432 case IV:
433 case VI:
434 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400435 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000436 break;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200437 //case V:
dburgessb3a0ca42011-10-12 07:44:40 +0000438 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400439 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000440 break;
ttsoufc40a842013-06-09 22:38:18 +0000441 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400442 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000443 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000444 default:
445 break;
446 }
447}
448
449
Alexander Chemerisf9e78be2017-03-17 15:00:34 -0700450CorrType Transceiver::expectedCorrType(GSM::Time currTime,
451 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000452{
Alexander Chemeris5a068062015-06-20 01:38:47 +0300453 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 };
454 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,
455 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 };
456 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,
457 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 -0400458 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000459 unsigned burstTN = currTime.TN();
460 unsigned burstFN = currTime.FN();
Alexander Chemeris5a068062015-06-20 01:38:47 +0300461 int subch;
dburgessb3a0ca42011-10-12 07:44:40 +0000462
Thomas Tsou204a9f12013-10-29 18:34:16 -0400463 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000464 case NONE:
465 return OFF;
466 break;
467 case FILL:
468 return IDLE;
469 break;
470 case I:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300471 // TODO: Are we expecting RACH on an IDLE frame?
472/* if (burstFN % 26 == 25)
473 return IDLE;*/
474 if (mHandover[burstTN][0])
475 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000476 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000477 break;
478 case II:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300479 subch = tchh_subslot[burstFN % 26];
480 if (subch == 1)
481 return IDLE;
482 if (mHandover[burstTN][0])
483 return RACH;
ttsou20642972013-03-27 22:00:25 +0000484 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000485 break;
486 case III:
Alexander Chemeris5a068062015-06-20 01:38:47 +0300487 subch = tchh_subslot[burstFN % 26];
488 if (mHandover[burstTN][subch])
489 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000490 return TSC;
491 break;
492 case IV:
493 case VI:
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200494 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000495 break;
496 case V: {
497 int mod51 = burstFN % 51;
498 if ((mod51 <= 36) && (mod51 >= 14))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200499 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000500 else if ((mod51 == 4) || (mod51 == 5))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200501 return mExtRACH ? EXT_RACH : RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000502 else if ((mod51 == 45) || (mod51 == 46))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200503 return mExtRACH ? EXT_RACH : RACH;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300504 else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
505 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000506 else
507 return TSC;
508 break;
509 }
510 case VII:
511 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
512 return IDLE;
Alexander Chemeris5a068062015-06-20 01:38:47 +0300513 else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
514 return RACH;
dburgessb3a0ca42011-10-12 07:44:40 +0000515 else
516 return TSC;
517 break;
ttsoufc40a842013-06-09 22:38:18 +0000518 case XIII: {
519 int mod52 = burstFN % 52;
520 if ((mod52 == 12) || (mod52 == 38))
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200521 return mExtRACH ? EXT_RACH : RACH;
ttsoufc40a842013-06-09 22:38:18 +0000522 else if ((mod52 == 25) || (mod52 == 51))
523 return IDLE;
524 else
525 return TSC;
526 break;
527 }
dburgessb3a0ca42011-10-12 07:44:40 +0000528 case LOOPBACK:
529 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
530 return IDLE;
531 else
532 return TSC;
533 break;
534 default:
535 return OFF;
536 break;
537 }
dburgessb3a0ca42011-10-12 07:44:40 +0000538}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400539
Alexander Chemerise692ce92015-06-12 00:15:31 -0400540void writeToFile(radioVector *radio_burst, size_t chan)
541{
542 GSM::Time time = radio_burst->getTime();
543 std::ostringstream fname;
544 fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
545 std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
546 outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
547 outfile.close();
548}
549
Thomas Tsou30421a72013-11-13 23:14:48 -0500550/*
551 * Pull bursts from the FIFO and handle according to the slot
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200552 * and burst correlation type. Equalzation is currently disabled.
Thomas Tsou30421a72013-11-13 23:14:48 -0500553 */
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200554bool Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
dburgessb3a0ca42011-10-12 07:44:40 +0000555{
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800556 int rc;
Thomas Tsou30421a72013-11-13 23:14:48 -0500557 complex amp;
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300558 float toa, max = -1.0, avg = 0.0;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500559 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500560 signalVector *burst;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500561 TransceiverState *state = &mStates[chan];
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200562 bi->rssi_valid = false;
dburgessb3a0ca42011-10-12 07:44:40 +0000563
Thomas Tsou30421a72013-11-13 23:14:48 -0500564 /* Blocking FIFO read */
565 radioVector *radio_burst = mReceiveFIFO[chan]->read();
566 if (!radio_burst)
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200567 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000568
Thomas Tsou30421a72013-11-13 23:14:48 -0500569 /* Set time and determine correlation type */
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200570 bi->burstTime = radio_burst->getTime();
571 CorrType type = expectedCorrType(bi->burstTime, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000572
Tom Tsou64464e62016-07-01 03:46:46 -0700573 /* Enable 8-PSK burst detection if EDGE is enabled */
574 if (mEdge && (type == TSC))
575 type = EDGE;
576
Alexander Chemerise692ce92015-06-12 00:15:31 -0400577 /* Debug: dump bursts to disk */
578 /* bits 0-7 - chan 0 timeslots
579 * bits 8-15 - chan 1 timeslots */
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200580 if (mWriteBurstToDiskMask & ((1<<bi->burstTime.TN()) << (8*chan)))
Alexander Chemerise692ce92015-06-12 00:15:31 -0400581 writeToFile(radio_burst, chan);
582
Alexander Chemeris2b542102015-06-08 22:46:38 -0400583 /* No processing if the timeslot is off.
584 * Not even power level or noise calculation. */
585 if (type == OFF) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500586 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200587 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000588 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000589
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500590 /* Select the diversity channel with highest energy */
591 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300592 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500593 if (pow > max) {
594 max = pow;
595 max_i = i;
596 }
597 avg += pow;
598 }
599
600 if (max_i < 0) {
601 LOG(ALERT) << "Received empty burst";
602 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200603 return false;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500604 }
605
Thomas Tsou30421a72013-11-13 23:14:48 -0500606 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500607 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500608 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400609
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200610 bi->rssi = 20.0 * log10(rxFullScale / avg);
Alexander Chemeris2b542102015-06-08 22:46:38 -0400611
612 /* RSSI estimation are valid */
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200613 bi->rssi_valid = true;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400614
615 if (type == IDLE) {
616 /* Update noise levels */
617 state->mNoises.insert(avg);
618 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200619 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
Alexander Chemeris2b542102015-06-08 22:46:38 -0400620
621 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200622 return false;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400623 } else {
624 /* Do not update noise levels */
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200625 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
Alexander Chemeris2b542102015-06-08 22:46:38 -0400626 }
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400627
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200628 unsigned max_toa = (type == RACH || type == EXT_RACH) ?
629 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
630
Thomas Tsou30421a72013-11-13 23:14:48 -0500631 /* Detect normal or RACH bursts */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200632 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa, max_toa);
Thomas Tsouf0782732013-10-29 15:55:47 -0400633
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800634 if (rc > 0) {
635 type = (CorrType) rc;
636 } else if (rc <= 0) {
637 if (rc == -SIGERR_CLIP) {
Alexander Chemeris954b1182015-06-04 15:39:41 -0400638 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800639 } else if (rc != SIGERR_NONE) {
Alexander Chemeris954b1182015-06-04 15:39:41 -0400640 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Tom Tsou577cd022015-05-18 13:57:54 -0700641 }
642
Thomas Tsou30421a72013-11-13 23:14:48 -0500643 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200644 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000645 }
dburgessb3a0ca42011-10-12 07:44:40 +0000646
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200647 bi->toa = toa;
648 bi->rxBurst = demodAnyBurst(*burst, mSPSRx, amp, toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500649
Thomas Tsou30421a72013-11-13 23:14:48 -0500650 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200651 return true;
dburgessb3a0ca42011-10-12 07:44:40 +0000652}
653
dburgessb3a0ca42011-10-12 07:44:40 +0000654void Transceiver::reset()
655{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400656 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
657 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000658}
659
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200660
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700661#define MAX_PACKET_LENGTH 100
662
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700663/**
664 * Matches a buffer with a command.
665 * @param buf a buffer to look command in
666 * @param cmd a command to look in buffer
667 * @param params pointer to arguments, or NULL
668 * @return true if command matches, otherwise false
669 */
670static bool match_cmd(char *buf,
671 const char *cmd, char **params)
672{
673 size_t cmd_len = strlen(cmd);
674
675 /* Check a command itself */
676 if (strncmp(buf, cmd, cmd_len))
677 return false;
678
679 /* A command has arguments */
680 if (params != NULL) {
681 /* Make sure there is a space */
682 if (buf[cmd_len] != ' ')
683 return false;
684
685 /* Update external pointer */
686 *params = buf + cmd_len + 1;
687 }
688
689 return true;
690}
691
Thomas Tsou204a9f12013-10-29 18:34:16 -0400692void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000693{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700694 char buffer[MAX_PACKET_LENGTH + 1];
695 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700696 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700697 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400698
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700699 /* Attempt to read from control socket */
700 msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH);
701 if (msgLen < 1)
dburgessb3a0ca42011-10-12 07:44:40 +0000702 return;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700703
704 /* Zero-terminate received string */
705 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000706
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700707 /* Verify a command signature */
708 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100709 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000710 return;
711 }
dburgessb3a0ca42011-10-12 07:44:40 +0000712
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700713 /* Set command pointer */
714 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200715 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700716
717 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800718 stop();
719 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700720 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700721 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000722 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700723 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000724 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300725 for (int i = 0; i < 8; i++) {
726 for (int j = 0; j < 8; j++)
727 mHandover[i][j] = false;
728 }
Tom Tsou365bc382016-10-19 15:26:04 -0700729 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700730 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700731 unsigned ts = 0, ss = 0;
732 sscanf(params, "%u %u", &ts, &ss);
733 if (ts > 7 || ss > 7) {
734 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
735 } else {
736 mHandover[ts][ss] = true;
737 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
738 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700739 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700740 unsigned ts = 0, ss = 0;
741 sscanf(params, "%u %u", &ts, &ss);
742 if (ts > 7 || ss > 7) {
743 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
744 } else {
745 mHandover[ts][ss] = false;
746 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
747 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700748 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000749 //set expected maximum time-of-arrival
750 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700751 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300752 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000753 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700754 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300755 //set expected maximum time-of-arrival
756 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700757 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300758 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
759 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700760 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000761 //set expected maximum time-of-arrival
762 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700763 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400764 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000765 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700766 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000767 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500768 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000769 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500770 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000771 }
772 else {
773 sprintf(response,"RSP NOISELEV 1 0");
774 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700775 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800776 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700777 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800778 power = mRadioInterface->setPowerAttenuation(power, chan);
779 mStates[chan].mPower = power;
780 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700781 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800782 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700783 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800784 power = mStates[chan].mPower + step;
785 power = mRadioInterface->setPowerAttenuation(power, chan);
786 mStates[chan].mPower = power;
787 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700788 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000789 // tune receiver
790 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700791 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500792 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400793 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100794 LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000795 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
796 }
797 else
798 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700799 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000800 // tune txmtr
801 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700802 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500803 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400804 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100805 LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000806 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
807 }
808 else
809 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700810 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000811 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500812 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700813 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700814 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500815 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700816 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100817 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000818 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400819 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000820 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700821 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700822 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000823 int corrCode;
824 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700825 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000826 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100827 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000828 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
829 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200830 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400831 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
832 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000833 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700834 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400835 // debug command! may change or disapear without notice
836 // set a mask which bursts to dump to disk
837 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700838 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400839 mWriteBurstToDiskMask = mask;
840 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700841 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100842 LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200843 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000844 }
845
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200846 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400847 mCtrlSockets[chan]->write(response, strlen(response) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +0000848}
849
Thomas Tsou204a9f12013-10-29 18:34:16 -0400850bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000851{
Tom Tsoue8871082016-07-01 02:46:04 -0700852 int burstLen;
853 char buffer[EDGE_BURST_NBITS + 50];
dburgessb3a0ca42011-10-12 07:44:40 +0000854
855 // check data socket
Tom Tsou2c650a62016-04-28 21:55:17 -0700856 size_t msgLen = mDataSockets[chan]->read(buffer, sizeof(buffer));
dburgessb3a0ca42011-10-12 07:44:40 +0000857
Tom Tsoue8871082016-07-01 02:46:04 -0700858 if (msgLen == gSlotLen + 1 + 4 + 1) {
859 burstLen = gSlotLen;
860 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
861 if (mSPSTx != 4)
862 return false;
863
864 burstLen = EDGE_BURST_NBITS;
865 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000866 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
867 return false;
868 }
869
870 int timeSlot = (int) buffer[0];
871 uint64_t frameNum = 0;
872 for (int i = 0; i < 4; i++)
873 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
dburgessb3a0ca42011-10-12 07:44:40 +0000874
dburgessb3a0ca42011-10-12 07:44:40 +0000875 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200876
dburgessb3a0ca42011-10-12 07:44:40 +0000877 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700878 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000879 BitVector::iterator itr = newBurst.begin();
880 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200881 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000882 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200883
dburgessb3a0ca42011-10-12 07:44:40 +0000884 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400885
886 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000887
888 return true;
889
890
891}
dburgessb3a0ca42011-10-12 07:44:40 +0000892
Thomas Tsou204a9f12013-10-29 18:34:16 -0400893void Transceiver::driveReceiveRadio()
894{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200895 int rc = mRadioInterface->driveReceiveRadio();
896 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400897 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200898 } else if (rc < 0) {
899 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
Pau Espin Pedrolb426e4a2019-06-04 12:39:28 +0200900 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200901 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
902 mForceClockInterface = false;
903 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -0400904 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400905}
906
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200907void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi, double dbm)
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800908{
909 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +0300910 << " chan: " << chan
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200911 << " time: " << bi->burstTime
912 << " RSSI: " << std::setw(5) << std::setprecision(1) << bi->rssi
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800913 << "dBFS/" << std::setw(6) << -dbm << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200914 << " noise: " << std::setw(5) << std::setprecision(1) << bi->noise
915 << "dBFS/" << std::setw(6) << -(bi->noise + rssiOffset) << "dBm"
916 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
917 << " bits: " << *(bi->rxBurst);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800918}
919
Thomas Tsou204a9f12013-10-29 18:34:16 -0400920void Transceiver::driveReceiveFIFO(size_t chan)
921{
Alexander Chemerise8905a02015-06-03 23:47:56 -0400922 double dBm; // in dBm
Alexander Chemerise8905a02015-06-03 23:47:56 -0400923 int TOAint; // in 1/256 symbols
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800924 unsigned nbits = gSlotLen;
dburgessb3a0ca42011-10-12 07:44:40 +0000925
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200926 struct trx_ul_burst_ind bi;
927
928 if (!pullRadioVector(chan, &bi))
929 return;
dburgessb3a0ca42011-10-12 07:44:40 +0000930
Alexander Chemerisb61c6102017-03-17 18:22:19 -0700931 // Convert -1..+1 soft bits to 0..1 soft bits
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200932 vectorSlicer(bi.rxBurst);
Alexander Chemerisb61c6102017-03-17 18:22:19 -0700933
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800934 /*
935 * EDGE demodulator returns 444 (148 * 3) bits
936 */
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200937 if (bi.rxBurst->size() == gSlotLen * 3)
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800938 nbits = gSlotLen * 3;
dburgessb3a0ca42011-10-12 07:44:40 +0000939
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200940 dBm = bi.rssi + rssiOffset;
941 logRxBurst(chan, &bi, dBm);
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400942
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200943 TOAint = (int) (bi.toa * 256.0 + 0.5); // round to closest integer
dburgessb3a0ca42011-10-12 07:44:40 +0000944
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200945 char burstString[sizeof(struct trxd_hdr_v0) + nbits + 2];
946 struct trxd_hdr_v0* pkt = (struct trxd_hdr_v0*)burstString;
947 pkt->common.version = 0;
948 pkt->common.reserved = 0;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200949 pkt->common.tn = bi.burstTime.TN();
950 osmo_store32be(bi.burstTime.FN(), &pkt->common.fn);
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200951 pkt->v0.rssi = dBm;
952 osmo_store16be(TOAint, &pkt->v0.toa);
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200953 SoftVector::iterator burstItr = bi.rxBurst->begin();
dburgessb3a0ca42011-10-12 07:44:40 +0000954
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800955 for (unsigned i = 0; i < nbits; i++)
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200956 pkt->soft_bits[i] = (char) round((*burstItr++) * 255.0);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800957
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200958 /* +1: Historical reason. There's an uninitizalied byte in there: pkt->soft_bits[bi.nbits] */
959 pkt->soft_bits[nbits + 1] = '\0';
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200960 delete bi.rxBurst;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800961
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200962 mDataSockets[chan]->write(burstString, sizeof(struct trxd_hdr_v0) + nbits + 2);
dburgessb3a0ca42011-10-12 07:44:40 +0000963}
964
Thomas Tsou204a9f12013-10-29 18:34:16 -0400965void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +0000966{
967
968 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200969 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +0000970 assure that transmit packets arrive at the radio/USRP
971 before they need to be transmitted.
972
973 Deadline clock indicates the burst that needs to be
974 pushed into the FIFO right NOW. If transmit queue does
975 not have a burst, stick in filler data.
976 */
977
978
979 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200980
dburgessb3a0ca42011-10-12 07:44:40 +0000981 if (mOn) {
982 //radioClock->wait(); // wait until clock updates
983 LOG(DEBUG) << "radio clock " << radioClock->get();
984 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
985 // if underrun, then we're not providing bursts to radio/USRP fast
986 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -0400987 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000988 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +0000989 // only update latency at the defined frame interval
990 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000991 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol74bcc562018-10-02 17:30:28 +0200992 LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
993 << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +0000994 mLatencyUpdateTime = radioClock->get();
995 }
996 }
997 else {
998 // if underrun hasn't occurred in the last sec (216 frames) drop
999 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +02001000 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +00001001 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
1002 mTransmitLatency.decTN();
1003 LOG(INFO) << "reduced latency: " << mTransmitLatency;
1004 mLatencyUpdateTime = radioClock->get();
1005 }
1006 }
1007 }
dburgessb3a0ca42011-10-12 07:44:40 +00001008 }
dburgessb3a0ca42011-10-12 07:44:40 +00001009 // time to push burst to transmit FIFO
1010 pushRadioVector(mTransmitDeadlineClock);
1011 mTransmitDeadlineClock.incTN();
1012 }
dburgessb3a0ca42011-10-12 07:44:40 +00001013 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001014
1015 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001016}
1017
1018
1019
1020void Transceiver::writeClockInterface()
1021{
1022 char command[50];
1023 // FIXME -- This should be adaptive.
1024 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1025
1026 LOG(INFO) << "ClockInterface: sending " << command;
1027
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001028 mClockSocket.write(command, strlen(command) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +00001029
1030 mLastClockUpdateTime = mTransmitDeadlineClock;
1031
Thomas Tsou92c16df2013-09-28 18:04:19 -04001032}
dburgessb3a0ca42011-10-12 07:44:40 +00001033
Thomas Tsou204a9f12013-10-29 18:34:16 -04001034void *RxUpperLoopAdapter(TransceiverChannel *chan)
1035{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001036 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001037 Transceiver *trx = chan->trx;
1038 size_t num = chan->num;
1039
1040 delete chan;
1041
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001042 snprintf(thread_name, 16, "RxUpper%zu", num);
1043 set_selfthread_name(thread_name);
1044
Thomas Tsou7553aa92013-11-08 12:50:03 -05001045 trx->setPriority(0.42);
1046
Thomas Tsou204a9f12013-10-29 18:34:16 -04001047 while (1) {
1048 trx->driveReceiveFIFO(num);
1049 pthread_testcancel();
1050 }
1051 return NULL;
1052}
1053
1054void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001055{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001056 set_selfthread_name("RxLower");
1057
Thomas Tsou7553aa92013-11-08 12:50:03 -05001058 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +00001059
dburgessb3a0ca42011-10-12 07:44:40 +00001060 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001061 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001062 pthread_testcancel();
1063 }
1064 return NULL;
1065}
1066
Thomas Tsou204a9f12013-10-29 18:34:16 -04001067void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001068{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001069 set_selfthread_name("TxLower");
1070
Thomas Tsou7553aa92013-11-08 12:50:03 -05001071 transceiver->setPriority(0.44);
1072
Thomas Tsou92c16df2013-09-28 18:04:19 -04001073 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001074 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001075 pthread_testcancel();
1076 }
1077 return NULL;
1078}
1079
Thomas Tsou204a9f12013-10-29 18:34:16 -04001080void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001081{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001082 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001083 Transceiver *trx = chan->trx;
1084 size_t num = chan->num;
1085
1086 delete chan;
1087
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001088 snprintf(thread_name, 16, "CtrlService%zu", num);
1089 set_selfthread_name(thread_name);
1090
dburgessb3a0ca42011-10-12 07:44:40 +00001091 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001092 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001093 pthread_testcancel();
1094 }
1095 return NULL;
1096}
1097
Thomas Tsou204a9f12013-10-29 18:34:16 -04001098void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001099{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001100 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001101 Transceiver *trx = chan->trx;
1102 size_t num = chan->num;
1103
1104 delete chan;
1105
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001106 snprintf(thread_name, 16, "TxUpper%zu", num);
1107 set_selfthread_name(thread_name);
1108
Thomas Tsoua4cf48c2013-11-09 21:44:26 -05001109 trx->setPriority(0.40);
1110
dburgessb3a0ca42011-10-12 07:44:40 +00001111 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001112 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001113 pthread_testcancel();
1114 }
1115 return NULL;
1116}