blob: 5a6b652ef35eba446c5b8ff27d81167c28557bca [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];
dburgessb3a0ca42011-10-12 07:44:40 +0000562
Thomas Tsou30421a72013-11-13 23:14:48 -0500563 /* Blocking FIFO read */
564 radioVector *radio_burst = mReceiveFIFO[chan]->read();
565 if (!radio_burst)
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200566 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000567
Thomas Tsou30421a72013-11-13 23:14:48 -0500568 /* Set time and determine correlation type */
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200569 bi->burstTime = radio_burst->getTime();
570 CorrType type = expectedCorrType(bi->burstTime, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000571
Tom Tsou64464e62016-07-01 03:46:46 -0700572 /* Enable 8-PSK burst detection if EDGE is enabled */
573 if (mEdge && (type == TSC))
574 type = EDGE;
575
Alexander Chemerise692ce92015-06-12 00:15:31 -0400576 /* Debug: dump bursts to disk */
577 /* bits 0-7 - chan 0 timeslots
578 * bits 8-15 - chan 1 timeslots */
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200579 if (mWriteBurstToDiskMask & ((1<<bi->burstTime.TN()) << (8*chan)))
Alexander Chemerise692ce92015-06-12 00:15:31 -0400580 writeToFile(radio_burst, chan);
581
Alexander Chemeris2b542102015-06-08 22:46:38 -0400582 /* No processing if the timeslot is off.
583 * Not even power level or noise calculation. */
584 if (type == OFF) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500585 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200586 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000587 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000588
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500589 /* Select the diversity channel with highest energy */
590 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300591 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500592 if (pow > max) {
593 max = pow;
594 max_i = i;
595 }
596 avg += pow;
597 }
598
599 if (max_i < 0) {
600 LOG(ALERT) << "Received empty burst";
601 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200602 return false;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500603 }
604
Thomas Tsou30421a72013-11-13 23:14:48 -0500605 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500606 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500607 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400608
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200609 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400610
Alexander Chemeris2b542102015-06-08 22:46:38 -0400611 if (type == IDLE) {
612 /* Update noise levels */
613 state->mNoises.insert(avg);
614 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200615 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400616
617 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200618 return false;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400619 } else {
620 /* Do not update noise levels */
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200621 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400622 }
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400623
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200624 unsigned max_toa = (type == RACH || type == EXT_RACH) ?
625 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
626
Thomas Tsou30421a72013-11-13 23:14:48 -0500627 /* Detect normal or RACH bursts */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200628 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa, max_toa);
Thomas Tsouf0782732013-10-29 15:55:47 -0400629
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800630 if (rc > 0) {
631 type = (CorrType) rc;
632 } else if (rc <= 0) {
633 if (rc == -SIGERR_CLIP) {
Alexander Chemeris954b1182015-06-04 15:39:41 -0400634 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800635 } else if (rc != SIGERR_NONE) {
Alexander Chemeris954b1182015-06-04 15:39:41 -0400636 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Tom Tsou577cd022015-05-18 13:57:54 -0700637 }
638
Thomas Tsou30421a72013-11-13 23:14:48 -0500639 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200640 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000641 }
dburgessb3a0ca42011-10-12 07:44:40 +0000642
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200643 bi->toa = toa;
644 bi->rxBurst = demodAnyBurst(*burst, mSPSRx, amp, toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500645
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200646 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
647 if (bi->rxBurst->size() == EDGE_BURST_NBITS)
648 bi->nbits = EDGE_BURST_NBITS;
649 else /* size() here is actually gSlotLen + 8, due to guard periods */
650 bi->nbits = gSlotLen;
651
Thomas Tsou30421a72013-11-13 23:14:48 -0500652 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200653 return true;
dburgessb3a0ca42011-10-12 07:44:40 +0000654}
655
dburgessb3a0ca42011-10-12 07:44:40 +0000656void Transceiver::reset()
657{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400658 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
659 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000660}
661
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200662
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700663#define MAX_PACKET_LENGTH 100
664
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700665/**
666 * Matches a buffer with a command.
667 * @param buf a buffer to look command in
668 * @param cmd a command to look in buffer
669 * @param params pointer to arguments, or NULL
670 * @return true if command matches, otherwise false
671 */
672static bool match_cmd(char *buf,
673 const char *cmd, char **params)
674{
675 size_t cmd_len = strlen(cmd);
676
677 /* Check a command itself */
678 if (strncmp(buf, cmd, cmd_len))
679 return false;
680
681 /* A command has arguments */
682 if (params != NULL) {
683 /* Make sure there is a space */
684 if (buf[cmd_len] != ' ')
685 return false;
686
687 /* Update external pointer */
688 *params = buf + cmd_len + 1;
689 }
690
691 return true;
692}
693
Thomas Tsou204a9f12013-10-29 18:34:16 -0400694void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000695{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700696 char buffer[MAX_PACKET_LENGTH + 1];
697 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700698 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700699 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400700
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700701 /* Attempt to read from control socket */
702 msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH);
703 if (msgLen < 1)
dburgessb3a0ca42011-10-12 07:44:40 +0000704 return;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700705
706 /* Zero-terminate received string */
707 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000708
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700709 /* Verify a command signature */
710 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100711 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000712 return;
713 }
dburgessb3a0ca42011-10-12 07:44:40 +0000714
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700715 /* Set command pointer */
716 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200717 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700718
719 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800720 stop();
721 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700722 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700723 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000724 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700725 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000726 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300727 for (int i = 0; i < 8; i++) {
728 for (int j = 0; j < 8; j++)
729 mHandover[i][j] = false;
730 }
Tom Tsou365bc382016-10-19 15:26:04 -0700731 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700732 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700733 unsigned ts = 0, ss = 0;
734 sscanf(params, "%u %u", &ts, &ss);
735 if (ts > 7 || ss > 7) {
736 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
737 } else {
738 mHandover[ts][ss] = true;
739 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
740 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700741 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700742 unsigned ts = 0, ss = 0;
743 sscanf(params, "%u %u", &ts, &ss);
744 if (ts > 7 || ss > 7) {
745 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
746 } else {
747 mHandover[ts][ss] = false;
748 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
749 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700750 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000751 //set expected maximum time-of-arrival
752 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700753 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300754 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000755 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700756 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300757 //set expected maximum time-of-arrival
758 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700759 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300760 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
761 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700762 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000763 //set expected maximum time-of-arrival
764 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700765 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400766 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000767 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700768 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000769 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500770 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000771 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500772 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000773 }
774 else {
775 sprintf(response,"RSP NOISELEV 1 0");
776 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700777 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800778 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700779 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800780 power = mRadioInterface->setPowerAttenuation(power, chan);
781 mStates[chan].mPower = power;
782 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700783 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800784 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700785 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800786 power = mStates[chan].mPower + step;
787 power = mRadioInterface->setPowerAttenuation(power, chan);
788 mStates[chan].mPower = power;
789 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700790 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000791 // tune receiver
792 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700793 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500794 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400795 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100796 LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000797 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
798 }
799 else
800 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700801 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000802 // tune txmtr
803 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700804 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500805 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400806 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100807 LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000808 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
809 }
810 else
811 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700812 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000813 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500814 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700815 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700816 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500817 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700818 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100819 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000820 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400821 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000822 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700823 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700824 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000825 int corrCode;
826 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700827 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000828 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100829 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000830 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
831 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200832 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400833 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
834 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000835 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700836 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400837 // debug command! may change or disapear without notice
838 // set a mask which bursts to dump to disk
839 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700840 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400841 mWriteBurstToDiskMask = mask;
842 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700843 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100844 LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200845 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000846 }
847
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200848 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400849 mCtrlSockets[chan]->write(response, strlen(response) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +0000850}
851
Thomas Tsou204a9f12013-10-29 18:34:16 -0400852bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000853{
Tom Tsoue8871082016-07-01 02:46:04 -0700854 int burstLen;
855 char buffer[EDGE_BURST_NBITS + 50];
dburgessb3a0ca42011-10-12 07:44:40 +0000856
857 // check data socket
Tom Tsou2c650a62016-04-28 21:55:17 -0700858 size_t msgLen = mDataSockets[chan]->read(buffer, sizeof(buffer));
dburgessb3a0ca42011-10-12 07:44:40 +0000859
Tom Tsoue8871082016-07-01 02:46:04 -0700860 if (msgLen == gSlotLen + 1 + 4 + 1) {
861 burstLen = gSlotLen;
862 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
863 if (mSPSTx != 4)
864 return false;
865
866 burstLen = EDGE_BURST_NBITS;
867 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000868 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
869 return false;
870 }
871
872 int timeSlot = (int) buffer[0];
873 uint64_t frameNum = 0;
874 for (int i = 0; i < 4; i++)
875 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
dburgessb3a0ca42011-10-12 07:44:40 +0000876
dburgessb3a0ca42011-10-12 07:44:40 +0000877 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200878
dburgessb3a0ca42011-10-12 07:44:40 +0000879 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700880 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000881 BitVector::iterator itr = newBurst.begin();
882 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200883 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000884 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200885
dburgessb3a0ca42011-10-12 07:44:40 +0000886 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400887
888 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000889
890 return true;
891
892
893}
dburgessb3a0ca42011-10-12 07:44:40 +0000894
Thomas Tsou204a9f12013-10-29 18:34:16 -0400895void Transceiver::driveReceiveRadio()
896{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200897 int rc = mRadioInterface->driveReceiveRadio();
898 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400899 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200900 } else if (rc < 0) {
901 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
Pau Espin Pedrolb426e4a2019-06-04 12:39:28 +0200902 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200903 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
904 mForceClockInterface = false;
905 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -0400906 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400907}
908
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200909void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800910{
911 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +0300912 << " chan: " << chan
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200913 << " time: " << bi->burstTime
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200914 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
915 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
916 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
917 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200918 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
919 << " bits: " << *(bi->rxBurst);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800920}
921
Thomas Tsou204a9f12013-10-29 18:34:16 -0400922void Transceiver::driveReceiveFIFO(size_t chan)
923{
Alexander Chemerise8905a02015-06-03 23:47:56 -0400924 int TOAint; // in 1/256 symbols
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
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200934 logRxBurst(chan, &bi);
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400935
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200936 TOAint = (int) (bi.toa * 256.0 + 0.5); // round to closest integer
dburgessb3a0ca42011-10-12 07:44:40 +0000937
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200938 char burstString[sizeof(struct trxd_hdr_v0) + bi.nbits + 2];
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200939 struct trxd_hdr_v0* pkt = (struct trxd_hdr_v0*)burstString;
940 pkt->common.version = 0;
941 pkt->common.reserved = 0;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200942 pkt->common.tn = bi.burstTime.TN();
943 osmo_store32be(bi.burstTime.FN(), &pkt->common.fn);
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200944 pkt->v0.rssi = bi.rssi;
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200945 osmo_store16be(TOAint, &pkt->v0.toa);
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200946 SoftVector::iterator burstItr = bi.rxBurst->begin();
dburgessb3a0ca42011-10-12 07:44:40 +0000947
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200948 for (unsigned i = 0; i < bi.nbits; i++)
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200949 pkt->soft_bits[i] = (char) round((*burstItr++) * 255.0);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800950
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200951 /* +1: Historical reason. There's an uninitizalied byte in there: pkt->soft_bits[bi.nbits] */
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200952 pkt->soft_bits[bi.nbits + 1] = '\0';
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200953 delete bi.rxBurst;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800954
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200955 mDataSockets[chan]->write(burstString, sizeof(struct trxd_hdr_v0) + bi.nbits + 2);
dburgessb3a0ca42011-10-12 07:44:40 +0000956}
957
Thomas Tsou204a9f12013-10-29 18:34:16 -0400958void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +0000959{
960
961 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200962 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +0000963 assure that transmit packets arrive at the radio/USRP
964 before they need to be transmitted.
965
966 Deadline clock indicates the burst that needs to be
967 pushed into the FIFO right NOW. If transmit queue does
968 not have a burst, stick in filler data.
969 */
970
971
972 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200973
dburgessb3a0ca42011-10-12 07:44:40 +0000974 if (mOn) {
975 //radioClock->wait(); // wait until clock updates
976 LOG(DEBUG) << "radio clock " << radioClock->get();
977 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
978 // if underrun, then we're not providing bursts to radio/USRP fast
979 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -0400980 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000981 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +0000982 // only update latency at the defined frame interval
983 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000984 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol74bcc562018-10-02 17:30:28 +0200985 LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
986 << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +0000987 mLatencyUpdateTime = radioClock->get();
988 }
989 }
990 else {
991 // if underrun hasn't occurred in the last sec (216 frames) drop
992 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +0200993 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000994 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
995 mTransmitLatency.decTN();
996 LOG(INFO) << "reduced latency: " << mTransmitLatency;
997 mLatencyUpdateTime = radioClock->get();
998 }
999 }
1000 }
dburgessb3a0ca42011-10-12 07:44:40 +00001001 }
dburgessb3a0ca42011-10-12 07:44:40 +00001002 // time to push burst to transmit FIFO
1003 pushRadioVector(mTransmitDeadlineClock);
1004 mTransmitDeadlineClock.incTN();
1005 }
dburgessb3a0ca42011-10-12 07:44:40 +00001006 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001007
1008 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001009}
1010
1011
1012
1013void Transceiver::writeClockInterface()
1014{
1015 char command[50];
1016 // FIXME -- This should be adaptive.
1017 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1018
1019 LOG(INFO) << "ClockInterface: sending " << command;
1020
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001021 mClockSocket.write(command, strlen(command) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +00001022
1023 mLastClockUpdateTime = mTransmitDeadlineClock;
1024
Thomas Tsou92c16df2013-09-28 18:04:19 -04001025}
dburgessb3a0ca42011-10-12 07:44:40 +00001026
Thomas Tsou204a9f12013-10-29 18:34:16 -04001027void *RxUpperLoopAdapter(TransceiverChannel *chan)
1028{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001029 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001030 Transceiver *trx = chan->trx;
1031 size_t num = chan->num;
1032
1033 delete chan;
1034
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001035 snprintf(thread_name, 16, "RxUpper%zu", num);
1036 set_selfthread_name(thread_name);
1037
Thomas Tsou7553aa92013-11-08 12:50:03 -05001038 trx->setPriority(0.42);
1039
Thomas Tsou204a9f12013-10-29 18:34:16 -04001040 while (1) {
1041 trx->driveReceiveFIFO(num);
1042 pthread_testcancel();
1043 }
1044 return NULL;
1045}
1046
1047void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001048{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001049 set_selfthread_name("RxLower");
1050
Thomas Tsou7553aa92013-11-08 12:50:03 -05001051 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +00001052
dburgessb3a0ca42011-10-12 07:44:40 +00001053 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001054 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001055 pthread_testcancel();
1056 }
1057 return NULL;
1058}
1059
Thomas Tsou204a9f12013-10-29 18:34:16 -04001060void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001061{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001062 set_selfthread_name("TxLower");
1063
Thomas Tsou7553aa92013-11-08 12:50:03 -05001064 transceiver->setPriority(0.44);
1065
Thomas Tsou92c16df2013-09-28 18:04:19 -04001066 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001067 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001068 pthread_testcancel();
1069 }
1070 return NULL;
1071}
1072
Thomas Tsou204a9f12013-10-29 18:34:16 -04001073void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001074{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001075 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001076 Transceiver *trx = chan->trx;
1077 size_t num = chan->num;
1078
1079 delete chan;
1080
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001081 snprintf(thread_name, 16, "CtrlService%zu", num);
1082 set_selfthread_name(thread_name);
1083
dburgessb3a0ca42011-10-12 07:44:40 +00001084 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001085 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001086 pthread_testcancel();
1087 }
1088 return NULL;
1089}
1090
Thomas Tsou204a9f12013-10-29 18:34:16 -04001091void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001092{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001093 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001094 Transceiver *trx = chan->trx;
1095 size_t num = chan->num;
1096
1097 delete chan;
1098
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001099 snprintf(thread_name, 16, "TxUpper%zu", num);
1100 set_selfthread_name(thread_name);
1101
Thomas Tsoua4cf48c2013-11-09 21:44:26 -05001102 trx->setPriority(0.40);
1103
dburgessb3a0ca42011-10-12 07:44:40 +00001104 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001105 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001106 pthread_testcancel();
1107 }
1108 return NULL;
1109}