blob: 415505c030e2ec4103ebe2350f2eff79ac2cc2cd [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 Tsouccb73e12014-04-15 17:41:28 -0400149 /* Filler table retransmissions - support only on channel 0 */
150 if (filler)
151 mStates[0].mRetrans = true;
152
Thomas Tsoud647ec52013-10-29 15:17:34 -0400153 mClockSocket = new UDPSocket(mBasePort, mAddr.c_str(), mBasePort + 100);
Thomas Tsoud647ec52013-10-29 15:17:34 -0400154
Thomas Tsou204a9f12013-10-29 18:34:16 -0400155 for (size_t i = 0; i < mChans; i++) {
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500156 c_srcport = mBasePort + 2 * i + 1;
157 c_dstport = mBasePort + 2 * i + 101;
158 d_srcport = mBasePort + 2 * i + 2;
159 d_dstport = mBasePort + 2 * i + 102;
160
161 mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport);
162 mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400163 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400164
Thomas Tsou204a9f12013-10-29 18:34:16 -0400165 for (size_t i = 0; i < mChans; i++) {
166 mControlServiceLoopThreads[i] = new Thread(32768);
167 mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
168 mRxServiceLoopThreads[i] = new Thread(32768);
169
170 for (size_t n = 0; n < 8; n++) {
171 burst = modulateBurst(gDummyBurst, 8 + (n % 4 == 0), mSPSTx);
172 scaleVector(*burst, txFullScale);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500173 mStates[i].init(n, burst, filler && !i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400174 delete burst;
175 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400176 }
177
178 return true;
179}
dburgessb3a0ca42011-10-12 07:44:40 +0000180
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500181void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400182 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000183{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500184 signalVector *burst;
185 radioVector *radio_burst;
186
Thomas Tsou204a9f12013-10-29 18:34:16 -0400187 if (chan >= mTxPriorityQueues.size()) {
188 LOG(ALERT) << "Invalid channel " << chan;
189 return;
190 }
191
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500192 if (wTime.TN() > 7) {
193 LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
194 return;
195 }
196
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500197 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
198 scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
dburgessb3a0ca42011-10-12 07:44:40 +0000199
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500200 radio_burst = new radioVector(wTime, burst);
201
202 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000203}
204
Thomas Tsou15d743e2014-01-25 02:34:03 -0500205void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
206{
207 int TN, modFN;
208 TransceiverState *state = &mStates[chan];
209
210 TN = burst->getTime().TN();
211 modFN = burst->getTime().FN() % state->fillerModulus[TN];
212
213 delete state->fillerTable[modFN][TN];
214 state->fillerTable[modFN][TN] = burst->getVector();
215 burst->setVector(NULL);
216}
217
dburgessb3a0ca42011-10-12 07:44:40 +0000218void Transceiver::pushRadioVector(GSM::Time &nowTime)
219{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400220 int TN, modFN;
221 radioVector *burst;
222 TransceiverState *state;
223 std::vector<signalVector *> bursts(mChans);
224 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500225 std::vector<bool> filler(mChans, true);
dburgessb3a0ca42011-10-12 07:44:40 +0000226
Thomas Tsou204a9f12013-10-29 18:34:16 -0400227 for (size_t i = 0; i < mChans; i ++) {
228 state = &mStates[i];
dburgessb3a0ca42011-10-12 07:44:40 +0000229
Thomas Tsou204a9f12013-10-29 18:34:16 -0400230 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
231 LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
Thomas Tsou15d743e2014-01-25 02:34:03 -0500232 if (state->mRetrans)
233 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500234 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400235 }
236
237 TN = nowTime.TN();
238 modFN = nowTime.FN() % state->fillerModulus[TN];
239
240 bursts[i] = state->fillerTable[modFN][TN];
241 zeros[i] = state->chanType[TN] == NONE;
242
243 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500244 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500245
246 if (state->mRetrans) {
247 updateFillerTable(i, burst);
248 } else {
249 burst->setVector(NULL);
250 filler[i] = false;
251 }
252
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500253 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400254 }
dburgessb3a0ca42011-10-12 07:44:40 +0000255 }
256
Thomas Tsou204a9f12013-10-29 18:34:16 -0400257 mRadioInterface->driveTransmitRadio(bursts, zeros);
258
Thomas Tsou15d743e2014-01-25 02:34:03 -0500259 for (size_t i = 0; i < mChans; i++) {
260 if (!filler[i])
261 delete bursts[i];
262 }
dburgessb3a0ca42011-10-12 07:44:40 +0000263}
264
Thomas Tsou204a9f12013-10-29 18:34:16 -0400265void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000266{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400267 TransceiverState *state = &mStates[chan];
268
269 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000270 case NONE:
271 case I:
272 case II:
273 case III:
274 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400275 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000276 break;
277 case IV:
278 case VI:
279 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400280 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000281 break;
282 //case V:
283 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400284 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000285 break;
ttsoufc40a842013-06-09 22:38:18 +0000286 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400287 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000288 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000289 default:
290 break;
291 }
292}
293
294
Thomas Tsou204a9f12013-10-29 18:34:16 -0400295Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
296 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000297{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400298 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000299 unsigned burstTN = currTime.TN();
300 unsigned burstFN = currTime.FN();
301
Thomas Tsou204a9f12013-10-29 18:34:16 -0400302 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000303 case NONE:
304 return OFF;
305 break;
306 case FILL:
307 return IDLE;
308 break;
309 case I:
310 return TSC;
311 /*if (burstFN % 26 == 25)
312 return IDLE;
313 else
314 return TSC;*/
315 break;
316 case II:
ttsou20642972013-03-27 22:00:25 +0000317 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000318 break;
319 case III:
320 return TSC;
321 break;
322 case IV:
323 case VI:
324 return RACH;
325 break;
326 case V: {
327 int mod51 = burstFN % 51;
328 if ((mod51 <= 36) && (mod51 >= 14))
329 return RACH;
330 else if ((mod51 == 4) || (mod51 == 5))
331 return RACH;
332 else if ((mod51 == 45) || (mod51 == 46))
333 return RACH;
334 else
335 return TSC;
336 break;
337 }
338 case VII:
339 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
340 return IDLE;
341 else
342 return TSC;
343 break;
ttsoufc40a842013-06-09 22:38:18 +0000344 case XIII: {
345 int mod52 = burstFN % 52;
346 if ((mod52 == 12) || (mod52 == 38))
347 return RACH;
348 else if ((mod52 == 25) || (mod52 == 51))
349 return IDLE;
350 else
351 return TSC;
352 break;
353 }
dburgessb3a0ca42011-10-12 07:44:40 +0000354 case LOOPBACK:
355 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
356 return IDLE;
357 else
358 return TSC;
359 break;
360 default:
361 return OFF;
362 break;
363 }
dburgessb3a0ca42011-10-12 07:44:40 +0000364}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400365
Thomas Tsou30421a72013-11-13 23:14:48 -0500366/*
367 * Detect RACH synchronization sequence within a burst. No equalization
368 * is used or available on the RACH channel.
369 */
370bool Transceiver::detectRACH(TransceiverState *state,
371 signalVector &burst,
372 complex &amp, float &toa)
373{
374 float threshold = 6.0;
375
376 return detectRACHBurst(burst, threshold, mSPSRx, &amp, &toa);
377}
378
379/*
380 * Detect normal burst training sequence midamble. Update equalization
381 * state information and channel estimate if necessary. Equalization
382 * is currently disabled.
383 */
Thomas Tsoua0179e32013-11-14 15:52:04 -0500384bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
Thomas Tsou30421a72013-11-13 23:14:48 -0500385 complex &amp, float &toa, GSM::Time &time)
386{
387 int tn = time.TN();
388 float chanOffset, threshold = 5.0;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500389 bool noise, needDFE = false, estimateChan = false;
Thomas Tsou30421a72013-11-13 23:14:48 -0500390 double elapsed = time - state->chanEstimateTime[tn];
391 signalVector *chanResp;
392
393 /* Check equalization update state */
Thomas Tsoufb827d02013-11-16 16:14:12 -0500394 if (needDFE && ((elapsed > 50) || (!state->chanResponse[tn]))) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500395 delete state->DFEForward[tn];
396 delete state->DFEFeedback[tn];
397 state->DFEForward[tn] = NULL;
398 state->DFEFeedback[tn] = NULL;
399
400 estimateChan = true;
401 }
402
403 /* Detect normal burst midambles */
404 if (!analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, &amp,
405 &toa, mMaxExpectedDelay, estimateChan,
406 &chanResp, &chanOffset)) {
407 return false;
408 }
409
Thomas Tsoua0179e32013-11-14 15:52:04 -0500410 noise = state->mNoiseLev;
411 state->SNRestimate[tn] = amp.norm2() / (noise * noise + 1.0);
Thomas Tsou30421a72013-11-13 23:14:48 -0500412
413 /* Set equalizer if unabled */
414 if (needDFE && estimateChan) {
415 state->chanResponse[tn] = chanResp;
416 state->chanRespOffset[tn] = chanOffset;
417 state->chanRespAmplitude[tn] = amp;
418
419 scaleVector(*chanResp, complex(1.0, 0.0) / amp);
420
421 designDFE(*chanResp, state->SNRestimate[tn],
422 7, &state->DFEForward[tn], &state->DFEFeedback[tn]);
423
424 state->chanEstimateTime[tn] = time;
425 }
426
427 return true;;
428}
429
430/*
431 * Demodulate GMSK burst using equalization if requested. Otherwise
432 * demodulate by direct rotation and soft slicing.
433 */
434SoftVector *Transceiver::demodulate(TransceiverState *state,
435 signalVector &burst, complex amp,
436 float toa, size_t tn, bool equalize)
437{
438 if (equalize) {
439 scaleVector(burst, complex(1.0, 0.0) / amp);
440 return equalizeBurst(burst,
441 toa - state->chanRespOffset[tn],
442 mSPSRx,
443 *state->DFEForward[tn],
444 *state->DFEFeedback[tn]);
445 }
446
447 return demodulateBurst(burst, mSPSRx, amp, toa);
448}
449
450/*
451 * Pull bursts from the FIFO and handle according to the slot
452 * and burst correlation type. Equalzation is currently disabled.
453 */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400454SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
455 int &timingOffset, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000456{
Thomas Tsou30421a72013-11-13 23:14:48 -0500457 bool success, equalize = false;
458 complex amp;
459 float toa, pow, max = -1.0, avg = 0.0;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500460 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500461 signalVector *burst;
Thomas Tsouef25dba2013-11-14 15:31:24 -0500462 SoftVector *bits = NULL;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500463 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000464
Thomas Tsou30421a72013-11-13 23:14:48 -0500465 /* Blocking FIFO read */
466 radioVector *radio_burst = mReceiveFIFO[chan]->read();
467 if (!radio_burst)
468 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000469
Thomas Tsou30421a72013-11-13 23:14:48 -0500470 /* Set time and determine correlation type */
471 GSM::Time time = radio_burst->getTime();
472 CorrType type = expectedCorrType(time, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000473
Thomas Tsou30421a72013-11-13 23:14:48 -0500474 if ((type == OFF) || (type == IDLE)) {
475 delete radio_burst;
dburgessb3a0ca42011-10-12 07:44:40 +0000476 return NULL;
477 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000478
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500479 /* Select the diversity channel with highest energy */
480 for (size_t i = 0; i < radio_burst->chans(); i++) {
481 energyDetect(*radio_burst->getVector(i), 20 * mSPSRx, 0.0, &pow);
482 if (pow > max) {
483 max = pow;
484 max_i = i;
485 }
486 avg += pow;
487 }
488
489 if (max_i < 0) {
490 LOG(ALERT) << "Received empty burst";
491 delete radio_burst;
492 return NULL;
493 }
494
Thomas Tsou30421a72013-11-13 23:14:48 -0500495 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500496 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500497 avg = sqrt(avg / radio_burst->chans());
Thomas Tsoua0179e32013-11-14 15:52:04 -0500498 state->mNoiseLev = state->mNoises.avg();
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400499
Thomas Tsou30421a72013-11-13 23:14:48 -0500500 /* Detect normal or RACH bursts */
501 if (type == TSC)
Thomas Tsoua0179e32013-11-14 15:52:04 -0500502 success = detectTSC(state, *burst, amp, toa, time);
Thomas Tsou30421a72013-11-13 23:14:48 -0500503 else
Thomas Tsoua0179e32013-11-14 15:52:04 -0500504 success = detectRACH(state, *burst, amp, toa);
Thomas Tsouf0782732013-10-29 15:55:47 -0400505
Thomas Tsou30421a72013-11-13 23:14:48 -0500506 if (!success) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500507 state->mNoises.insert(avg);
Thomas Tsou30421a72013-11-13 23:14:48 -0500508 delete radio_burst;
509 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000510 }
dburgessb3a0ca42011-10-12 07:44:40 +0000511
Thomas Tsou30421a72013-11-13 23:14:48 -0500512 /* Demodulate and set output info */
513 if (equalize && (type != TSC))
514 equalize = false;
dburgessb3a0ca42011-10-12 07:44:40 +0000515
Thomas Tsoua0179e32013-11-14 15:52:04 -0500516 if (avg - state->mNoiseLev > 0.0)
517 bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500518
Thomas Tsou30421a72013-11-13 23:14:48 -0500519 wTime = time;
520 RSSI = (int) floor(20.0 * log10(rxFullScale / avg));
521 timingOffset = (int) round(toa * 256.0 / mSPSRx);
dburgessb3a0ca42011-10-12 07:44:40 +0000522
Thomas Tsou30421a72013-11-13 23:14:48 -0500523 delete radio_burst;
524
525 return bits;
dburgessb3a0ca42011-10-12 07:44:40 +0000526}
527
528void Transceiver::start()
529{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400530 TransceiverChannel *chan;
531
532 for (size_t i = 0; i < mControlServiceLoopThreads.size(); i++) {
533 chan = new TransceiverChannel(this, i);
534 mControlServiceLoopThreads[i]->start((void * (*)(void*))
535 ControlServiceLoopAdapter, (void*) chan);
536 }
dburgessb3a0ca42011-10-12 07:44:40 +0000537}
538
539void Transceiver::reset()
540{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400541 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
542 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000543}
544
545
Thomas Tsou204a9f12013-10-29 18:34:16 -0400546void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000547{
dburgessb3a0ca42011-10-12 07:44:40 +0000548 int MAX_PACKET_LENGTH = 100;
549
550 // check control socket
551 char buffer[MAX_PACKET_LENGTH];
552 int msgLen = -1;
553 buffer[0] = '\0';
Thomas Tsoud647ec52013-10-29 15:17:34 -0400554
Thomas Tsou204a9f12013-10-29 18:34:16 -0400555 msgLen = mCtrlSockets[chan]->read(buffer);
dburgessb3a0ca42011-10-12 07:44:40 +0000556
557 if (msgLen < 1) {
558 return;
559 }
560
561 char cmdcheck[4];
562 char command[MAX_PACKET_LENGTH];
563 char response[MAX_PACKET_LENGTH];
564
565 sscanf(buffer,"%3s %s",cmdcheck,command);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400566
567 if (!chan)
568 writeClockInterface();
dburgessb3a0ca42011-10-12 07:44:40 +0000569
570 if (strcmp(cmdcheck,"CMD")!=0) {
571 LOG(WARNING) << "bogus message on control interface";
572 return;
573 }
574 LOG(INFO) << "command is " << buffer;
575
576 if (strcmp(command,"POWEROFF")==0) {
577 // turn off transmitter/demod
578 sprintf(response,"RSP POWEROFF 0");
579 }
580 else if (strcmp(command,"POWERON")==0) {
581 // turn on transmitter/demod
582 if (!mTxFreq || !mRxFreq)
583 sprintf(response,"RSP POWERON 1");
584 else {
585 sprintf(response,"RSP POWERON 0");
Thomas Tsou204a9f12013-10-29 18:34:16 -0400586 if (!chan && !mOn) {
dburgessb3a0ca42011-10-12 07:44:40 +0000587 // Prepare for thread start
588 mPower = -20;
589 mRadioInterface->start();
dburgessb3a0ca42011-10-12 07:44:40 +0000590
591 // Start radio interface threads.
Thomas Tsou204a9f12013-10-29 18:34:16 -0400592 mTxLowerLoopThread->start((void * (*)(void*))
593 TxLowerLoopAdapter,(void*) this);
594 mRxLowerLoopThread->start((void * (*)(void*))
595 RxLowerLoopAdapter,(void*) this);
dburgessb3a0ca42011-10-12 07:44:40 +0000596
Thomas Tsou204a9f12013-10-29 18:34:16 -0400597 for (size_t i = 0; i < mChans; i++) {
598 TransceiverChannel *chan = new TransceiverChannel(this, i);
599 mRxServiceLoopThreads[i]->start((void * (*)(void*))
600 RxUpperLoopAdapter, (void*) chan);
601
602 chan = new TransceiverChannel(this, i);
603 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
604 TxUpperLoopAdapter, (void*) chan);
605 }
606
607 writeClockInterface();
dburgessb3a0ca42011-10-12 07:44:40 +0000608 mOn = true;
609 }
610 }
611 }
612 else if (strcmp(command,"SETMAXDLY")==0) {
613 //set expected maximum time-of-arrival
614 int maxDelay;
615 sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
616 mMaxExpectedDelay = maxDelay; // 1 GSM symbol is approx. 1 km
617 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
618 }
619 else if (strcmp(command,"SETRXGAIN")==0) {
620 //set expected maximum time-of-arrival
621 int newGain;
622 sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400623 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000624 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
625 }
626 else if (strcmp(command,"NOISELEV")==0) {
627 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500628 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000629 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500630 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000631 }
632 else {
633 sprintf(response,"RSP NOISELEV 1 0");
634 }
Thomas Tsoucb269a32013-11-15 14:15:47 -0500635 }
636 else if (!strcmp(command, "SETPOWER")) {
dburgessb3a0ca42011-10-12 07:44:40 +0000637 // set output power in dB
638 int dbPwr;
Thomas Tsoucb269a32013-11-15 14:15:47 -0500639 sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbPwr);
640 if (!mOn)
641 sprintf(response, "RSP SETPOWER 1 %d", dbPwr);
dburgessb3a0ca42011-10-12 07:44:40 +0000642 else {
643 mPower = dbPwr;
Thomas Tsoucb269a32013-11-15 14:15:47 -0500644 mRadioInterface->setPowerAttenuation(mPower, chan);
645 sprintf(response, "RSP SETPOWER 0 %d", dbPwr);
dburgessb3a0ca42011-10-12 07:44:40 +0000646 }
647 }
Thomas Tsoucb269a32013-11-15 14:15:47 -0500648 else if (!strcmp(command,"ADJPOWER")) {
dburgessb3a0ca42011-10-12 07:44:40 +0000649 // adjust power in dB steps
650 int dbStep;
Thomas Tsoucb269a32013-11-15 14:15:47 -0500651 sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbStep);
652 if (!mOn)
653 sprintf(response, "RSP ADJPOWER 1 %d", mPower);
dburgessb3a0ca42011-10-12 07:44:40 +0000654 else {
655 mPower += dbStep;
Thomas Tsoucb269a32013-11-15 14:15:47 -0500656 mRadioInterface->setPowerAttenuation(mPower, chan);
657 sprintf(response, "RSP ADJPOWER 0 %d", mPower);
dburgessb3a0ca42011-10-12 07:44:40 +0000658 }
659 }
dburgessb3a0ca42011-10-12 07:44:40 +0000660 else if (strcmp(command,"RXTUNE")==0) {
661 // tune receiver
662 int freqKhz;
663 sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500664 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400665 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000666 LOG(ALERT) << "RX failed to tune";
667 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
668 }
669 else
670 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
671 }
672 else if (strcmp(command,"TXTUNE")==0) {
673 // tune txmtr
674 int freqKhz;
675 sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500676 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400677 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000678 LOG(ALERT) << "TX failed to tune";
679 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
680 }
681 else
682 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
683 }
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500684 else if (!strcmp(command,"SETTSC")) {
dburgessb3a0ca42011-10-12 07:44:40 +0000685 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500686 unsigned TSC;
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500687 sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000688 if (mOn)
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500689 sprintf(response, "RSP SETTSC 1 %d", TSC);
690 else if (chan && (TSC != mTSC))
691 sprintf(response, "RSP SETTSC 1 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000692 else {
693 mTSC = TSC;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400694 generateMidamble(mSPSRx, TSC);
Thomas Tsou83e06892013-08-20 16:10:01 -0400695 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000696 }
697 }
698 else if (strcmp(command,"SETSLOT")==0) {
699 // set TSC
700 int corrCode;
701 int timeslot;
702 sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&timeslot,&corrCode);
703 if ((timeslot < 0) || (timeslot > 7)) {
704 LOG(WARNING) << "bogus message on control interface";
705 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
706 return;
707 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400708 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
709 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000710 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
711
712 }
713 else {
714 LOG(WARNING) << "bogus command " << command << " on control interface.";
715 }
716
Thomas Tsou204a9f12013-10-29 18:34:16 -0400717 mCtrlSockets[chan]->write(response, strlen(response) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +0000718}
719
Thomas Tsou204a9f12013-10-29 18:34:16 -0400720bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000721{
dburgessb3a0ca42011-10-12 07:44:40 +0000722 char buffer[gSlotLen+50];
723
724 // check data socket
Thomas Tsou204a9f12013-10-29 18:34:16 -0400725 size_t msgLen = mDataSockets[chan]->read(buffer);
dburgessb3a0ca42011-10-12 07:44:40 +0000726
727 if (msgLen!=gSlotLen+1+4+1) {
728 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
729 return false;
730 }
731
732 int timeSlot = (int) buffer[0];
733 uint64_t frameNum = 0;
734 for (int i = 0; i < 4; i++)
735 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
dburgessb3a0ca42011-10-12 07:44:40 +0000736
dburgessb3a0ca42011-10-12 07:44:40 +0000737 // periodically update GSM core clock
738 LOG(DEBUG) << "mTransmitDeadlineClock " << mTransmitDeadlineClock
739 << " mLastClockUpdateTime " << mLastClockUpdateTime;
dburgessb3a0ca42011-10-12 07:44:40 +0000740
Thomas Tsou204a9f12013-10-29 18:34:16 -0400741 if (!chan) {
742 if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
743 writeClockInterface();
744 }
dburgessb3a0ca42011-10-12 07:44:40 +0000745
746 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
747
748 int RSSI = (int) buffer[5];
749 static BitVector newBurst(gSlotLen);
750 BitVector::iterator itr = newBurst.begin();
751 char *bufferItr = buffer+6;
752 while (itr < newBurst.end())
753 *itr++ = *bufferItr++;
754
755 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400756
757 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000758
759 return true;
760
761
762}
dburgessb3a0ca42011-10-12 07:44:40 +0000763
Thomas Tsou204a9f12013-10-29 18:34:16 -0400764void Transceiver::driveReceiveRadio()
765{
766 if (!mRadioInterface->driveReceiveRadio())
767 usleep(100000);
768}
769
770void Transceiver::driveReceiveFIFO(size_t chan)
771{
dburgessb3a0ca42011-10-12 07:44:40 +0000772 SoftVector *rxBurst = NULL;
773 int RSSI;
774 int TOA; // in 1/256 of a symbol
775 GSM::Time burstTime;
776
Thomas Tsou204a9f12013-10-29 18:34:16 -0400777 rxBurst = pullRadioVector(burstTime, RSSI, TOA, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000778
779 if (rxBurst) {
780
781 LOG(DEBUG) << "burst parameters: "
782 << " time: " << burstTime
783 << " RSSI: " << RSSI
784 << " TOA: " << TOA
785 << " bits: " << *rxBurst;
786
787 char burstString[gSlotLen+10];
788 burstString[0] = burstTime.TN();
789 for (int i = 0; i < 4; i++)
790 burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
791 burstString[5] = RSSI;
792 burstString[6] = (TOA >> 8) & 0x0ff;
793 burstString[7] = TOA & 0x0ff;
794 SoftVector::iterator burstItr = rxBurst->begin();
795
796 for (unsigned int i = 0; i < gSlotLen; i++) {
797 burstString[8+i] =(char) round((*burstItr++)*255.0);
798 }
799 burstString[gSlotLen+9] = '\0';
800 delete rxBurst;
801
Thomas Tsou204a9f12013-10-29 18:34:16 -0400802 mDataSockets[chan]->write(burstString,gSlotLen+10);
dburgessb3a0ca42011-10-12 07:44:40 +0000803 }
dburgessb3a0ca42011-10-12 07:44:40 +0000804}
805
Thomas Tsou204a9f12013-10-29 18:34:16 -0400806void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +0000807{
808
809 /**
810 Features a carefully controlled latency mechanism, to
811 assure that transmit packets arrive at the radio/USRP
812 before they need to be transmitted.
813
814 Deadline clock indicates the burst that needs to be
815 pushed into the FIFO right NOW. If transmit queue does
816 not have a burst, stick in filler data.
817 */
818
819
820 RadioClock *radioClock = (mRadioInterface->getClock());
821
822 if (mOn) {
823 //radioClock->wait(); // wait until clock updates
824 LOG(DEBUG) << "radio clock " << radioClock->get();
825 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
826 // if underrun, then we're not providing bursts to radio/USRP fast
827 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -0400828 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000829 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +0000830 // only update latency at the defined frame interval
831 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000832 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
833 LOG(INFO) << "new latency: " << mTransmitLatency;
834 mLatencyUpdateTime = radioClock->get();
835 }
836 }
837 else {
838 // if underrun hasn't occurred in the last sec (216 frames) drop
839 // transmit latency by a timeslot
ttsou2173abf2012-08-08 00:51:31 +0000840 if (mTransmitLatency > GSM::Time(USB_LATENCY_MIN)) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000841 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
842 mTransmitLatency.decTN();
843 LOG(INFO) << "reduced latency: " << mTransmitLatency;
844 mLatencyUpdateTime = radioClock->get();
845 }
846 }
847 }
dburgessb3a0ca42011-10-12 07:44:40 +0000848 }
dburgessb3a0ca42011-10-12 07:44:40 +0000849 // time to push burst to transmit FIFO
850 pushRadioVector(mTransmitDeadlineClock);
851 mTransmitDeadlineClock.incTN();
852 }
dburgessb3a0ca42011-10-12 07:44:40 +0000853 }
Thomas Tsou92c16df2013-09-28 18:04:19 -0400854
855 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +0000856}
857
858
859
860void Transceiver::writeClockInterface()
861{
862 char command[50];
863 // FIXME -- This should be adaptive.
864 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
865
866 LOG(INFO) << "ClockInterface: sending " << command;
867
Thomas Tsoud647ec52013-10-29 15:17:34 -0400868 mClockSocket->write(command, strlen(command) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +0000869
870 mLastClockUpdateTime = mTransmitDeadlineClock;
871
Thomas Tsou92c16df2013-09-28 18:04:19 -0400872}
dburgessb3a0ca42011-10-12 07:44:40 +0000873
Thomas Tsou204a9f12013-10-29 18:34:16 -0400874void *RxUpperLoopAdapter(TransceiverChannel *chan)
875{
876 Transceiver *trx = chan->trx;
877 size_t num = chan->num;
878
879 delete chan;
880
Thomas Tsou7553aa92013-11-08 12:50:03 -0500881 trx->setPriority(0.42);
882
Thomas Tsou204a9f12013-10-29 18:34:16 -0400883 while (1) {
884 trx->driveReceiveFIFO(num);
885 pthread_testcancel();
886 }
887 return NULL;
888}
889
890void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +0000891{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500892 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +0000893
dburgessb3a0ca42011-10-12 07:44:40 +0000894 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400895 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -0400896 pthread_testcancel();
897 }
898 return NULL;
899}
900
Thomas Tsou204a9f12013-10-29 18:34:16 -0400901void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -0400902{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500903 transceiver->setPriority(0.44);
904
Thomas Tsou92c16df2013-09-28 18:04:19 -0400905 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400906 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +0000907 pthread_testcancel();
908 }
909 return NULL;
910}
911
Thomas Tsou204a9f12013-10-29 18:34:16 -0400912void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000913{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400914 Transceiver *trx = chan->trx;
915 size_t num = chan->num;
916
917 delete chan;
918
dburgessb3a0ca42011-10-12 07:44:40 +0000919 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400920 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +0000921 pthread_testcancel();
922 }
923 return NULL;
924}
925
Thomas Tsou204a9f12013-10-29 18:34:16 -0400926void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000927{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400928 Transceiver *trx = chan->trx;
929 size_t num = chan->num;
930
931 delete chan;
932
Thomas Tsoua4cf48c2013-11-09 21:44:26 -0500933 trx->setPriority(0.40);
934
dburgessb3a0ca42011-10-12 07:44:40 +0000935 while (1) {
936 bool stale = false;
937 // Flush the UDP packets until a successful transfer.
Thomas Tsou204a9f12013-10-29 18:34:16 -0400938 while (!trx->driveTxPriorityQueue(num)) {
939 stale = true;
dburgessb3a0ca42011-10-12 07:44:40 +0000940 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400941 if (!num && stale) {
dburgessb3a0ca42011-10-12 07:44:40 +0000942 // If a packet was stale, remind the GSM stack of the clock.
Thomas Tsou204a9f12013-10-29 18:34:16 -0400943 trx->writeClockInterface();
dburgessb3a0ca42011-10-12 07:44:40 +0000944 }
945 pthread_testcancel();
946 }
947 return NULL;
948}