blob: 488565433832109961b7d6e42136f1e888ef67b8 [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()
Tom Tsoua4d1a412014-11-25 15:46:56 -080046 : mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
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,
Tom Tsoueb54bdd2014-11-25 16:06:32 -080087 const char *wTRXAddress,
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)
Tom Tsoueb54bdd2014-11-25 16:06:32 -080091 : mBasePort(wBasePort), mAddr(wTRXAddress),
92 mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
93 mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
94 mSPSTx(wSPS), mSPSRx(1), mChans(wChans), mOn(false),
95 mTxFreq(0.0), mRxFreq(0.0), mMaxExpectedDelay(0)
dburgessb3a0ca42011-10-12 07:44:40 +000096{
dburgessb3a0ca42011-10-12 07:44:40 +000097 txFullScale = mRadioInterface->fullScaleInputValue();
98 rxFullScale = mRadioInterface->fullScaleOutputValue();
dburgessb3a0ca42011-10-12 07:44:40 +000099}
100
101Transceiver::~Transceiver()
102{
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800103 stop();
104
dburgessb3a0ca42011-10-12 07:44:40 +0000105 sigProcLibDestroy();
Thomas Tsoud647ec52013-10-29 15:17:34 -0400106
Thomas Tsou204a9f12013-10-29 18:34:16 -0400107 for (size_t i = 0; i < mChans; i++) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800108 mControlServiceLoopThreads[i]->cancel();
109 mControlServiceLoopThreads[i]->join();
110 delete mControlServiceLoopThreads[i];
111
Thomas Tsou204a9f12013-10-29 18:34:16 -0400112 mTxPriorityQueues[i].clear();
113 delete mCtrlSockets[i];
114 delete mDataSockets[i];
115 }
dburgessb3a0ca42011-10-12 07:44:40 +0000116}
Thomas Tsou83e06892013-08-20 16:10:01 -0400117
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800118/*
119 * Initialize transceiver
120 *
121 * Start or restart the control loop. Any further control is handled through the
122 * socket API. Randomize the central radio clock set the downlink burst
123 * counters. Note that the clock will not update until the radio starts, but we
124 * are still expected to report clock indications through control channel
125 * activity.
126 */
Thomas Tsou15d743e2014-01-25 02:34:03 -0500127bool Transceiver::init(bool filler)
Thomas Tsou83e06892013-08-20 16:10:01 -0400128{
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500129 int d_srcport, d_dstport, c_srcport, c_dstport;
Thomas Tsouf0782732013-10-29 15:55:47 -0400130 signalVector *burst;
131
Thomas Tsou204a9f12013-10-29 18:34:16 -0400132 if (!mChans) {
133 LOG(ALERT) << "No channels assigned";
134 return false;
135 }
136
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400137 if (!sigProcLibSetup(mSPSTx)) {
Thomas Tsou83e06892013-08-20 16:10:01 -0400138 LOG(ALERT) << "Failed to initialize signal processing library";
139 return false;
140 }
141
Thomas Tsou204a9f12013-10-29 18:34:16 -0400142 mDataSockets.resize(mChans);
143 mCtrlSockets.resize(mChans);
144
145 mControlServiceLoopThreads.resize(mChans);
146 mTxPriorityQueueServiceLoopThreads.resize(mChans);
147 mRxServiceLoopThreads.resize(mChans);
148
149 mTxPriorityQueues.resize(mChans);
150 mReceiveFIFO.resize(mChans);
151 mStates.resize(mChans);
152
Thomas Tsouccb73e12014-04-15 17:41:28 -0400153 /* Filler table retransmissions - support only on channel 0 */
154 if (filler)
155 mStates[0].mRetrans = true;
156
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800157 /* Setup sockets */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400158 for (size_t i = 0; i < mChans; i++) {
Thomas Tsoue1ce9252013-11-13 22:40:44 -0500159 c_srcport = mBasePort + 2 * i + 1;
160 c_dstport = mBasePort + 2 * i + 101;
161 d_srcport = mBasePort + 2 * i + 2;
162 d_dstport = mBasePort + 2 * i + 102;
163
164 mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport);
165 mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400166 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400167
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800168 /* Randomize the central clock */
169 GSM::Time startTime(random() % gHyperframe, 0);
170 mRadioInterface->getClock()->set(startTime);
171 mTransmitDeadlineClock = startTime;
172 mLastClockUpdateTime = startTime;
173 mLatencyUpdateTime = startTime;
174
175 /* Start control threads */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400176 for (size_t i = 0; i < mChans; i++) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800177 TransceiverChannel *chan = new TransceiverChannel(this, i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400178 mControlServiceLoopThreads[i] = new Thread(32768);
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800179 mControlServiceLoopThreads[i]->start((void * (*)(void*))
180 ControlServiceLoopAdapter, (void*) chan);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400181
182 for (size_t n = 0; n < 8; n++) {
183 burst = modulateBurst(gDummyBurst, 8 + (n % 4 == 0), mSPSTx);
184 scaleVector(*burst, txFullScale);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500185 mStates[i].init(n, burst, filler && !i);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400186 delete burst;
187 }
Thomas Tsou83e06892013-08-20 16:10:01 -0400188 }
189
190 return true;
191}
dburgessb3a0ca42011-10-12 07:44:40 +0000192
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800193/*
194 * Start the transceiver
195 *
196 * Submit command(s) to the radio device to commence streaming samples and
197 * launch threads to handle sample I/O. Re-synchronize the transmit burst
198 * counters to the central radio clock here as well.
199 */
200bool Transceiver::start()
201{
202 ScopedLock lock(mLock);
203
204 if (mOn) {
205 LOG(ERR) << "Transceiver already running";
206 return false;
207 }
208
209 LOG(NOTICE) << "Starting the transceiver";
210
211 GSM::Time time = mRadioInterface->getClock()->get();
212 mTransmitDeadlineClock = time;
213 mLastClockUpdateTime = time;
214 mLatencyUpdateTime = time;
215
216 if (!mRadioInterface->start()) {
217 LOG(ALERT) << "Device failed to start";
218 return false;
219 }
220
221 /* Device is running - launch I/O threads */
222 mRxLowerLoopThread = new Thread(32768);
223 mTxLowerLoopThread = new Thread(32768);
224 mTxLowerLoopThread->start((void * (*)(void*))
225 TxLowerLoopAdapter,(void*) this);
226 mRxLowerLoopThread->start((void * (*)(void*))
227 RxLowerLoopAdapter,(void*) this);
228
229 /* Launch uplink and downlink burst processing threads */
230 for (size_t i = 0; i < mChans; i++) {
231 TransceiverChannel *chan = new TransceiverChannel(this, i);
232 mRxServiceLoopThreads[i] = new Thread(32768);
233 mRxServiceLoopThreads[i]->start((void * (*)(void*))
234 RxUpperLoopAdapter, (void*) chan);
235
236 chan = new TransceiverChannel(this, i);
237 mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
238 mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
239 TxUpperLoopAdapter, (void*) chan);
240 }
241
242 writeClockInterface();
243 mOn = true;
244 return true;
245}
246
247/*
248 * Stop the transceiver
249 *
250 * Perform stopping by disabling receive streaming and issuing cancellation
251 * requests to running threads. Most threads will timeout and terminate once
252 * device is disabled, but the transmit loop may block waiting on the central
253 * UMTS clock. Explicitly signal the clock to make sure that the transmit loop
254 * makes it to the thread cancellation point.
255 */
256void Transceiver::stop()
257{
258 ScopedLock lock(mLock);
259
260 if (!mOn)
261 return;
262
263 LOG(NOTICE) << "Stopping the transceiver";
264 mTxLowerLoopThread->cancel();
265 mRxLowerLoopThread->cancel();
266
267 for (size_t i = 0; i < mChans; i++) {
268 mRxServiceLoopThreads[i]->cancel();
269 mTxPriorityQueueServiceLoopThreads[i]->cancel();
270 }
271
272 LOG(INFO) << "Stopping the device";
273 mRadioInterface->stop();
274
275 for (size_t i = 0; i < mChans; i++) {
276 mRxServiceLoopThreads[i]->join();
277 mTxPriorityQueueServiceLoopThreads[i]->join();
278 delete mRxServiceLoopThreads[i];
279 delete mTxPriorityQueueServiceLoopThreads[i];
280
281 mTxPriorityQueues[i].clear();
282 }
283
284 mTxLowerLoopThread->join();
285 mRxLowerLoopThread->join();
286 delete mTxLowerLoopThread;
287 delete mRxLowerLoopThread;
288
289 mOn = false;
290 LOG(NOTICE) << "Transceiver stopped";
291}
292
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500293void Transceiver::addRadioVector(size_t chan, BitVector &bits,
Thomas Tsou204a9f12013-10-29 18:34:16 -0400294 int RSSI, GSM::Time &wTime)
dburgessb3a0ca42011-10-12 07:44:40 +0000295{
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500296 signalVector *burst;
297 radioVector *radio_burst;
298
Thomas Tsou204a9f12013-10-29 18:34:16 -0400299 if (chan >= mTxPriorityQueues.size()) {
300 LOG(ALERT) << "Invalid channel " << chan;
301 return;
302 }
303
Thomas Tsou2d0c00b2013-11-14 15:28:23 -0500304 if (wTime.TN() > 7) {
305 LOG(ALERT) << "Received burst with invalid slot " << wTime.TN();
306 return;
307 }
308
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500309 burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
310 scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
dburgessb3a0ca42011-10-12 07:44:40 +0000311
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500312 radio_burst = new radioVector(wTime, burst);
313
314 mTxPriorityQueues[chan].write(radio_burst);
dburgessb3a0ca42011-10-12 07:44:40 +0000315}
316
Thomas Tsou15d743e2014-01-25 02:34:03 -0500317void Transceiver::updateFillerTable(size_t chan, radioVector *burst)
318{
319 int TN, modFN;
320 TransceiverState *state = &mStates[chan];
321
322 TN = burst->getTime().TN();
323 modFN = burst->getTime().FN() % state->fillerModulus[TN];
324
325 delete state->fillerTable[modFN][TN];
326 state->fillerTable[modFN][TN] = burst->getVector();
327 burst->setVector(NULL);
328}
329
dburgessb3a0ca42011-10-12 07:44:40 +0000330void Transceiver::pushRadioVector(GSM::Time &nowTime)
331{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400332 int TN, modFN;
333 radioVector *burst;
334 TransceiverState *state;
335 std::vector<signalVector *> bursts(mChans);
336 std::vector<bool> zeros(mChans);
Thomas Tsou15d743e2014-01-25 02:34:03 -0500337 std::vector<bool> filler(mChans, true);
dburgessb3a0ca42011-10-12 07:44:40 +0000338
Thomas Tsou204a9f12013-10-29 18:34:16 -0400339 for (size_t i = 0; i < mChans; i ++) {
340 state = &mStates[i];
dburgessb3a0ca42011-10-12 07:44:40 +0000341
Thomas Tsou204a9f12013-10-29 18:34:16 -0400342 while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) {
343 LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
Thomas Tsou15d743e2014-01-25 02:34:03 -0500344 if (state->mRetrans)
345 updateFillerTable(i, burst);
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500346 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400347 }
348
349 TN = nowTime.TN();
350 modFN = nowTime.FN() % state->fillerModulus[TN];
351
352 bursts[i] = state->fillerTable[modFN][TN];
353 zeros[i] = state->chanType[TN] == NONE;
354
355 if ((burst = mTxPriorityQueues[i].getCurrentBurst(nowTime))) {
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500356 bursts[i] = burst->getVector();
Thomas Tsou15d743e2014-01-25 02:34:03 -0500357
358 if (state->mRetrans) {
359 updateFillerTable(i, burst);
360 } else {
361 burst->setVector(NULL);
362 filler[i] = false;
363 }
364
Thomas Tsoua2fe91a2013-11-13 22:48:11 -0500365 delete burst;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400366 }
dburgessb3a0ca42011-10-12 07:44:40 +0000367 }
368
Thomas Tsou204a9f12013-10-29 18:34:16 -0400369 mRadioInterface->driveTransmitRadio(bursts, zeros);
370
Thomas Tsou15d743e2014-01-25 02:34:03 -0500371 for (size_t i = 0; i < mChans; i++) {
372 if (!filler[i])
373 delete bursts[i];
374 }
dburgessb3a0ca42011-10-12 07:44:40 +0000375}
376
Thomas Tsou204a9f12013-10-29 18:34:16 -0400377void Transceiver::setModulus(size_t timeslot, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000378{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400379 TransceiverState *state = &mStates[chan];
380
381 switch (state->chanType[timeslot]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000382 case NONE:
383 case I:
384 case II:
385 case III:
386 case FILL:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400387 state->fillerModulus[timeslot] = 26;
dburgessb3a0ca42011-10-12 07:44:40 +0000388 break;
389 case IV:
390 case VI:
391 case V:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400392 state->fillerModulus[timeslot] = 51;
dburgessb3a0ca42011-10-12 07:44:40 +0000393 break;
394 //case V:
395 case VII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400396 state->fillerModulus[timeslot] = 102;
dburgessb3a0ca42011-10-12 07:44:40 +0000397 break;
ttsoufc40a842013-06-09 22:38:18 +0000398 case XIII:
Thomas Tsou204a9f12013-10-29 18:34:16 -0400399 state->fillerModulus[timeslot] = 52;
ttsoufc40a842013-06-09 22:38:18 +0000400 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000401 default:
402 break;
403 }
404}
405
406
Thomas Tsou204a9f12013-10-29 18:34:16 -0400407Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
408 size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000409{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400410 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000411 unsigned burstTN = currTime.TN();
412 unsigned burstFN = currTime.FN();
413
Thomas Tsou204a9f12013-10-29 18:34:16 -0400414 switch (state->chanType[burstTN]) {
dburgessb3a0ca42011-10-12 07:44:40 +0000415 case NONE:
416 return OFF;
417 break;
418 case FILL:
419 return IDLE;
420 break;
421 case I:
422 return TSC;
423 /*if (burstFN % 26 == 25)
424 return IDLE;
425 else
426 return TSC;*/
427 break;
428 case II:
ttsou20642972013-03-27 22:00:25 +0000429 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000430 break;
431 case III:
432 return TSC;
433 break;
434 case IV:
435 case VI:
436 return RACH;
437 break;
438 case V: {
439 int mod51 = burstFN % 51;
440 if ((mod51 <= 36) && (mod51 >= 14))
441 return RACH;
442 else if ((mod51 == 4) || (mod51 == 5))
443 return RACH;
444 else if ((mod51 == 45) || (mod51 == 46))
445 return RACH;
446 else
447 return TSC;
448 break;
449 }
450 case VII:
451 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
452 return IDLE;
453 else
454 return TSC;
455 break;
ttsoufc40a842013-06-09 22:38:18 +0000456 case XIII: {
457 int mod52 = burstFN % 52;
458 if ((mod52 == 12) || (mod52 == 38))
459 return RACH;
460 else if ((mod52 == 25) || (mod52 == 51))
461 return IDLE;
462 else
463 return TSC;
464 break;
465 }
dburgessb3a0ca42011-10-12 07:44:40 +0000466 case LOOPBACK:
467 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
468 return IDLE;
469 else
470 return TSC;
471 break;
472 default:
473 return OFF;
474 break;
475 }
dburgessb3a0ca42011-10-12 07:44:40 +0000476}
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400477
Thomas Tsou30421a72013-11-13 23:14:48 -0500478/*
479 * Detect RACH synchronization sequence within a burst. No equalization
480 * is used or available on the RACH channel.
481 */
482bool Transceiver::detectRACH(TransceiverState *state,
483 signalVector &burst,
484 complex &amp, float &toa)
485{
486 float threshold = 6.0;
487
488 return detectRACHBurst(burst, threshold, mSPSRx, &amp, &toa);
489}
490
491/*
492 * Detect normal burst training sequence midamble. Update equalization
493 * state information and channel estimate if necessary. Equalization
494 * is currently disabled.
495 */
Thomas Tsoua0179e32013-11-14 15:52:04 -0500496bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
Thomas Tsou30421a72013-11-13 23:14:48 -0500497 complex &amp, float &toa, GSM::Time &time)
498{
499 int tn = time.TN();
500 float chanOffset, threshold = 5.0;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500501 bool noise, needDFE = false, estimateChan = false;
Thomas Tsou30421a72013-11-13 23:14:48 -0500502 double elapsed = time - state->chanEstimateTime[tn];
503 signalVector *chanResp;
504
505 /* Check equalization update state */
Thomas Tsoufb827d02013-11-16 16:14:12 -0500506 if (needDFE && ((elapsed > 50) || (!state->chanResponse[tn]))) {
Thomas Tsou30421a72013-11-13 23:14:48 -0500507 delete state->DFEForward[tn];
508 delete state->DFEFeedback[tn];
509 state->DFEForward[tn] = NULL;
510 state->DFEFeedback[tn] = NULL;
511
512 estimateChan = true;
513 }
514
515 /* Detect normal burst midambles */
516 if (!analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, &amp,
517 &toa, mMaxExpectedDelay, estimateChan,
518 &chanResp, &chanOffset)) {
519 return false;
520 }
521
Thomas Tsoua0179e32013-11-14 15:52:04 -0500522 noise = state->mNoiseLev;
523 state->SNRestimate[tn] = amp.norm2() / (noise * noise + 1.0);
Thomas Tsou30421a72013-11-13 23:14:48 -0500524
525 /* Set equalizer if unabled */
526 if (needDFE && estimateChan) {
527 state->chanResponse[tn] = chanResp;
528 state->chanRespOffset[tn] = chanOffset;
529 state->chanRespAmplitude[tn] = amp;
530
531 scaleVector(*chanResp, complex(1.0, 0.0) / amp);
532
533 designDFE(*chanResp, state->SNRestimate[tn],
534 7, &state->DFEForward[tn], &state->DFEFeedback[tn]);
535
536 state->chanEstimateTime[tn] = time;
537 }
538
539 return true;;
540}
541
542/*
543 * Demodulate GMSK burst using equalization if requested. Otherwise
544 * demodulate by direct rotation and soft slicing.
545 */
546SoftVector *Transceiver::demodulate(TransceiverState *state,
547 signalVector &burst, complex amp,
548 float toa, size_t tn, bool equalize)
549{
550 if (equalize) {
551 scaleVector(burst, complex(1.0, 0.0) / amp);
552 return equalizeBurst(burst,
553 toa - state->chanRespOffset[tn],
554 mSPSRx,
555 *state->DFEForward[tn],
556 *state->DFEFeedback[tn]);
557 }
558
559 return demodulateBurst(burst, mSPSRx, amp, toa);
560}
561
562/*
563 * Pull bursts from the FIFO and handle according to the slot
564 * and burst correlation type. Equalzation is currently disabled.
565 */
Thomas Tsou204a9f12013-10-29 18:34:16 -0400566SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
567 int &timingOffset, size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000568{
Thomas Tsou30421a72013-11-13 23:14:48 -0500569 bool success, equalize = false;
570 complex amp;
571 float toa, pow, max = -1.0, avg = 0.0;
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500572 int max_i = -1;
Thomas Tsou30421a72013-11-13 23:14:48 -0500573 signalVector *burst;
Thomas Tsouef25dba2013-11-14 15:31:24 -0500574 SoftVector *bits = NULL;
Thomas Tsoua0179e32013-11-14 15:52:04 -0500575 TransceiverState *state = &mStates[chan];
dburgessb3a0ca42011-10-12 07:44:40 +0000576
Thomas Tsou30421a72013-11-13 23:14:48 -0500577 /* Blocking FIFO read */
578 radioVector *radio_burst = mReceiveFIFO[chan]->read();
579 if (!radio_burst)
580 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000581
Thomas Tsou30421a72013-11-13 23:14:48 -0500582 /* Set time and determine correlation type */
583 GSM::Time time = radio_burst->getTime();
584 CorrType type = expectedCorrType(time, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000585
Thomas Tsou30421a72013-11-13 23:14:48 -0500586 if ((type == OFF) || (type == IDLE)) {
587 delete radio_burst;
dburgessb3a0ca42011-10-12 07:44:40 +0000588 return NULL;
589 }
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000590
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500591 /* Select the diversity channel with highest energy */
592 for (size_t i = 0; i < radio_burst->chans(); i++) {
593 energyDetect(*radio_burst->getVector(i), 20 * mSPSRx, 0.0, &pow);
594 if (pow > max) {
595 max = pow;
596 max_i = i;
597 }
598 avg += pow;
599 }
600
601 if (max_i < 0) {
602 LOG(ALERT) << "Received empty burst";
603 delete radio_burst;
604 return NULL;
605 }
606
Thomas Tsou30421a72013-11-13 23:14:48 -0500607 /* Average noise on diversity paths and update global levels */
Thomas Tsoue90a42b2013-11-13 23:38:09 -0500608 burst = radio_burst->getVector(max_i);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500609 avg = sqrt(avg / radio_burst->chans());
Thomas Tsoua0179e32013-11-14 15:52:04 -0500610 state->mNoiseLev = state->mNoises.avg();
Thomas Tsoufa3a7872013-10-17 21:23:34 -0400611
Thomas Tsou30421a72013-11-13 23:14:48 -0500612 /* Detect normal or RACH bursts */
613 if (type == TSC)
Thomas Tsoua0179e32013-11-14 15:52:04 -0500614 success = detectTSC(state, *burst, amp, toa, time);
Thomas Tsou30421a72013-11-13 23:14:48 -0500615 else
Thomas Tsoua0179e32013-11-14 15:52:04 -0500616 success = detectRACH(state, *burst, amp, toa);
Thomas Tsouf0782732013-10-29 15:55:47 -0400617
Thomas Tsou30421a72013-11-13 23:14:48 -0500618 if (!success) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500619 state->mNoises.insert(avg);
Thomas Tsou30421a72013-11-13 23:14:48 -0500620 delete radio_burst;
621 return NULL;
dburgessb3a0ca42011-10-12 07:44:40 +0000622 }
dburgessb3a0ca42011-10-12 07:44:40 +0000623
Thomas Tsou30421a72013-11-13 23:14:48 -0500624 /* Demodulate and set output info */
625 if (equalize && (type != TSC))
626 equalize = false;
dburgessb3a0ca42011-10-12 07:44:40 +0000627
Thomas Tsoua0179e32013-11-14 15:52:04 -0500628 if (avg - state->mNoiseLev > 0.0)
629 bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
Thomas Tsouef25dba2013-11-14 15:31:24 -0500630
Thomas Tsou30421a72013-11-13 23:14:48 -0500631 wTime = time;
632 RSSI = (int) floor(20.0 * log10(rxFullScale / avg));
633 timingOffset = (int) round(toa * 256.0 / mSPSRx);
dburgessb3a0ca42011-10-12 07:44:40 +0000634
Thomas Tsou30421a72013-11-13 23:14:48 -0500635 delete radio_burst;
636
637 return bits;
dburgessb3a0ca42011-10-12 07:44:40 +0000638}
639
dburgessb3a0ca42011-10-12 07:44:40 +0000640void Transceiver::reset()
641{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400642 for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
643 mTxPriorityQueues[i].clear();
dburgessb3a0ca42011-10-12 07:44:40 +0000644}
645
646
Thomas Tsou204a9f12013-10-29 18:34:16 -0400647void Transceiver::driveControl(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000648{
dburgessb3a0ca42011-10-12 07:44:40 +0000649 int MAX_PACKET_LENGTH = 100;
650
651 // check control socket
652 char buffer[MAX_PACKET_LENGTH];
653 int msgLen = -1;
654 buffer[0] = '\0';
Thomas Tsoud647ec52013-10-29 15:17:34 -0400655
Thomas Tsou204a9f12013-10-29 18:34:16 -0400656 msgLen = mCtrlSockets[chan]->read(buffer);
dburgessb3a0ca42011-10-12 07:44:40 +0000657
658 if (msgLen < 1) {
659 return;
660 }
661
662 char cmdcheck[4];
663 char command[MAX_PACKET_LENGTH];
664 char response[MAX_PACKET_LENGTH];
665
666 sscanf(buffer,"%3s %s",cmdcheck,command);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400667
668 if (!chan)
669 writeClockInterface();
dburgessb3a0ca42011-10-12 07:44:40 +0000670
671 if (strcmp(cmdcheck,"CMD")!=0) {
672 LOG(WARNING) << "bogus message on control interface";
673 return;
674 }
675 LOG(INFO) << "command is " << buffer;
676
677 if (strcmp(command,"POWEROFF")==0) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800678 stop();
679 sprintf(response,"RSP POWEROFF 0");
dburgessb3a0ca42011-10-12 07:44:40 +0000680 }
681 else if (strcmp(command,"POWERON")==0) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800682 if (!start())
dburgessb3a0ca42011-10-12 07:44:40 +0000683 sprintf(response,"RSP POWERON 1");
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800684 else
dburgessb3a0ca42011-10-12 07:44:40 +0000685 sprintf(response,"RSP POWERON 0");
dburgessb3a0ca42011-10-12 07:44:40 +0000686 }
687 else if (strcmp(command,"SETMAXDLY")==0) {
688 //set expected maximum time-of-arrival
689 int maxDelay;
690 sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
691 mMaxExpectedDelay = maxDelay; // 1 GSM symbol is approx. 1 km
692 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
693 }
694 else if (strcmp(command,"SETRXGAIN")==0) {
695 //set expected maximum time-of-arrival
696 int newGain;
697 sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400698 newGain = mRadioInterface->setRxGain(newGain, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000699 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
700 }
701 else if (strcmp(command,"NOISELEV")==0) {
702 if (mOn) {
Thomas Tsoua0179e32013-11-14 15:52:04 -0500703 float lev = mStates[chan].mNoiseLev;
dburgessb3a0ca42011-10-12 07:44:40 +0000704 sprintf(response,"RSP NOISELEV 0 %d",
Thomas Tsoua0179e32013-11-14 15:52:04 -0500705 (int) round(20.0 * log10(rxFullScale / lev)));
dburgessb3a0ca42011-10-12 07:44:40 +0000706 }
707 else {
708 sprintf(response,"RSP NOISELEV 1 0");
709 }
Thomas Tsoucb269a32013-11-15 14:15:47 -0500710 }
711 else if (!strcmp(command, "SETPOWER")) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800712 int power;
713 sscanf(buffer, "%3s %s %d", cmdcheck, command, &power);
714 power = mRadioInterface->setPowerAttenuation(power, chan);
715 mStates[chan].mPower = power;
716 sprintf(response, "RSP SETPOWER 0 %d", power);
dburgessb3a0ca42011-10-12 07:44:40 +0000717 }
Thomas Tsoucb269a32013-11-15 14:15:47 -0500718 else if (!strcmp(command,"ADJPOWER")) {
Tom Tsoua4d1a412014-11-25 15:46:56 -0800719 int power, step;
720 sscanf(buffer, "%3s %s %d", cmdcheck, command, &step);
721 power = mStates[chan].mPower + step;
722 power = mRadioInterface->setPowerAttenuation(power, chan);
723 mStates[chan].mPower = power;
724 sprintf(response, "RSP ADJPOWER 0 %d", power);
dburgessb3a0ca42011-10-12 07:44:40 +0000725 }
dburgessb3a0ca42011-10-12 07:44:40 +0000726 else if (strcmp(command,"RXTUNE")==0) {
727 // tune receiver
728 int freqKhz;
729 sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500730 mRxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400731 if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000732 LOG(ALERT) << "RX failed to tune";
733 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
734 }
735 else
736 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
737 }
738 else if (strcmp(command,"TXTUNE")==0) {
739 // tune txmtr
740 int freqKhz;
741 sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
Thomas Tsou477b77c2013-11-15 16:13:59 -0500742 mTxFreq = freqKhz * 1e3;
Thomas Tsou204a9f12013-10-29 18:34:16 -0400743 if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
dburgessb3a0ca42011-10-12 07:44:40 +0000744 LOG(ALERT) << "TX failed to tune";
745 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
746 }
747 else
748 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
749 }
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500750 else if (!strcmp(command,"SETTSC")) {
dburgessb3a0ca42011-10-12 07:44:40 +0000751 // set TSC
Thomas Tsou3f32ab52013-11-15 16:32:54 -0500752 unsigned TSC;
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500753 sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000754 if (mOn)
Thomas Tsoud3fccea2013-11-15 14:22:53 -0500755 sprintf(response, "RSP SETTSC 1 %d", TSC);
756 else if (chan && (TSC != mTSC))
757 sprintf(response, "RSP SETTSC 1 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000758 else {
759 mTSC = TSC;
Thomas Tsouc1f7c422013-10-11 13:49:55 -0400760 generateMidamble(mSPSRx, TSC);
Thomas Tsou83e06892013-08-20 16:10:01 -0400761 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000762 }
763 }
764 else if (strcmp(command,"SETSLOT")==0) {
765 // set TSC
766 int corrCode;
767 int timeslot;
768 sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&timeslot,&corrCode);
769 if ((timeslot < 0) || (timeslot > 7)) {
770 LOG(WARNING) << "bogus message on control interface";
771 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
772 return;
773 }
Thomas Tsou204a9f12013-10-29 18:34:16 -0400774 mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode;
775 setModulus(timeslot, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000776 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
777
778 }
779 else {
780 LOG(WARNING) << "bogus command " << command << " on control interface.";
781 }
782
Thomas Tsou204a9f12013-10-29 18:34:16 -0400783 mCtrlSockets[chan]->write(response, strlen(response) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +0000784}
785
Thomas Tsou204a9f12013-10-29 18:34:16 -0400786bool Transceiver::driveTxPriorityQueue(size_t chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000787{
dburgessb3a0ca42011-10-12 07:44:40 +0000788 char buffer[gSlotLen+50];
789
790 // check data socket
Thomas Tsou204a9f12013-10-29 18:34:16 -0400791 size_t msgLen = mDataSockets[chan]->read(buffer);
dburgessb3a0ca42011-10-12 07:44:40 +0000792
793 if (msgLen!=gSlotLen+1+4+1) {
794 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
795 return false;
796 }
797
798 int timeSlot = (int) buffer[0];
799 uint64_t frameNum = 0;
800 for (int i = 0; i < 4; i++)
801 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
dburgessb3a0ca42011-10-12 07:44:40 +0000802
dburgessb3a0ca42011-10-12 07:44:40 +0000803 // periodically update GSM core clock
804 LOG(DEBUG) << "mTransmitDeadlineClock " << mTransmitDeadlineClock
805 << " mLastClockUpdateTime " << mLastClockUpdateTime;
dburgessb3a0ca42011-10-12 07:44:40 +0000806
Thomas Tsou204a9f12013-10-29 18:34:16 -0400807 if (!chan) {
808 if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
809 writeClockInterface();
810 }
dburgessb3a0ca42011-10-12 07:44:40 +0000811
812 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
813
814 int RSSI = (int) buffer[5];
815 static BitVector newBurst(gSlotLen);
816 BitVector::iterator itr = newBurst.begin();
817 char *bufferItr = buffer+6;
818 while (itr < newBurst.end())
819 *itr++ = *bufferItr++;
820
821 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
Thomas Tsou204a9f12013-10-29 18:34:16 -0400822
823 addRadioVector(chan, newBurst, RSSI, currTime);
dburgessb3a0ca42011-10-12 07:44:40 +0000824
825 return true;
826
827
828}
dburgessb3a0ca42011-10-12 07:44:40 +0000829
Thomas Tsou204a9f12013-10-29 18:34:16 -0400830void Transceiver::driveReceiveRadio()
831{
832 if (!mRadioInterface->driveReceiveRadio())
833 usleep(100000);
834}
835
836void Transceiver::driveReceiveFIFO(size_t chan)
837{
dburgessb3a0ca42011-10-12 07:44:40 +0000838 SoftVector *rxBurst = NULL;
839 int RSSI;
840 int TOA; // in 1/256 of a symbol
841 GSM::Time burstTime;
842
Thomas Tsou204a9f12013-10-29 18:34:16 -0400843 rxBurst = pullRadioVector(burstTime, RSSI, TOA, chan);
dburgessb3a0ca42011-10-12 07:44:40 +0000844
845 if (rxBurst) {
846
847 LOG(DEBUG) << "burst parameters: "
848 << " time: " << burstTime
849 << " RSSI: " << RSSI
850 << " TOA: " << TOA
851 << " bits: " << *rxBurst;
852
853 char burstString[gSlotLen+10];
854 burstString[0] = burstTime.TN();
855 for (int i = 0; i < 4; i++)
856 burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
857 burstString[5] = RSSI;
858 burstString[6] = (TOA >> 8) & 0x0ff;
859 burstString[7] = TOA & 0x0ff;
860 SoftVector::iterator burstItr = rxBurst->begin();
861
862 for (unsigned int i = 0; i < gSlotLen; i++) {
863 burstString[8+i] =(char) round((*burstItr++)*255.0);
864 }
865 burstString[gSlotLen+9] = '\0';
866 delete rxBurst;
867
Thomas Tsou204a9f12013-10-29 18:34:16 -0400868 mDataSockets[chan]->write(burstString,gSlotLen+10);
dburgessb3a0ca42011-10-12 07:44:40 +0000869 }
dburgessb3a0ca42011-10-12 07:44:40 +0000870}
871
Thomas Tsou204a9f12013-10-29 18:34:16 -0400872void Transceiver::driveTxFIFO()
dburgessb3a0ca42011-10-12 07:44:40 +0000873{
874
875 /**
876 Features a carefully controlled latency mechanism, to
877 assure that transmit packets arrive at the radio/USRP
878 before they need to be transmitted.
879
880 Deadline clock indicates the burst that needs to be
881 pushed into the FIFO right NOW. If transmit queue does
882 not have a burst, stick in filler data.
883 */
884
885
886 RadioClock *radioClock = (mRadioInterface->getClock());
887
888 if (mOn) {
889 //radioClock->wait(); // wait until clock updates
890 LOG(DEBUG) << "radio clock " << radioClock->get();
891 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
892 // if underrun, then we're not providing bursts to radio/USRP fast
893 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -0400894 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000895 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +0000896 // only update latency at the defined frame interval
897 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000898 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
899 LOG(INFO) << "new latency: " << mTransmitLatency;
900 mLatencyUpdateTime = radioClock->get();
901 }
902 }
903 else {
904 // if underrun hasn't occurred in the last sec (216 frames) drop
905 // transmit latency by a timeslot
ttsou2173abf2012-08-08 00:51:31 +0000906 if (mTransmitLatency > GSM::Time(USB_LATENCY_MIN)) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000907 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
908 mTransmitLatency.decTN();
909 LOG(INFO) << "reduced latency: " << mTransmitLatency;
910 mLatencyUpdateTime = radioClock->get();
911 }
912 }
913 }
dburgessb3a0ca42011-10-12 07:44:40 +0000914 }
dburgessb3a0ca42011-10-12 07:44:40 +0000915 // time to push burst to transmit FIFO
916 pushRadioVector(mTransmitDeadlineClock);
917 mTransmitDeadlineClock.incTN();
918 }
dburgessb3a0ca42011-10-12 07:44:40 +0000919 }
Thomas Tsou92c16df2013-09-28 18:04:19 -0400920
921 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +0000922}
923
924
925
926void Transceiver::writeClockInterface()
927{
928 char command[50];
929 // FIXME -- This should be adaptive.
930 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
931
932 LOG(INFO) << "ClockInterface: sending " << command;
933
Tom Tsoueb54bdd2014-11-25 16:06:32 -0800934 mClockSocket.write(command, strlen(command) + 1);
dburgessb3a0ca42011-10-12 07:44:40 +0000935
936 mLastClockUpdateTime = mTransmitDeadlineClock;
937
Thomas Tsou92c16df2013-09-28 18:04:19 -0400938}
dburgessb3a0ca42011-10-12 07:44:40 +0000939
Thomas Tsou204a9f12013-10-29 18:34:16 -0400940void *RxUpperLoopAdapter(TransceiverChannel *chan)
941{
942 Transceiver *trx = chan->trx;
943 size_t num = chan->num;
944
945 delete chan;
946
Thomas Tsou7553aa92013-11-08 12:50:03 -0500947 trx->setPriority(0.42);
948
Thomas Tsou204a9f12013-10-29 18:34:16 -0400949 while (1) {
950 trx->driveReceiveFIFO(num);
951 pthread_testcancel();
952 }
953 return NULL;
954}
955
956void *RxLowerLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +0000957{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500958 transceiver->setPriority(0.45);
kurtis.heimerl6b495a52011-11-26 03:17:21 +0000959
dburgessb3a0ca42011-10-12 07:44:40 +0000960 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400961 transceiver->driveReceiveRadio();
Thomas Tsou92c16df2013-09-28 18:04:19 -0400962 pthread_testcancel();
963 }
964 return NULL;
965}
966
Thomas Tsou204a9f12013-10-29 18:34:16 -0400967void *TxLowerLoopAdapter(Transceiver *transceiver)
Thomas Tsou92c16df2013-09-28 18:04:19 -0400968{
Thomas Tsou7553aa92013-11-08 12:50:03 -0500969 transceiver->setPriority(0.44);
970
Thomas Tsou92c16df2013-09-28 18:04:19 -0400971 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400972 transceiver->driveTxFIFO();
dburgessb3a0ca42011-10-12 07:44:40 +0000973 pthread_testcancel();
974 }
975 return NULL;
976}
977
Thomas Tsou204a9f12013-10-29 18:34:16 -0400978void *ControlServiceLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000979{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400980 Transceiver *trx = chan->trx;
981 size_t num = chan->num;
982
983 delete chan;
984
dburgessb3a0ca42011-10-12 07:44:40 +0000985 while (1) {
Thomas Tsou204a9f12013-10-29 18:34:16 -0400986 trx->driveControl(num);
dburgessb3a0ca42011-10-12 07:44:40 +0000987 pthread_testcancel();
988 }
989 return NULL;
990}
991
Thomas Tsou204a9f12013-10-29 18:34:16 -0400992void *TxUpperLoopAdapter(TransceiverChannel *chan)
dburgessb3a0ca42011-10-12 07:44:40 +0000993{
Thomas Tsou204a9f12013-10-29 18:34:16 -0400994 Transceiver *trx = chan->trx;
995 size_t num = chan->num;
996
997 delete chan;
998
Thomas Tsoua4cf48c2013-11-09 21:44:26 -0500999 trx->setPriority(0.40);
1000
dburgessb3a0ca42011-10-12 07:44:40 +00001001 while (1) {
Tom Tsoueb54bdd2014-11-25 16:06:32 -08001002 trx->driveTxPriorityQueue(num);
dburgessb3a0ca42011-10-12 07:44:40 +00001003 pthread_testcancel();
1004 }
1005 return NULL;
1006}