blob: 6e678ace69c70448c1c89ecfc090bcdd603f6b77 [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;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200561 GSM::Time burstTime;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500562 TransceiverState *state = &mStates[chan];
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 Pedrol07ddce52019-07-01 16:36:10 +0200570 burstTime = radio_burst->getTime();
571 bi->fn = burstTime.FN();
572 bi->tn = burstTime.TN();
573 CorrType type = expectedCorrType(burstTime, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000574
Tom Tsou64464e62016-07-01 03:46:46 -0700575 /* Enable 8-PSK burst detection if EDGE is enabled */
576 if (mEdge && (type == TSC))
577 type = EDGE;
578
Alexander Chemerise692ce92015-06-12 00:15:31 -0400579 /* Debug: dump bursts to disk */
580 /* bits 0-7 - chan 0 timeslots
581 * bits 8-15 - chan 1 timeslots */
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200582 if (mWriteBurstToDiskMask & ((1<<bi->tn) << (8*chan)))
Alexander Chemerise692ce92015-06-12 00:15:31 -0400583 writeToFile(radio_burst, chan);
584
Alexander Chemeris2b542102015-06-08 22:46:38 -0400585 /* No processing if the timeslot is off.
586 * Not even power level or noise calculation. */
587 if (type == OFF) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500588 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200589 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000590 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000591
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500592 /* Select the diversity channel with highest energy */
593 for (size_t i = 0; i < radio_burst->chans(); i++) {
Alexander Chemeris1dd05cf2017-03-15 23:23:36 +0300594 float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500595 if (pow > max) {
596 max = pow;
597 max_i = i;
598 }
599 avg += pow;
600 }
601
602 if (max_i < 0) {
603 LOG(ALERT) << "Received empty burst";
604 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200605 return false;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500606 }
607
Thomas Tsou30421a72013-11-13 23:14:48 -0500608 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500609 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500610 avg = sqrt(avg / radio_burst->chans());
Alexander Chemeris2b542102015-06-08 22:46:38 -0400611
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200612 bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400613
Alexander Chemeris2b542102015-06-08 22:46:38 -0400614 if (type == IDLE) {
615 /* Update noise levels */
616 state->mNoises.insert(avg);
617 state->mNoiseLev = state->mNoises.avg();
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200618 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400619
620 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200621 return false;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400622 } else {
623 /* Do not update noise levels */
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200624 bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
Alexander Chemeris2b542102015-06-08 22:46:38 -0400625 }
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400626
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200627 unsigned max_toa = (type == RACH || type == EXT_RACH) ?
628 mMaxExpectedDelayAB : mMaxExpectedDelayNB;
629
Thomas Tsou30421a72013-11-13 23:14:48 -0500630 /* Detect normal or RACH bursts */
Vadim Yanitskiya8b35652018-10-22 02:52:18 +0200631 rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, amp, toa, max_toa);
Thomas Tsouf0782732013-10-29 15:55:47 -0400632
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800633 if (rc > 0) {
634 type = (CorrType) rc;
635 } else if (rc <= 0) {
636 if (rc == -SIGERR_CLIP) {
Alexander Chemeris954b1182015-06-04 15:39:41 -0400637 LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800638 } else if (rc != SIGERR_NONE) {
Alexander Chemeris954b1182015-06-04 15:39:41 -0400639 LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
Tom Tsou577cd022015-05-18 13:57:54 -0700640 }
641
Thomas Tsou30421a72013-11-13 23:14:48 -0500642 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200643 return false;
dburgessb3a0ca42011-10-12 07:44:40 +0000644 }
dburgessb3a0ca42011-10-12 07:44:40 +0000645
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200646 bi->toa = toa;
647 bi->rxBurst = demodAnyBurst(*burst, mSPSRx, amp, toa, type);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500648
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200649 /* EDGE demodulator returns 444 (gSlotLen * 3) bits */
650 if (bi->rxBurst->size() == EDGE_BURST_NBITS)
651 bi->nbits = EDGE_BURST_NBITS;
652 else /* size() here is actually gSlotLen + 8, due to guard periods */
653 bi->nbits = gSlotLen;
654
Pau Espin Pedrol25ae1902019-07-01 16:40:44 +0200655 // Convert -1..+1 soft bits to 0..1 soft bits
656 vectorSlicer(bi->rxBurst);
657
Thomas Tsou30421a72013-11-13 23:14:48 -0500658 delete radio_burst;
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200659 return true;
dburgessb3a0ca42011-10-12 07:44:40 +0000660}
661
dburgessb3a0ca42011-10-12 07:44:40 +0000662void Transceiver::reset()
663{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400664 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
665 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000666}
667
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200668
Vadim Yanitskiybd0efb02018-03-09 02:45:07 +0700669#define MAX_PACKET_LENGTH 100
670
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700671/**
672 * Matches a buffer with a command.
673 * @param buf a buffer to look command in
674 * @param cmd a command to look in buffer
675 * @param params pointer to arguments, or NULL
676 * @return true if command matches, otherwise false
677 */
678static bool match_cmd(char *buf,
679 const char *cmd, char **params)
680{
681 size_t cmd_len = strlen(cmd);
682
683 /* Check a command itself */
684 if (strncmp(buf, cmd, cmd_len))
685 return false;
686
687 /* A command has arguments */
688 if (params != NULL) {
689 /* Make sure there is a space */
690 if (buf[cmd_len] != ' ')
691 return false;
692
693 /* Update external pointer */
694 *params = buf + cmd_len + 1;
695 }
696
697 return true;
698}
699
Thomas Tsou204a9f12013-10-29 18:34:16 -0400700void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000701{
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700702 char buffer[MAX_PACKET_LENGTH + 1];
703 char response[MAX_PACKET_LENGTH + 1];
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700704 char *command, *params;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700705 int msgLen;
Thomas Tsoud647ec52013-10-29 15:17:34 -0400706
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700707 /* Attempt to read from control socket */
708 msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH);
709 if (msgLen < 1)
dburgessb3a0ca42011-10-12 07:44:40 +0000710 return;
Vadim Yanitskiy4d9b59c2018-03-09 02:54:45 +0700711
712 /* Zero-terminate received string */
713 buffer[msgLen] = '\0';
dburgessb3a0ca42011-10-12 07:44:40 +0000714
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700715 /* Verify a command signature */
716 if (strncmp(buffer, "CMD ", 4)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100717 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000718 return;
719 }
dburgessb3a0ca42011-10-12 07:44:40 +0000720
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700721 /* Set command pointer */
722 command = buffer + 4;
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200723 LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700724
725 if (match_cmd(command, "POWEROFF", NULL)) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800726 stop();
727 sprintf(response,"RSP POWEROFF 0");
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700728 } else if (match_cmd(command, "POWERON", NULL)) {
Tom Tsou365bc382016-10-19 15:26:04 -0700729 if (!start()) {
dburgessb3a0ca42011-10-12 07:44:40 +0000730 sprintf(response,"RSP POWERON 1");
Tom Tsou365bc382016-10-19 15:26:04 -0700731 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000732 sprintf(response,"RSP POWERON 0");
Alexander Chemeris5a068062015-06-20 01:38:47 +0300733 for (int i = 0; i < 8; i++) {
734 for (int j = 0; j < 8; j++)
735 mHandover[i][j] = false;
736 }
Tom Tsou365bc382016-10-19 15:26:04 -0700737 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700738 } else if (match_cmd(command, "HANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700739 unsigned ts = 0, ss = 0;
740 sscanf(params, "%u %u", &ts, &ss);
741 if (ts > 7 || ss > 7) {
742 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
743 } else {
744 mHandover[ts][ss] = true;
745 sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss);
746 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700747 } else if (match_cmd(command, "NOHANDOVER", &params)) {
Vadim Yanitskiyc0c6d702018-03-09 05:08:23 +0700748 unsigned ts = 0, ss = 0;
749 sscanf(params, "%u %u", &ts, &ss);
750 if (ts > 7 || ss > 7) {
751 sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss);
752 } else {
753 mHandover[ts][ss] = false;
754 sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
755 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700756 } else if (match_cmd(command, "SETMAXDLY", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000757 //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 mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
dburgessb3a0ca42011-10-12 07:44:40 +0000761 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700762 } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300763 //set expected maximum time-of-arrival
764 int maxDelay;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700765 sscanf(params, "%d", &maxDelay);
Alexander Chemeris78d1fc92016-03-19 21:16:22 +0300766 mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
767 sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700768 } else if (match_cmd(command, "SETRXGAIN", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000769 //set expected maximum time-of-arrival
770 int newGain;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700771 sscanf(params, "%d", &newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400772 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000773 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700774 } else if (match_cmd(command, "NOISELEV", NULL)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000775 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500776 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000777 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500778 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000779 }
780 else {
781 sprintf(response,"RSP NOISELEV 1 0");
782 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700783 } else if (match_cmd(command, "SETPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800784 int power;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700785 sscanf(params, "%d", &power);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800786 power = mRadioInterface->setPowerAttenuation(power, chan);
787 mStates[chan].mPower = power;
788 sprintf(response, "RSP SETPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700789 } else if (match_cmd(command, "ADJPOWER", &params)) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800790 int power, step;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700791 sscanf(params, "%d", &step);
Tom Tsoua4d1a412014-11-25 15:46:56 -0800792 power = mStates[chan].mPower + step;
793 power = mRadioInterface->setPowerAttenuation(power, chan);
794 mStates[chan].mPower = power;
795 sprintf(response, "RSP ADJPOWER 0 %d", power);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700796 } else if (match_cmd(command, "RXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000797 // tune receiver
798 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700799 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500800 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400801 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100802 LOGC(DTRXCTRL, ALERT) << "RX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000803 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
804 }
805 else
806 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700807 } else if (match_cmd(command, "TXTUNE", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000808 // tune txmtr
809 int freqKhz;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700810 sscanf(params, "%d", &freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500811 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400812 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100813 LOGC(DTRXCTRL, ALERT) << "TX failed to tune";
dburgessb3a0ca42011-10-12 07:44:40 +0000814 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
815 }
816 else
817 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700818 } else if (match_cmd(command, "SETTSC", &params)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000819 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500820 unsigned TSC;
Vadim Yanitskiy8c6c5d22018-03-09 05:01:21 +0700821 sscanf(params, "%u", &TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700822 if (TSC > 7) {
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500823 sprintf(response, "RSP SETTSC 1 %d", TSC);
Tom Tsou60317342017-03-30 19:36:41 -0700824 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100825 LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000826 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400827 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000828 }
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700829 } else if (match_cmd(command, "SETSLOT", &params)) {
Alexander Chemeris1fe52822015-05-20 12:02:29 -0700830 // set slot type
dburgessb3a0ca42011-10-12 07:44:40 +0000831 int corrCode;
832 int timeslot;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700833 sscanf(params, "%d %d", &timeslot, &corrCode);
dburgessb3a0ca42011-10-12 07:44:40 +0000834 if ((timeslot < 0) || (timeslot > 7)) {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100835 LOGC(DTRXCTRL, WARNING) << "bogus message on control interface";
dburgessb3a0ca42011-10-12 07:44:40 +0000836 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
837 return;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200838 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400839 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
840 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000841 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700842 } else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
Alexander Chemerise692ce92015-06-12 00:15:31 -0400843 // debug command! may change or disapear without notice
844 // set a mask which bursts to dump to disk
845 int mask;
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700846 sscanf(params, "%d", &mask);
Alexander Chemerise692ce92015-06-12 00:15:31 -0400847 mWriteBurstToDiskMask = mask;
848 sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
Vadim Yanitskiya62fcf72018-03-09 03:24:08 +0700849 } else {
Pau Espin Pedrol441d82a2018-12-04 16:37:24 +0100850 LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface.";
Alexander Chemeris4438a9f2013-04-08 00:14:08 +0200851 sprintf(response,"RSP ERR 1");
dburgessb3a0ca42011-10-12 07:44:40 +0000852 }
853
Pau Espin Pedrolfc73c072019-05-03 19:40:00 +0200854 LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'";
Thomas Tsou204a9f12013-10-29 18:34:16 -0400855 mCtrlSockets[chan]->write(response, strlen(response) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +0000856}
857
Thomas Tsou204a9f12013-10-29 18:34:16 -0400858bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000859{
Tom Tsoue8871082016-07-01 02:46:04 -0700860 int burstLen;
861 char buffer[EDGE_BURST_NBITS + 50];
dburgessb3a0ca42011-10-12 07:44:40 +0000862
863 // check data socket
Tom Tsou2c650a62016-04-28 21:55:17 -0700864 size_t msgLen = mDataSockets[chan]->read(buffer, sizeof(buffer));
dburgessb3a0ca42011-10-12 07:44:40 +0000865
Tom Tsoue8871082016-07-01 02:46:04 -0700866 if (msgLen == gSlotLen + 1 + 4 + 1) {
867 burstLen = gSlotLen;
868 } else if (msgLen == EDGE_BURST_NBITS + 1 + 4 + 1) {
869 if (mSPSTx != 4)
870 return false;
871
872 burstLen = EDGE_BURST_NBITS;
873 } else {
dburgessb3a0ca42011-10-12 07:44:40 +0000874 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
875 return false;
876 }
877
878 int timeSlot = (int) buffer[0];
879 uint64_t frameNum = 0;
880 for (int i = 0; i < 4; i++)
881 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
dburgessb3a0ca42011-10-12 07:44:40 +0000882
dburgessb3a0ca42011-10-12 07:44:40 +0000883 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200884
dburgessb3a0ca42011-10-12 07:44:40 +0000885 int RSSI = (int) buffer[5];
Tom Tsou7c741ec2016-07-19 11:20:59 -0700886 BitVector newBurst(burstLen);
dburgessb3a0ca42011-10-12 07:44:40 +0000887 BitVector::iterator itr = newBurst.begin();
888 char *bufferItr = buffer+6;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200889 while (itr < newBurst.end())
dburgessb3a0ca42011-10-12 07:44:40 +0000890 *itr++ = *bufferItr++;
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200891
dburgessb3a0ca42011-10-12 07:44:40 +0000892 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400893
894 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000895
896 return true;
897
898
899}
dburgessb3a0ca42011-10-12 07:44:40 +0000900
Thomas Tsou204a9f12013-10-29 18:34:16 -0400901void Transceiver::driveReceiveRadio()
902{
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200903 int rc = mRadioInterface->driveReceiveRadio();
904 if (rc == 0) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400905 usleep(100000);
Pau Espin Pedroldb936b92018-09-03 16:50:49 +0200906 } else if (rc < 0) {
907 LOG(FATAL) << "radio Interface receive failed, requesting stop.";
Pau Espin Pedrolb426e4a2019-06-04 12:39:28 +0200908 osmo_signal_dispatch(SS_MAIN, S_MAIN_STOP_REQUIRED, NULL);
Pau Espin Pedrol934da482017-07-04 16:25:20 +0200909 } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
910 mForceClockInterface = false;
911 writeClockInterface();
Alexander Chemeris6a2bf0d2015-05-24 19:28:09 -0400912 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400913}
914
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200915void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800916{
917 LOG(DEBUG) << std::fixed << std::right
Alexander Chemeris58e95912016-03-25 18:20:28 +0300918 << " chan: " << chan
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200919 << " time: " << bi->tn << ":" << bi->fn
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200920 << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
921 << "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
922 << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
923 << "dBFS/" << std::setw(6) << -bi->noise << "dBm"
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200924 << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
925 << " bits: " << *(bi->rxBurst);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800926}
927
Thomas Tsou204a9f12013-10-29 18:34:16 -0400928void Transceiver::driveReceiveFIFO(size_t chan)
929{
Alexander Chemerise8905a02015-06-03 23:47:56 -0400930 int TOAint; // in 1/256 symbols
dburgessb3a0ca42011-10-12 07:44:40 +0000931
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200932 struct trx_ul_burst_ind bi;
933
934 if (!pullRadioVector(chan, &bi))
935 return;
dburgessb3a0ca42011-10-12 07:44:40 +0000936
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200937 logRxBurst(chan, &bi);
Alexander Chemerisdbe26ab2015-06-04 00:14:51 -0400938
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200939 TOAint = (int) (bi.toa * 256.0 + 0.5); // round to closest integer
dburgessb3a0ca42011-10-12 07:44:40 +0000940
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200941 char burstString[sizeof(struct trxd_hdr_v0) + bi.nbits + 2];
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200942 struct trxd_hdr_v0* pkt = (struct trxd_hdr_v0*)burstString;
943 pkt->common.version = 0;
944 pkt->common.reserved = 0;
Pau Espin Pedrol07ddce52019-07-01 16:36:10 +0200945 pkt->common.tn = bi.tn;
946 osmo_store32be(bi.fn, &pkt->common.fn);
Pau Espin Pedrol607a4142019-07-01 13:56:17 +0200947 pkt->v0.rssi = bi.rssi;
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200948 osmo_store16be(TOAint, &pkt->v0.toa);
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200949 SoftVector::iterator burstItr = bi.rxBurst->begin();
dburgessb3a0ca42011-10-12 07:44:40 +0000950
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200951 for (unsigned i = 0; i < bi.nbits; i++)
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200952 pkt->soft_bits[i] = (char) round((*burstItr++) * 255.0);
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800953
Pau Espin Pedrol778b30a2019-06-28 13:27:24 +0200954 /* +1: Historical reason. There's an uninitizalied byte in there: pkt->soft_bits[bi.nbits] */
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200955 pkt->soft_bits[bi.nbits + 1] = '\0';
Pau Espin Pedrolddd18a52019-06-28 17:01:16 +0200956 delete bi.rxBurst;
Tom Tsoub0aefcb2016-03-06 03:44:34 -0800957
Pau Espin Pedrol0e67cf22019-07-02 14:59:47 +0200958 mDataSockets[chan]->write(burstString, sizeof(struct trxd_hdr_v0) + bi.nbits + 2);
dburgessb3a0ca42011-10-12 07:44:40 +0000959}
960
Thomas Tsou204a9f12013-10-29 18:34:16 -0400961void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +0000962{
963
964 /**
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200965 Features a carefully controlled latency mechanism, to
dburgessb3a0ca42011-10-12 07:44:40 +0000966 assure that transmit packets arrive at the radio/USRP
967 before they need to be transmitted.
968
969 Deadline clock indicates the burst that needs to be
970 pushed into the FIFO right NOW. If transmit queue does
971 not have a burst, stick in filler data.
972 */
973
974
975 RadioClock *radioClock = (mRadioInterface->getClock());
Pau Espin Pedrol7c405a02017-07-04 16:24:06 +0200976
dburgessb3a0ca42011-10-12 07:44:40 +0000977 if (mOn) {
978 //radioClock->wait(); // wait until clock updates
979 LOG(DEBUG) << "radio clock " << radioClock->get();
980 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
981 // if underrun, then we're not providing bursts to radio/USRP fast
982 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -0400983 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000984 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +0000985 // only update latency at the defined frame interval
986 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000987 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
Pau Espin Pedrol74bcc562018-10-02 17:30:28 +0200988 LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun "
989 << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")";
kurtis.heimerle380af32011-11-26 03:18:55 +0000990 mLatencyUpdateTime = radioClock->get();
991 }
992 }
993 else {
994 // if underrun hasn't occurred in the last sec (216 frames) drop
995 // transmit latency by a timeslot
Pau Espin Pedrole564f0f2018-04-24 18:43:51 +0200996 if (mTransmitLatency > mRadioInterface->minLatency()) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000997 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
998 mTransmitLatency.decTN();
999 LOG(INFO) << "reduced latency: " << mTransmitLatency;
1000 mLatencyUpdateTime = radioClock->get();
1001 }
1002 }
1003 }
dburgessb3a0ca42011-10-12 07:44:40 +00001004 }
dburgessb3a0ca42011-10-12 07:44:40 +00001005 // time to push burst to transmit FIFO
1006 pushRadioVector(mTransmitDeadlineClock);
1007 mTransmitDeadlineClock.incTN();
1008 }
dburgessb3a0ca42011-10-12 07:44:40 +00001009 }
Thomas Tsou92c16df2013-09-28 18:04:19 -04001010
1011 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +00001012}
1013
1014
1015
1016void Transceiver::writeClockInterface()
1017{
1018 char command[50];
1019 // FIXME -- This should be adaptive.
1020 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
1021
1022 LOG(INFO) << "ClockInterface: sending " << command;
1023
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001024 mClockSocket.write(command, strlen(command) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +00001025
1026 mLastClockUpdateTime = mTransmitDeadlineClock;
1027
Thomas Tsou92c16df2013-09-28 18:04:19 -04001028}
dburgessb3a0ca42011-10-12 07:44:40 +00001029
Thomas Tsou204a9f12013-10-29 18:34:16 -04001030void *RxUpperLoopAdapter(TransceiverChannel *chan)
1031{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001032 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001033 Transceiver *trx = chan->trx;
1034 size_t num = chan->num;
1035
1036 delete chan;
1037
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001038 snprintf(thread_name, 16, "RxUpper%zu", num);
1039 set_selfthread_name(thread_name);
1040
Thomas Tsou7553aa92013-11-08 12:50:03 -05001041 trx->setPriority(0.42);
1042
Thomas Tsou204a9f12013-10-29 18:34:16 -04001043 while (1) {
1044 trx->driveReceiveFIFO(num);
1045 pthread_testcancel();
1046 }
1047 return NULL;
1048}
1049
1050void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +00001051{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001052 set_selfthread_name("RxLower");
1053
Thomas Tsou7553aa92013-11-08 12:50:03 -05001054 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +00001055
dburgessb3a0ca42011-10-12 07:44:40 +00001056 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001057 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -04001058 pthread_testcancel();
1059 }
1060 return NULL;
1061}
1062
Thomas Tsou204a9f12013-10-29 18:34:16 -04001063void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -04001064{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001065 set_selfthread_name("TxLower");
1066
Thomas Tsou7553aa92013-11-08 12:50:03 -05001067 transceiver->setPriority(0.44);
1068
Thomas Tsou92c16df2013-09-28 18:04:19 -04001069 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001070 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +00001071 pthread_testcancel();
1072 }
1073 return NULL;
1074}
1075
Thomas Tsou204a9f12013-10-29 18:34:16 -04001076void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001077{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001078 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001079 Transceiver *trx = chan->trx;
1080 size_t num = chan->num;
1081
1082 delete chan;
1083
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001084 snprintf(thread_name, 16, "CtrlService%zu", num);
1085 set_selfthread_name(thread_name);
1086
dburgessb3a0ca42011-10-12 07:44:40 +00001087 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -04001088 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001089 pthread_testcancel();
1090 }
1091 return NULL;
1092}
1093
Thomas Tsou204a9f12013-10-29 18:34:16 -04001094void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +00001095{
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001096 char thread_name[16];
Thomas Tsou204a9f12013-10-29 18:34:16 -04001097 Transceiver *trx = chan->trx;
1098 size_t num = chan->num;
1099
1100 delete chan;
1101
Pau Espin Pedrol5b60c982018-09-20 18:04:46 +02001102 snprintf(thread_name, 16, "TxUpper%zu", num);
1103 set_selfthread_name(thread_name);
1104
Thomas Tsoua4cf48c2013-11-09 21:44:26 -05001105 trx->setPriority(0.40);
1106
dburgessb3a0ca42011-10-12 07:44:40 +00001107 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001108 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001109 pthread_testcancel();
1110 }
1111 return NULL;
1112}