blob: 724fe8d4f33d2096935d82b765ff38fe33954455 [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>
25#include "Transceiver.h"
26#include <Logger.h>
27
ttsou2173abf2012-08-08 00:51:31 +000028#ifdef HAVE_CONFIG_H
29#include "config.h"
30#endif
dburgessb3a0ca42011-10-12 07:44:40 +000031
Alexander Chemerisd734e2d2013-06-16 14:30:58 +040032using namespace GSM;
33
kurtis.heimerlec842de2012-11-23 08:37:32 +000034#define USB_LATENCY_INTRVL 10,0
ttsou2173abf2012-08-08 00:51:31 +000035
36#if USE_UHD
kurtis.heimerlec842de2012-11-23 08:37:32 +000037# define USB_LATENCY_MIN 6,7
ttsou2173abf2012-08-08 00:51:31 +000038#else
kurtis.heimerlec842de2012-11-23 08:37:32 +000039# define USB_LATENCY_MIN 1,1
ttsou2173abf2012-08-08 00:51:31 +000040#endif
dburgessb3a0ca42011-10-12 07:44:40 +000041
Thomas Tsoufa3a7872013-10-17 21:23:34 -040042/* Number of running values use in noise average */
43#define NOISE_CNT 20
ttsoue8dde022012-12-06 15:43:55 +000044
Thomas Tsouf0782732013-10-29 15:55:47 -040045TransceiverState::TransceiverState()
Thomas Tsou15d743e2014-01-25 02:34:03 -050046 : mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT)
Thomas Tsouf0782732013-10-29 15:55:47 -040047{
48 for (int i = 0; i < 8; i++) {
49 chanType[i] = Transceiver::NONE;
50 fillerModulus[i] = 26;
51 chanResponse[i] = NULL;
52 DFEForward[i] = NULL;
53 DFEFeedback[i] = NULL;
54
55 for (int n = 0; n < 102; n++)
56 fillerTable[n][i] = NULL;
57 }
58}
59
60TransceiverState::~TransceiverState()
61{
62 for (int i = 0; i < 8; i++) {
63 delete chanResponse[i];
64 delete DFEForward[i];
65 delete DFEFeedback[i];
66
67 for (int n = 0; n < 102; n++)
68 delete fillerTable[n][i];
69 }
70}
71
Thomas Tsou15d743e2014-01-25 02:34:03 -050072void TransceiverState::init(size_t slot, signalVector *burst, bool fill)
Thomas Tsouf0782732013-10-29 15:55:47 -040073{
Thomas Tsou15d743e2014-01-25 02:34:03 -050074 signalVector *filler;
75
76 for (int i = 0; i < 102; i++) {
77 if (fill)
78 filler = new signalVector(*burst);
79 else
80 filler = new signalVector(burst->size());
81
82 fillerTable[i][slot] = filler;
83 }
Thomas Tsouf0782732013-10-29 15:55:47 -040084}
85
dburgessb3a0ca42011-10-12 07:44:40 +000086Transceiver::Transceiver(int wBasePort,
87 const char *TRXAddress,
Thomas Tsou204a9f12013-10-29 18:34:16 -040088 size_t wSPS, size_t wChans,
dburgessb3a0ca42011-10-12 07:44:40 +000089 GSM::Time wTransmitLatency,
90 RadioInterface *wRadioInterface)
Thomas Tsoud647ec52013-10-29 15:17:34 -040091 : mBasePort(wBasePort), mAddr(TRXAddress),
Thomas Tsoua0179e32013-11-14 15:52:04 -050092 mTransmitLatency(wTransmitLatency), mClockSocket(NULL),
93 mRadioInterface(wRadioInterface), mSPSTx(wSPS), mSPSRx(1), mChans(wChans),
Thomas Tsou204a9f12013-10-29 18:34:16 -040094 mOn(false), mTxFreq(0.0), mRxFreq(0.0), mPower(-10), mMaxExpectedDelay(0)
dburgessb3a0ca42011-10-12 07:44:40 +000095{
dburgessb3a0ca42011-10-12 07:44:40 +000096 GSM::Time startTime(random() % gHyperframe,0);
97
Thomas Tsou204a9f12013-10-29 18:34:16 -040098 mRxLowerLoopThread = new Thread(32768);
99 mTxLowerLoopThread = new Thread(32768);
dburgessb3a0ca42011-10-12 07:44:40 +0000100
dburgessb3a0ca42011-10-12 07:44:40 +0000101 mTransmitDeadlineClock = startTime;
102 mLastClockUpdateTime = startTime;
103 mLatencyUpdateTime = startTime;
104 mRadioInterface->getClock()->set(startTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000105
dburgessb3a0ca42011-10-12 07:44:40 +0000106 txFullScale = mRadioInterface->fullScaleInputValue();
107 rxFullScale = mRadioInterface->fullScaleOutputValue();
dburgessb3a0ca42011-10-12 07:44:40 +0000108}
109
110Transceiver::~Transceiver()
111{
dburgessb3a0ca42011-10-12 07:44:40 +0000112 sigProcLibDestroy();
Thomas Tsoud647ec52013-10-29 15:17:34 -0400113
114 delete mClockSocket;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400115
116 for (size_t i = 0; i < mChans; i++) {
117 mTxPriorityQueues[i].clear();
118 delete mCtrlSockets[i];
119 delete mDataSockets[i];
120 }
dburgessb3a0ca42011-10-12 07:44:40 +0000121}
Thomas Tsou83e06892013-08-20 16:10:01 -0400122
Thomas Tsou15d743e2014-01-25 02:34:03 -0500123bool Transceiver::init(bool filler)
Thomas Tsou83e06892013-08-20 16:10:01 -0400124{
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500125 int d_srcport, d_dstport, c_srcport, c_dstport;
Thomas Tsouf0782732013-10-29 15:55:47 -0400126 signalVector *burst;
127
Thomas Tsou204a9f12013-10-29 18:34:16 -0400128 if (!mChans) {
129 LOG(ALERT) << "No channels assigned";
130 return false;
131 }
132
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400133 if (!sigProcLibSetup(mSPSTx)) {
Thomas Tsou83e06892013-08-20 16:10:01 -0400134 LOG(ALERT) << "Failed to initialize signal processing library";
135 return false;
136 }
137
Thomas Tsou204a9f12013-10-29 18:34:16 -0400138 mDataSockets.resize(mChans);
139 mCtrlSockets.resize(mChans);
140
141 mControlServiceLoopThreads.resize(mChans);
142 mTxPriorityQueueServiceLoopThreads.resize(mChans);
143 mRxServiceLoopThreads.resize(mChans);
144
145 mTxPriorityQueues.resize(mChans);
146 mReceiveFIFO.resize(mChans);
147 mStates.resize(mChans);
148
Thomas Tsoud647ec52013-10-29 15:17:34 -0400149 mClockSocket = new UDPSocket(mBasePort, mAddr.c_str(), mBasePort + 100);
Thomas Tsoud647ec52013-10-29 15:17:34 -0400150
Thomas Tsou204a9f12013-10-29 18:34:16 -0400151 for (size_t i = 0; i < mChans; i++) {
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500152 c_srcport = mBasePort + 2 * i + 1;
153 c_dstport = mBasePort + 2 * i + 101;
154 d_srcport = mBasePort + 2 * i + 2;
155 d_dstport = mBasePort + 2 * i + 102;
156
157 mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport);
158 mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400159 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400160
Thomas Tsou204a9f12013-10-29 18:34:16 -0400161 for (size_t i = 0; i < mChans; i++) {
162 mControlServiceLoopThreads[i] = new Thread(32768);
163 mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
164 mRxServiceLoopThreads[i] = new Thread(32768);
165
166 for (size_t n = 0; n < 8; n++) {
167 burst = modulateBurst(gDummyBurst, 8 + (n % 4 == 0), mSPSTx);
168 scaleVector(*burst, txFullScale);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500169 mStates[i].init(n, burst, filler && !i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400170 delete burst;
171 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400172 }
173
174 return true;
175}
dburgessb3a0ca42011-10-12 07:44:40 +0000176
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500177void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400178 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000179{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500180 signalVector *burst;
181 radioVector *radio_burst;
182
Thomas Tsou204a9f12013-10-29 18:34:16 -0400183 if (chan >= mTxPriorityQueues.size()) {
184 LOG(ALERT) << "Invalid channel " << chan;
185 return;
186 }
187
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500188 if (wTime.TN() > 7) {
189 LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
190 return;
191 }
192
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500193 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
194 scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
dburgessb3a0ca42011-10-12 07:44:40 +0000195
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500196 radio_burst = new radioVector(wTime, burst);
197
198 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000199}
200
Thomas Tsou15d743e2014-01-25 02:34:03 -0500201void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
202{
203 int TN, modFN;
204 TransceiverState *state = &mStates[chan];
205
206 TN = burst->getTime().TN();
207 modFN = burst->getTime().FN() % state->fillerModulus[TN];
208
209 delete state->fillerTable[modFN][TN];
210 state->fillerTable[modFN][TN] = burst->getVector();
211 burst->setVector(NULL);
212}
213
dburgessb3a0ca42011-10-12 07:44:40 +0000214void Transceiver::pushRadioVector(GSM::Time &nowTime)
215{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400216 int TN, modFN;
217 radioVector *burst;
218 TransceiverState *state;
219 std::vector<signalVector *> bursts(mChans);
220 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500221 std::vector<bool> filler(mChans, true);
dburgessb3a0ca42011-10-12 07:44:40 +0000222
Thomas Tsou204a9f12013-10-29 18:34:16 -0400223 for (size_t i = 0; i < mChans; i ++) {
224 state = &mStates[i];
dburgessb3a0ca42011-10-12 07:44:40 +0000225
Thomas Tsou204a9f12013-10-29 18:34:16 -0400226 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
227 LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
Thomas Tsou15d743e2014-01-25 02:34:03 -0500228 if (state->mRetrans)
229 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500230 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400231 }
232
233 TN = nowTime.TN();
234 modFN = nowTime.FN() % state->fillerModulus[TN];
235
236 bursts[i] = state->fillerTable[modFN][TN];
237 zeros[i] = state->chanType[TN] == NONE;
238
239 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500240 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500241
242 if (state->mRetrans) {
243 updateFillerTable(i, burst);
244 } else {
245 burst->setVector(NULL);
246 filler[i] = false;
247 }
248
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500249 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400250 }
dburgessb3a0ca42011-10-12 07:44:40 +0000251 }
252
Thomas Tsou204a9f12013-10-29 18:34:16 -0400253 mRadioInterface->driveTransmitRadio(bursts, zeros);
254
Thomas Tsou15d743e2014-01-25 02:34:03 -0500255 for (size_t i = 0; i < mChans; i++) {
256 if (!filler[i])
257 delete bursts[i];
258 }
dburgessb3a0ca42011-10-12 07:44:40 +0000259}
260
Thomas Tsou204a9f12013-10-29 18:34:16 -0400261void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000262{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400263 TransceiverState *state = &mStates[chan];
264
265 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000266 case NONE:
267 case I:
268 case II:
269 case III:
270 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400271 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000272 break;
273 case IV:
274 case VI:
275 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400276 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000277 break;
278 //case V:
279 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400280 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000281 break;
ttsoufc40a842013-06-09 22:38:18 +0000282 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400283 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000284 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000285 default:
286 break;
287 }
288}
289
290
Thomas Tsou204a9f12013-10-29 18:34:16 -0400291Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
292 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000293{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400294 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000295 unsigned burstTN = currTime.TN();
296 unsigned burstFN = currTime.FN();
297
Thomas Tsou204a9f12013-10-29 18:34:16 -0400298 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000299 case NONE:
300 return OFF;
301 break;
302 case FILL:
303 return IDLE;
304 break;
305 case I:
306 return TSC;
307 /*if (burstFN % 26 == 25)
308 return IDLE;
309 else
310 return TSC;*/
311 break;
312 case II:
ttsou20642972013-03-27 22:00:25 +0000313 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000314 break;
315 case III:
316 return TSC;
317 break;
318 case IV:
319 case VI:
320 return RACH;
321 break;
322 case V: {
323 int mod51 = burstFN % 51;
324 if ((mod51 <= 36) && (mod51 >= 14))
325 return RACH;
326 else if ((mod51 == 4) || (mod51 == 5))
327 return RACH;
328 else if ((mod51 == 45) || (mod51 == 46))
329 return RACH;
330 else
331 return TSC;
332 break;
333 }
334 case VII:
335 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
336 return IDLE;
337 else
338 return TSC;
339 break;
ttsoufc40a842013-06-09 22:38:18 +0000340 case XIII: {
341 int mod52 = burstFN % 52;
342 if ((mod52 == 12) || (mod52 == 38))
343 return RACH;
344 else if ((mod52 == 25) || (mod52 == 51))
345 return IDLE;
346 else
347 return TSC;
348 break;
349 }
dburgessb3a0ca42011-10-12 07:44:40 +0000350 case LOOPBACK:
351 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
352 return IDLE;
353 else
354 return TSC;
355 break;
356 default:
357 return OFF;
358 break;
359 }
dburgessb3a0ca42011-10-12 07:44:40 +0000360}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400361
Thomas Tsou30421a72013-11-13 23:14:48 -0500362/*
363 * Detect RACH synchronization sequence within a burst. No equalization
364 * is used or available on the RACH channel.
365 */
366bool Transceiver::detectRACH(TransceiverState *state,
367 signalVector &burst,
368 complex &amp, float &toa)
369{
370 float threshold = 6.0;
371
372 return detectRACHBurst(burst, threshold, mSPSRx, &amp, &toa);
373}
374
375/*
376 * Detect normal burst training sequence midamble. Update equalization
377 * state information and channel estimate if necessary. Equalization
378 * is currently disabled.
379 */
Thomas Tsoua0179e32013-11-14 15:52:04 -0500380bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
Thomas Tsou30421a72013-11-13 23:14:48 -0500381 complex &amp, float &toa, GSM::Time &time)
382{
383 int tn = time.TN();
384 float chanOffset, threshold = 5.0;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500385 bool noise, needDFE = false, estimateChan = false;
Thomas Tsou30421a72013-11-13 23:14:48 -0500386 double elapsed = time - state->chanEstimateTime[tn];
387 signalVector *chanResp;
388
389 /* Check equalization update state */
Thomas Tsoufb827d02013-11-16 16:14:12 -0500390 if (needDFE && ((elapsed > 50) || (!state->chanResponse[tn]))) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500391 delete state->DFEForward[tn];
392 delete state->DFEFeedback[tn];
393 state->DFEForward[tn] = NULL;
394 state->DFEFeedback[tn] = NULL;
395
396 estimateChan = true;
397 }
398
399 /* Detect normal burst midambles */
400 if (!analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, &amp,
401 &toa, mMaxExpectedDelay, estimateChan,
402 &chanResp, &chanOffset)) {
403 return false;
404 }
405
Thomas Tsoua0179e32013-11-14 15:52:04 -0500406 noise = state->mNoiseLev;
407 state->SNRestimate[tn] = amp.norm2() / (noise * noise + 1.0);
Thomas Tsou30421a72013-11-13 23:14:48 -0500408
409 /* Set equalizer if unabled */
410 if (needDFE && estimateChan) {
411 state->chanResponse[tn] = chanResp;
412 state->chanRespOffset[tn] = chanOffset;
413 state->chanRespAmplitude[tn] = amp;
414
415 scaleVector(*chanResp, complex(1.0, 0.0) / amp);
416
417 designDFE(*chanResp, state->SNRestimate[tn],
418 7, &state->DFEForward[tn], &state->DFEFeedback[tn]);
419
420 state->chanEstimateTime[tn] = time;
421 }
422
423 return true;;
424}
425
426/*
427 * Demodulate GMSK burst using equalization if requested. Otherwise
428 * demodulate by direct rotation and soft slicing.
429 */
430SoftVector *Transceiver::demodulate(TransceiverState *state,
431 signalVector &burst, complex amp,
432 float toa, size_t tn, bool equalize)
433{
434 if (equalize) {
435 scaleVector(burst, complex(1.0, 0.0) / amp);
436 return equalizeBurst(burst,
437 toa - state->chanRespOffset[tn],
438 mSPSRx,
439 *state->DFEForward[tn],
440 *state->DFEFeedback[tn]);
441 }
442
443 return demodulateBurst(burst, mSPSRx, amp, toa);
444}
445
446/*
447 * Pull bursts from the FIFO and handle according to the slot
448 * and burst correlation type. Equalzation is currently disabled.
449 */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400450SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
451 int &timingOffset, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000452{
Thomas Tsou30421a72013-11-13 23:14:48 -0500453 bool success, equalize = false;
454 complex amp;
455 float toa, pow, max = -1.0, avg = 0.0;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500456 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500457 signalVector *burst;
Thomas Tsouef25dba2013-11-14 15:31:24 -0500458 SoftVector *bits = NULL;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500459 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000460
Thomas Tsou30421a72013-11-13 23:14:48 -0500461 /* Blocking FIFO read */
462 radioVector *radio_burst = mReceiveFIFO[chan]->read();
463 if (!radio_burst)
464 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000465
Thomas Tsou30421a72013-11-13 23:14:48 -0500466 /* Set time and determine correlation type */
467 GSM::Time time = radio_burst->getTime();
468 CorrType type = expectedCorrType(time, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000469
Thomas Tsou30421a72013-11-13 23:14:48 -0500470 if ((type == OFF) || (type == IDLE)) {
471 delete radio_burst;
dburgessb3a0ca42011-10-12 07:44:40 +0000472 return NULL;
473 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000474
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500475 /* Select the diversity channel with highest energy */
476 for (size_t i = 0; i < radio_burst->chans(); i++) {
477 energyDetect(*radio_burst->getVector(i), 20 * mSPSRx, 0.0, &pow);
478 if (pow > max) {
479 max = pow;
480 max_i = i;
481 }
482 avg += pow;
483 }
484
485 if (max_i < 0) {
486 LOG(ALERT) << "Received empty burst";
487 delete radio_burst;
488 return NULL;
489 }
490
Thomas Tsou30421a72013-11-13 23:14:48 -0500491 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500492 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500493 avg = sqrt(avg / radio_burst->chans());
Thomas Tsoua0179e32013-11-14 15:52:04 -0500494 state->mNoiseLev = state->mNoises.avg();
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400495
Thomas Tsou30421a72013-11-13 23:14:48 -0500496 /* Detect normal or RACH bursts */
497 if (type == TSC)
Thomas Tsoua0179e32013-11-14 15:52:04 -0500498 success = detectTSC(state, *burst, amp, toa, time);
Thomas Tsou30421a72013-11-13 23:14:48 -0500499 else
Thomas Tsoua0179e32013-11-14 15:52:04 -0500500 success = detectRACH(state, *burst, amp, toa);
Thomas Tsouf0782732013-10-29 15:55:47 -0400501
Thomas Tsou30421a72013-11-13 23:14:48 -0500502 if (!success) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500503 state->mNoises.insert(avg);
Thomas Tsou30421a72013-11-13 23:14:48 -0500504 delete radio_burst;
505 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000506 }
dburgessb3a0ca42011-10-12 07:44:40 +0000507
Thomas Tsou30421a72013-11-13 23:14:48 -0500508 /* Demodulate and set output info */
509 if (equalize && (type != TSC))
510 equalize = false;
dburgessb3a0ca42011-10-12 07:44:40 +0000511
Thomas Tsoua0179e32013-11-14 15:52:04 -0500512 if (avg - state->mNoiseLev > 0.0)
513 bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500514
Thomas Tsou30421a72013-11-13 23:14:48 -0500515 wTime = time;
516 RSSI = (int) floor(20.0 * log10(rxFullScale / avg));
517 timingOffset = (int) round(toa * 256.0 / mSPSRx);
dburgessb3a0ca42011-10-12 07:44:40 +0000518
Thomas Tsou30421a72013-11-13 23:14:48 -0500519 delete radio_burst;
520
521 return bits;
dburgessb3a0ca42011-10-12 07:44:40 +0000522}
523
524void Transceiver::start()
525{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400526 TransceiverChannel *chan;
527
528 for (size_t i = 0; i < mControlServiceLoopThreads.size(); i++) {
529 chan = new TransceiverChannel(this, i);
530 mControlServiceLoopThreads[i]->start((void * (*)(void*))
531 ControlServiceLoopAdapter, (void*) chan);
532 }
dburgessb3a0ca42011-10-12 07:44:40 +0000533}
534
535void Transceiver::reset()
536{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400537 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
538 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000539}
540
541
Thomas Tsou204a9f12013-10-29 18:34:16 -0400542void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000543{
dburgessb3a0ca42011-10-12 07:44:40 +0000544 int MAX_PACKET_LENGTH = 100;
545
546 // check control socket
547 char buffer[MAX_PACKET_LENGTH];
548 int msgLen = -1;
549 buffer[0] = '\0';
Thomas Tsoud647ec52013-10-29 15:17:34 -0400550
Thomas Tsou204a9f12013-10-29 18:34:16 -0400551 msgLen = mCtrlSockets[chan]->read(buffer);
dburgessb3a0ca42011-10-12 07:44:40 +0000552
553 if (msgLen < 1) {
554 return;
555 }
556
557 char cmdcheck[4];
558 char command[MAX_PACKET_LENGTH];
559 char response[MAX_PACKET_LENGTH];
560
561 sscanf(buffer,"%3s %s",cmdcheck,command);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400562
563 if (!chan)
564 writeClockInterface();
dburgessb3a0ca42011-10-12 07:44:40 +0000565
566 if (strcmp(cmdcheck,"CMD")!=0) {
567 LOG(WARNING) << "bogus message on control interface";
568 return;
569 }
570 LOG(INFO) << "command is " << buffer;
571
572 if (strcmp(command,"POWEROFF")==0) {
573 // turn off transmitter/demod
574 sprintf(response,"RSP POWEROFF 0");
575 }
576 else if (strcmp(command,"POWERON")==0) {
577 // turn on transmitter/demod
578 if (!mTxFreq || !mRxFreq)
579 sprintf(response,"RSP POWERON 1");
580 else {
581 sprintf(response,"RSP POWERON 0");
Thomas Tsou204a9f12013-10-29 18:34:16 -0400582 if (!chan && !mOn) {
dburgessb3a0ca42011-10-12 07:44:40 +0000583 // Prepare for thread start
584 mPower = -20;
585 mRadioInterface->start();
dburgessb3a0ca42011-10-12 07:44:40 +0000586
587 // Start radio interface threads.
Thomas Tsou204a9f12013-10-29 18:34:16 -0400588 mTxLowerLoopThread->start((void * (*)(void*))
589 TxLowerLoopAdapter,(void*) this);
590 mRxLowerLoopThread->start((void * (*)(void*))
591 RxLowerLoopAdapter,(void*) this);
dburgessb3a0ca42011-10-12 07:44:40 +0000592
Thomas Tsou204a9f12013-10-29 18:34:16 -0400593 for (size_t i = 0; i < mChans; i++) {
594 TransceiverChannel *chan = new TransceiverChannel(this, i);
595 mRxServiceLoopThreads[i]->start((void * (*)(void*))
596 RxUpperLoopAdapter, (void*) chan);
597
598 chan = new TransceiverChannel(this, i);
599 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
600 TxUpperLoopAdapter, (void*) chan);
601 }
602
603 writeClockInterface();
dburgessb3a0ca42011-10-12 07:44:40 +0000604 mOn = true;
605 }
606 }
607 }
608 else if (strcmp(command,"SETMAXDLY")==0) {
609 //set expected maximum time-of-arrival
610 int maxDelay;
611 sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
612 mMaxExpectedDelay = maxDelay; // 1 GSM symbol is approx. 1 km
613 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
614 }
615 else if (strcmp(command,"SETRXGAIN")==0) {
616 //set expected maximum time-of-arrival
617 int newGain;
618 sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400619 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000620 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
621 }
622 else if (strcmp(command,"NOISELEV")==0) {
623 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500624 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000625 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500626 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000627 }
628 else {
629 sprintf(response,"RSP NOISELEV 1 0");
630 }
Thomas Tsoucb269a32013-11-15 14:15:47 -0500631 }
632 else if (!strcmp(command, "SETPOWER")) {
dburgessb3a0ca42011-10-12 07:44:40 +0000633 // set output power in dB
634 int dbPwr;
Thomas Tsoucb269a32013-11-15 14:15:47 -0500635 sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbPwr);
636 if (!mOn)
637 sprintf(response, "RSP SETPOWER 1 %d", dbPwr);
dburgessb3a0ca42011-10-12 07:44:40 +0000638 else {
639 mPower = dbPwr;
Thomas Tsoucb269a32013-11-15 14:15:47 -0500640 mRadioInterface->setPowerAttenuation(mPower, chan);
641 sprintf(response, "RSP SETPOWER 0 %d", dbPwr);
dburgessb3a0ca42011-10-12 07:44:40 +0000642 }
643 }
Thomas Tsoucb269a32013-11-15 14:15:47 -0500644 else if (!strcmp(command,"ADJPOWER")) {
dburgessb3a0ca42011-10-12 07:44:40 +0000645 // adjust power in dB steps
646 int dbStep;
Thomas Tsoucb269a32013-11-15 14:15:47 -0500647 sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbStep);
648 if (!mOn)
649 sprintf(response, "RSP ADJPOWER 1 %d", mPower);
dburgessb3a0ca42011-10-12 07:44:40 +0000650 else {
651 mPower += dbStep;
Thomas Tsoucb269a32013-11-15 14:15:47 -0500652 mRadioInterface->setPowerAttenuation(mPower, chan);
653 sprintf(response, "RSP ADJPOWER 0 %d", mPower);
dburgessb3a0ca42011-10-12 07:44:40 +0000654 }
655 }
dburgessb3a0ca42011-10-12 07:44:40 +0000656 else if (strcmp(command,"RXTUNE")==0) {
657 // tune receiver
658 int freqKhz;
659 sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500660 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400661 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000662 LOG(ALERT) << "RX failed to tune";
663 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
664 }
665 else
666 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
667 }
668 else if (strcmp(command,"TXTUNE")==0) {
669 // tune txmtr
670 int freqKhz;
671 sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500672 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400673 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000674 LOG(ALERT) << "TX failed to tune";
675 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
676 }
677 else
678 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
679 }
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500680 else if (!strcmp(command,"SETTSC")) {
dburgessb3a0ca42011-10-12 07:44:40 +0000681 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500682 unsigned TSC;
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500683 sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000684 if (mOn)
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500685 sprintf(response, "RSP SETTSC 1 %d", TSC);
686 else if (chan && (TSC != mTSC))
687 sprintf(response, "RSP SETTSC 1 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000688 else {
689 mTSC = TSC;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400690 generateMidamble(mSPSRx, TSC);
Thomas Tsou83e06892013-08-20 16:10:01 -0400691 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000692 }
693 }
694 else if (strcmp(command,"SETSLOT")==0) {
695 // set TSC
696 int corrCode;
697 int timeslot;
698 sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&timeslot,&corrCode);
699 if ((timeslot < 0) || (timeslot > 7)) {
700 LOG(WARNING) << "bogus message on control interface";
701 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
702 return;
703 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400704 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
705 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000706 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
707
708 }
709 else {
710 LOG(WARNING) << "bogus command " << command << " on control interface.";
711 }
712
Thomas Tsou204a9f12013-10-29 18:34:16 -0400713 mCtrlSockets[chan]->write(response, strlen(response) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +0000714}
715
Thomas Tsou204a9f12013-10-29 18:34:16 -0400716bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000717{
dburgessb3a0ca42011-10-12 07:44:40 +0000718 char buffer[gSlotLen+50];
719
720 // check data socket
Thomas Tsou204a9f12013-10-29 18:34:16 -0400721 size_t msgLen = mDataSockets[chan]->read(buffer);
dburgessb3a0ca42011-10-12 07:44:40 +0000722
723 if (msgLen!=gSlotLen+1+4+1) {
724 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
725 return false;
726 }
727
728 int timeSlot = (int) buffer[0];
729 uint64_t frameNum = 0;
730 for (int i = 0; i < 4; i++)
731 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
dburgessb3a0ca42011-10-12 07:44:40 +0000732
dburgessb3a0ca42011-10-12 07:44:40 +0000733 // periodically update GSM core clock
734 LOG(DEBUG) << "mTransmitDeadlineClock " << mTransmitDeadlineClock
735 << " mLastClockUpdateTime " << mLastClockUpdateTime;
dburgessb3a0ca42011-10-12 07:44:40 +0000736
Thomas Tsou204a9f12013-10-29 18:34:16 -0400737 if (!chan) {
738 if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
739 writeClockInterface();
740 }
dburgessb3a0ca42011-10-12 07:44:40 +0000741
742 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
743
744 int RSSI = (int) buffer[5];
745 static BitVector newBurst(gSlotLen);
746 BitVector::iterator itr = newBurst.begin();
747 char *bufferItr = buffer+6;
748 while (itr < newBurst.end())
749 *itr++ = *bufferItr++;
750
751 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400752
753 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000754
755 return true;
756
757
758}
dburgessb3a0ca42011-10-12 07:44:40 +0000759
Thomas Tsou204a9f12013-10-29 18:34:16 -0400760void Transceiver::driveReceiveRadio()
761{
762 if (!mRadioInterface->driveReceiveRadio())
763 usleep(100000);
764}
765
766void Transceiver::driveReceiveFIFO(size_t chan)
767{
dburgessb3a0ca42011-10-12 07:44:40 +0000768 SoftVector *rxBurst = NULL;
769 int RSSI;
770 int TOA; // in 1/256 of a symbol
771 GSM::Time burstTime;
772
Thomas Tsou204a9f12013-10-29 18:34:16 -0400773 rxBurst = pullRadioVector(burstTime, RSSI, TOA, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000774
775 if (rxBurst) {
776
777 LOG(DEBUG) << "burst parameters: "
778 << " time: " << burstTime
779 << " RSSI: " << RSSI
780 << " TOA: " << TOA
781 << " bits: " << *rxBurst;
782
783 char burstString[gSlotLen+10];
784 burstString[0] = burstTime.TN();
785 for (int i = 0; i < 4; i++)
786 burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
787 burstString[5] = RSSI;
788 burstString[6] = (TOA >> 8) & 0x0ff;
789 burstString[7] = TOA & 0x0ff;
790 SoftVector::iterator burstItr = rxBurst->begin();
791
792 for (unsigned int i = 0; i < gSlotLen; i++) {
793 burstString[8+i] =(char) round((*burstItr++)*255.0);
794 }
795 burstString[gSlotLen+9] = '\0';
796 delete rxBurst;
797
Thomas Tsou204a9f12013-10-29 18:34:16 -0400798 mDataSockets[chan]->write(burstString,gSlotLen+10);
dburgessb3a0ca42011-10-12 07:44:40 +0000799 }
dburgessb3a0ca42011-10-12 07:44:40 +0000800}
801
Thomas Tsou204a9f12013-10-29 18:34:16 -0400802void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +0000803{
804
805 /**
806 Features a carefully controlled latency mechanism, to
807 assure that transmit packets arrive at the radio/USRP
808 before they need to be transmitted.
809
810 Deadline clock indicates the burst that needs to be
811 pushed into the FIFO right NOW. If transmit queue does
812 not have a burst, stick in filler data.
813 */
814
815
816 RadioClock *radioClock = (mRadioInterface->getClock());
817
818 if (mOn) {
819 //radioClock->wait(); // wait until clock updates
820 LOG(DEBUG) << "radio clock " << radioClock->get();
821 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
822 // if underrun, then we're not providing bursts to radio/USRP fast
823 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -0400824 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000825 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +0000826 // only update latency at the defined frame interval
827 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000828 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
829 LOG(INFO) << "new latency: " << mTransmitLatency;
830 mLatencyUpdateTime = radioClock->get();
831 }
832 }
833 else {
834 // if underrun hasn't occurred in the last sec (216 frames) drop
835 // transmit latency by a timeslot
ttsou2173abf2012-08-08 00:51:31 +0000836 if (mTransmitLatency > GSM::Time(USB_LATENCY_MIN)) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000837 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
838 mTransmitLatency.decTN();
839 LOG(INFO) << "reduced latency: " << mTransmitLatency;
840 mLatencyUpdateTime = radioClock->get();
841 }
842 }
843 }
dburgessb3a0ca42011-10-12 07:44:40 +0000844 }
dburgessb3a0ca42011-10-12 07:44:40 +0000845 // time to push burst to transmit FIFO
846 pushRadioVector(mTransmitDeadlineClock);
847 mTransmitDeadlineClock.incTN();
848 }
dburgessb3a0ca42011-10-12 07:44:40 +0000849 }
Thomas Tsou92c16df2013-09-28 18:04:19 -0400850
851 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +0000852}
853
854
855
856void Transceiver::writeClockInterface()
857{
858 char command[50];
859 // FIXME -- This should be adaptive.
860 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
861
862 LOG(INFO) << "ClockInterface: sending " << command;
863
Thomas Tsoud647ec52013-10-29 15:17:34 -0400864 mClockSocket->write(command, strlen(command) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +0000865
866 mLastClockUpdateTime = mTransmitDeadlineClock;
867
Thomas Tsou92c16df2013-09-28 18:04:19 -0400868}
dburgessb3a0ca42011-10-12 07:44:40 +0000869
Thomas Tsou204a9f12013-10-29 18:34:16 -0400870void *RxUpperLoopAdapter(TransceiverChannel *chan)
871{
872 Transceiver *trx = chan->trx;
873 size_t num = chan->num;
874
875 delete chan;
876
Thomas Tsou7553aa92013-11-08 12:50:03 -0500877 trx->setPriority(0.42);
878
Thomas Tsou204a9f12013-10-29 18:34:16 -0400879 while (1) {
880 trx->driveReceiveFIFO(num);
881 pthread_testcancel();
882 }
883 return NULL;
884}
885
886void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +0000887{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500888 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +0000889
dburgessb3a0ca42011-10-12 07:44:40 +0000890 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400891 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -0400892 pthread_testcancel();
893 }
894 return NULL;
895}
896
Thomas Tsou204a9f12013-10-29 18:34:16 -0400897void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -0400898{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500899 transceiver->setPriority(0.44);
900
Thomas Tsou92c16df2013-09-28 18:04:19 -0400901 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400902 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +0000903 pthread_testcancel();
904 }
905 return NULL;
906}
907
Thomas Tsou204a9f12013-10-29 18:34:16 -0400908void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000909{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400910 Transceiver *trx = chan->trx;
911 size_t num = chan->num;
912
913 delete chan;
914
dburgessb3a0ca42011-10-12 07:44:40 +0000915 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400916 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +0000917 pthread_testcancel();
918 }
919 return NULL;
920}
921
Thomas Tsou204a9f12013-10-29 18:34:16 -0400922void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000923{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400924 Transceiver *trx = chan->trx;
925 size_t num = chan->num;
926
927 delete chan;
928
Thomas Tsoua4cf48c2013-11-09 21:44:26 -0500929 trx->setPriority(0.40);
930
dburgessb3a0ca42011-10-12 07:44:40 +0000931 while (1) {
932 bool stale = false;
933 // Flush the UDP packets until a successful transfer.
Thomas Tsou204a9f12013-10-29 18:34:16 -0400934 while (!trx->driveTxPriorityQueue(num)) {
935 stale = true;
dburgessb3a0ca42011-10-12 07:44:40 +0000936 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400937 if (!num && stale) {
dburgessb3a0ca42011-10-12 07:44:40 +0000938 // If a packet was stale, remind the GSM stack of the clock.
Thomas Tsou204a9f12013-10-29 18:34:16 -0400939 trx->writeClockInterface();
dburgessb3a0ca42011-10-12 07:44:40 +0000940 }
941 pthread_testcancel();
942 }
943 return NULL;
944}