blob: 03aa3c165cb9a25359c3af7571e0d01a6958409b [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
24
25/*
26 Compilation switches
27 TRANSMIT_LOGGING write every burst on the given slot to a log
28*/
29
30
31#include <stdio.h>
32#include "Transceiver.h"
33#include <Logger.h>
34
ttsou2173abf2012-08-08 00:51:31 +000035#ifdef HAVE_CONFIG_H
36#include "config.h"
37#endif
dburgessb3a0ca42011-10-12 07:44:40 +000038
Alexander Chemerisd734e2d2013-06-16 14:30:58 +040039using namespace GSM;
40
kurtis.heimerlec842de2012-11-23 08:37:32 +000041#define USB_LATENCY_INTRVL 10,0
ttsou2173abf2012-08-08 00:51:31 +000042
43#if USE_UHD
kurtis.heimerlec842de2012-11-23 08:37:32 +000044# define USB_LATENCY_MIN 6,7
ttsou2173abf2012-08-08 00:51:31 +000045#else
kurtis.heimerlec842de2012-11-23 08:37:32 +000046# define USB_LATENCY_MIN 1,1
ttsou2173abf2012-08-08 00:51:31 +000047#endif
dburgessb3a0ca42011-10-12 07:44:40 +000048
ttsoue8dde022012-12-06 15:43:55 +000049#define INIT_ENERGY_THRSHD 5.0f
50
dburgessb3a0ca42011-10-12 07:44:40 +000051Transceiver::Transceiver(int wBasePort,
52 const char *TRXAddress,
Thomas Tsoud24cc2c2013-08-20 15:41:45 -040053 int wSPS,
dburgessb3a0ca42011-10-12 07:44:40 +000054 GSM::Time wTransmitLatency,
55 RadioInterface *wRadioInterface)
56 :mDataSocket(wBasePort+2,TRXAddress,wBasePort+102),
57 mControlSocket(wBasePort+1,TRXAddress,wBasePort+101),
58 mClockSocket(wBasePort,TRXAddress,wBasePort+100)
59{
dburgessb3a0ca42011-10-12 07:44:40 +000060 GSM::Time startTime(random() % gHyperframe,0);
61
Thomas Tsou92c16df2013-09-28 18:04:19 -040062 mRxServiceLoopThread = new Thread(32768);
63 mTxServiceLoopThread = new Thread(32768);
dburgessb3a0ca42011-10-12 07:44:40 +000064 mControlServiceLoopThread = new Thread(32768); ///< thread to process control messages from GSM core
65 mTransmitPriorityQueueServiceLoopThread = new Thread(32768);///< thread to process transmit bursts from GSM core
66
Thomas Tsoud24cc2c2013-08-20 15:41:45 -040067 mSPS = wSPS;
dburgessb3a0ca42011-10-12 07:44:40 +000068 mRadioInterface = wRadioInterface;
69 mTransmitLatency = wTransmitLatency;
70 mTransmitDeadlineClock = startTime;
71 mLastClockUpdateTime = startTime;
72 mLatencyUpdateTime = startTime;
73 mRadioInterface->getClock()->set(startTime);
74 mMaxExpectedDelay = 0;
75
dburgessb3a0ca42011-10-12 07:44:40 +000076 txFullScale = mRadioInterface->fullScaleInputValue();
77 rxFullScale = mRadioInterface->fullScaleOutputValue();
78
dburgessb3a0ca42011-10-12 07:44:40 +000079 mOn = false;
80 mTxFreq = 0.0;
81 mRxFreq = 0.0;
82 mPower = -10;
ttsoue8dde022012-12-06 15:43:55 +000083 mEnergyThreshold = INIT_ENERGY_THRSHD;
dburgessb3a0ca42011-10-12 07:44:40 +000084 prevFalseDetectionTime = startTime;
Thomas Tsou83e06892013-08-20 16:10:01 -040085
dburgessb3a0ca42011-10-12 07:44:40 +000086}
87
88Transceiver::~Transceiver()
89{
dburgessb3a0ca42011-10-12 07:44:40 +000090 sigProcLibDestroy();
91 mTransmitPriorityQueue.clear();
92}
Thomas Tsou83e06892013-08-20 16:10:01 -040093
94bool Transceiver::init()
95{
96 if (!sigProcLibSetup(mSPS)) {
97 LOG(ALERT) << "Failed to initialize signal processing library";
98 return false;
99 }
100
101 // initialize filler tables with dummy bursts
102 for (int i = 0; i < 8; i++) {
103 signalVector* modBurst = modulateBurst(gDummyBurst,
104 8 + (i % 4 == 0),
105 mSPS);
106 if (!modBurst) {
107 sigProcLibDestroy();
108 LOG(ALERT) << "Failed to initialize filler table";
109 return false;
110 }
111
112 scaleVector(*modBurst,txFullScale);
113 fillerModulus[i]=26;
114 for (int j = 0; j < 102; j++) {
115 fillerTable[j][i] = new signalVector(*modBurst);
116 }
117
118 delete modBurst;
119 mChanType[i] = NONE;
120 channelResponse[i] = NULL;
121 DFEForward[i] = NULL;
122 DFEFeedback[i] = NULL;
123 channelEstimateTime[i] = mTransmitDeadlineClock;
124 }
125
126 return true;
127}
dburgessb3a0ca42011-10-12 07:44:40 +0000128
129void Transceiver::addRadioVector(BitVector &burst,
130 int RSSI,
131 GSM::Time &wTime)
132{
133 // modulate and stick into queue
Thomas Tsou83e06892013-08-20 16:10:01 -0400134 signalVector* modBurst = modulateBurst(burst,
dburgessb3a0ca42011-10-12 07:44:40 +0000135 8 + (wTime.TN() % 4 == 0),
Thomas Tsoud24cc2c2013-08-20 15:41:45 -0400136 mSPS);
dburgessb3a0ca42011-10-12 07:44:40 +0000137 scaleVector(*modBurst,txFullScale * pow(10,-RSSI/10));
138 radioVector *newVec = new radioVector(*modBurst,wTime);
139 mTransmitPriorityQueue.write(newVec);
140
141 delete modBurst;
142}
143
144#ifdef TRANSMIT_LOGGING
145void Transceiver::unModulateVector(signalVector wVector)
146{
Thomas Tsou83e06892013-08-20 16:10:01 -0400147 SoftVector *burst = demodulateBurst(wVector, mSPS, 1.0, 0.0);
dburgessb3a0ca42011-10-12 07:44:40 +0000148 LOG(DEBUG) << "LOGGED BURST: " << *burst;
149
150/*
151 unsigned char burstStr[gSlotLen+1];
152 SoftVector::iterator burstItr = burst->begin();
153 for (int i = 0; i < gSlotLen; i++) {
154 // FIXME: Demod bits are inverted!
155 burstStr[i] = (unsigned char) ((*burstItr++)*255.0);
156 }
157 burstStr[gSlotLen]='\0';
158 LOG(DEBUG) << "LOGGED BURST: " << burstStr;
159*/
160 delete burst;
161}
162#endif
163
164void Transceiver::pushRadioVector(GSM::Time &nowTime)
165{
166
167 // dump stale bursts, if any
168 while (radioVector* staleBurst = mTransmitPriorityQueue.getStaleBurst(nowTime)) {
169 // Even if the burst is stale, put it in the fillter table.
170 // (It might be an idle pattern.)
171 LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
kurtis.heimerl06286132011-11-26 03:18:43 +0000172 const GSM::Time& nextTime = staleBurst->getTime();
dburgessb3a0ca42011-10-12 07:44:40 +0000173 int TN = nextTime.TN();
174 int modFN = nextTime.FN() % fillerModulus[TN];
175 delete fillerTable[modFN][TN];
176 fillerTable[modFN][TN] = staleBurst;
177 }
178
179 int TN = nowTime.TN();
180 int modFN = nowTime.FN() % fillerModulus[nowTime.TN()];
181
182 // if queue contains data at the desired timestamp, stick it into FIFO
183 if (radioVector *next = (radioVector*) mTransmitPriorityQueue.getCurrentBurst(nowTime)) {
184 LOG(DEBUG) << "transmitFIFO: wrote burst " << next << " at time: " << nowTime;
185 delete fillerTable[modFN][TN];
186 fillerTable[modFN][TN] = new signalVector(*(next));
187 mRadioInterface->driveTransmitRadio(*(next),(mChanType[TN]==NONE)); //fillerTable[modFN][TN]));
188 delete next;
189#ifdef TRANSMIT_LOGGING
190 if (nowTime.TN()==TRANSMIT_LOGGING) {
191 unModulateVector(*(fillerTable[modFN][TN]));
192 }
193#endif
194 return;
195 }
196
197 // otherwise, pull filler data, and push to radio FIFO
198 mRadioInterface->driveTransmitRadio(*(fillerTable[modFN][TN]),(mChanType[TN]==NONE));
199#ifdef TRANSMIT_LOGGING
200 if (nowTime.TN()==TRANSMIT_LOGGING)
201 unModulateVector(*fillerTable[modFN][TN]);
202#endif
203
204}
205
206void Transceiver::setModulus(int timeslot)
207{
208 switch (mChanType[timeslot]) {
209 case NONE:
210 case I:
211 case II:
212 case III:
213 case FILL:
214 fillerModulus[timeslot] = 26;
215 break;
216 case IV:
217 case VI:
218 case V:
219 fillerModulus[timeslot] = 51;
220 break;
221 //case V:
222 case VII:
223 fillerModulus[timeslot] = 102;
224 break;
ttsoufc40a842013-06-09 22:38:18 +0000225 case XIII:
226 fillerModulus[timeslot] = 52;
227 break;
dburgessb3a0ca42011-10-12 07:44:40 +0000228 default:
229 break;
230 }
231}
232
233
234Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime)
235{
236
237 unsigned burstTN = currTime.TN();
238 unsigned burstFN = currTime.FN();
239
240 switch (mChanType[burstTN]) {
241 case NONE:
242 return OFF;
243 break;
244 case FILL:
245 return IDLE;
246 break;
247 case I:
248 return TSC;
249 /*if (burstFN % 26 == 25)
250 return IDLE;
251 else
252 return TSC;*/
253 break;
254 case II:
ttsou20642972013-03-27 22:00:25 +0000255 return TSC;
dburgessb3a0ca42011-10-12 07:44:40 +0000256 break;
257 case III:
258 return TSC;
259 break;
260 case IV:
261 case VI:
262 return RACH;
263 break;
264 case V: {
265 int mod51 = burstFN % 51;
266 if ((mod51 <= 36) && (mod51 >= 14))
267 return RACH;
268 else if ((mod51 == 4) || (mod51 == 5))
269 return RACH;
270 else if ((mod51 == 45) || (mod51 == 46))
271 return RACH;
272 else
273 return TSC;
274 break;
275 }
276 case VII:
277 if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
278 return IDLE;
279 else
280 return TSC;
281 break;
ttsoufc40a842013-06-09 22:38:18 +0000282 case XIII: {
283 int mod52 = burstFN % 52;
284 if ((mod52 == 12) || (mod52 == 38))
285 return RACH;
286 else if ((mod52 == 25) || (mod52 == 51))
287 return IDLE;
288 else
289 return TSC;
290 break;
291 }
dburgessb3a0ca42011-10-12 07:44:40 +0000292 case LOOPBACK:
293 if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
294 return IDLE;
295 else
296 return TSC;
297 break;
298 default:
299 return OFF;
300 break;
301 }
302
303}
304
305SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
306 int &RSSI,
307 int &timingOffset)
308{
Thomas Tsoud5a80c32013-09-19 11:24:14 -0400309 bool needDFE = false;
dburgessb3a0ca42011-10-12 07:44:40 +0000310
311 radioVector *rxBurst = (radioVector *) mReceiveFIFO->get();
312
313 if (!rxBurst) return NULL;
314
kurtis.heimerl06286132011-11-26 03:18:43 +0000315 LOG(DEBUG) << "receiveFIFO: read radio vector at time: " << rxBurst->getTime() << ", new size: " << mReceiveFIFO->size();
dburgessb3a0ca42011-10-12 07:44:40 +0000316
kurtis.heimerl06286132011-11-26 03:18:43 +0000317 int timeslot = rxBurst->getTime().TN();
dburgessb3a0ca42011-10-12 07:44:40 +0000318
kurtis.heimerl06286132011-11-26 03:18:43 +0000319 CorrType corrType = expectedCorrType(rxBurst->getTime());
dburgessb3a0ca42011-10-12 07:44:40 +0000320
321 if ((corrType==OFF) || (corrType==IDLE)) {
322 delete rxBurst;
323 return NULL;
324 }
325
326 // check to see if received burst has sufficient
327 signalVector *vectorBurst = rxBurst;
328 complex amplitude = 0.0;
329 float TOA = 0.0;
330 float avgPwr = 0.0;
Thomas Tsou8181b012013-08-20 21:17:19 -0400331#ifdef ENERGY_DETECT
Thomas Tsoud24cc2c2013-08-20 15:41:45 -0400332 if (!energyDetect(*vectorBurst, 20 * mSPS, mEnergyThreshold, &avgPwr)) {
kurtis.heimerl06286132011-11-26 03:18:43 +0000333 LOG(DEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->getTime();
334 double framesElapsed = rxBurst->getTime()-prevFalseDetectionTime;
dburgessb3a0ca42011-10-12 07:44:40 +0000335 if (framesElapsed > 50) { // if we haven't had any false detections for a while, lower threshold
336 mEnergyThreshold -= 10.0/10.0;
kurtis.heimerl3ed6fb72011-11-26 03:17:52 +0000337 if (mEnergyThreshold < 0.0)
338 mEnergyThreshold = 0.0;
339
kurtis.heimerl06286132011-11-26 03:18:43 +0000340 prevFalseDetectionTime = rxBurst->getTime();
dburgessb3a0ca42011-10-12 07:44:40 +0000341 }
342 delete rxBurst;
343 return NULL;
344 }
kurtis.heimerl06286132011-11-26 03:18:43 +0000345 LOG(DEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->getTime();
Thomas Tsou8181b012013-08-20 21:17:19 -0400346#endif
dburgessb3a0ca42011-10-12 07:44:40 +0000347 // run the proper correlator
348 bool success = false;
349 if (corrType==TSC) {
kurtis.heimerl06286132011-11-26 03:18:43 +0000350 LOG(DEBUG) << "looking for TSC at time: " << rxBurst->getTime();
dburgessb3a0ca42011-10-12 07:44:40 +0000351 signalVector *channelResp;
kurtis.heimerl06286132011-11-26 03:18:43 +0000352 double framesElapsed = rxBurst->getTime()-channelEstimateTime[timeslot];
dburgessb3a0ca42011-10-12 07:44:40 +0000353 bool estimateChannel = false;
354 if ((framesElapsed > 50) || (channelResponse[timeslot]==NULL)) {
355 if (channelResponse[timeslot]) delete channelResponse[timeslot];
356 if (DFEForward[timeslot]) delete DFEForward[timeslot];
357 if (DFEFeedback[timeslot]) delete DFEFeedback[timeslot];
358 channelResponse[timeslot] = NULL;
359 DFEForward[timeslot] = NULL;
360 DFEFeedback[timeslot] = NULL;
361 estimateChannel = true;
362 }
363 if (!needDFE) estimateChannel = false;
364 float chanOffset;
365 success = analyzeTrafficBurst(*vectorBurst,
366 mTSC,
Thomas Tsou865bca42013-08-21 20:58:00 -0400367 5.0,
Thomas Tsoud24cc2c2013-08-20 15:41:45 -0400368 mSPS,
dburgessb3a0ca42011-10-12 07:44:40 +0000369 &amplitude,
370 &TOA,
371 mMaxExpectedDelay,
372 estimateChannel,
373 &channelResp,
374 &chanOffset);
375 if (success) {
376 LOG(DEBUG) << "FOUND TSC!!!!!! " << amplitude << " " << TOA;
377 mEnergyThreshold -= 1.0F/10.0F;
378 if (mEnergyThreshold < 0.0) mEnergyThreshold = 0.0;
379 SNRestimate[timeslot] = amplitude.norm2()/(mEnergyThreshold*mEnergyThreshold+1.0); // this is not highly accurate
380 if (estimateChannel) {
381 LOG(DEBUG) << "estimating channel...";
382 channelResponse[timeslot] = channelResp;
383 chanRespOffset[timeslot] = chanOffset;
384 chanRespAmplitude[timeslot] = amplitude;
385 scaleVector(*channelResp, complex(1.0,0.0)/amplitude);
386 designDFE(*channelResp, SNRestimate[timeslot], 7, &DFEForward[timeslot], &DFEFeedback[timeslot]);
kurtis.heimerl06286132011-11-26 03:18:43 +0000387 channelEstimateTime[timeslot] = rxBurst->getTime();
dburgessb3a0ca42011-10-12 07:44:40 +0000388 LOG(DEBUG) << "SNR: " << SNRestimate[timeslot] << ", DFE forward: " << *DFEForward[timeslot] << ", DFE backward: " << *DFEFeedback[timeslot];
389 }
390 }
391 else {
kurtis.heimerl06286132011-11-26 03:18:43 +0000392 double framesElapsed = rxBurst->getTime()-prevFalseDetectionTime;
393 LOG(DEBUG) << "wTime: " << rxBurst->getTime() << ", pTime: " << prevFalseDetectionTime << ", fElapsed: " << framesElapsed;
dburgessb3a0ca42011-10-12 07:44:40 +0000394 mEnergyThreshold += 10.0F/10.0F*exp(-framesElapsed);
kurtis.heimerl06286132011-11-26 03:18:43 +0000395 prevFalseDetectionTime = rxBurst->getTime();
dburgessb3a0ca42011-10-12 07:44:40 +0000396 channelResponse[timeslot] = NULL;
397 }
398 }
399 else {
400 // RACH burst
401 success = detectRACHBurst(*vectorBurst,
Thomas Tsou865bca42013-08-21 20:58:00 -0400402 6.0,
Thomas Tsoud24cc2c2013-08-20 15:41:45 -0400403 mSPS,
dburgessb3a0ca42011-10-12 07:44:40 +0000404 &amplitude,
405 &TOA);
406 if (success) {
407 LOG(DEBUG) << "FOUND RACH!!!!!! " << amplitude << " " << TOA;
408 mEnergyThreshold -= (1.0F/10.0F);
409 if (mEnergyThreshold < 0.0) mEnergyThreshold = 0.0;
410 channelResponse[timeslot] = NULL;
411 }
412 else {
kurtis.heimerl06286132011-11-26 03:18:43 +0000413 double framesElapsed = rxBurst->getTime()-prevFalseDetectionTime;
dburgessb3a0ca42011-10-12 07:44:40 +0000414 mEnergyThreshold += (1.0F/10.0F)*exp(-framesElapsed);
kurtis.heimerl06286132011-11-26 03:18:43 +0000415 prevFalseDetectionTime = rxBurst->getTime();
dburgessb3a0ca42011-10-12 07:44:40 +0000416 }
417 }
418 LOG(DEBUG) << "energy Threshold = " << mEnergyThreshold;
419
420 // demodulate burst
421 SoftVector *burst = NULL;
422 if ((rxBurst) && (success)) {
423 if ((corrType==RACH) || (!needDFE)) {
424 burst = demodulateBurst(*vectorBurst,
Thomas Tsoud24cc2c2013-08-20 15:41:45 -0400425 mSPS,
dburgessb3a0ca42011-10-12 07:44:40 +0000426 amplitude,TOA);
427 }
428 else { // TSC
429 scaleVector(*vectorBurst,complex(1.0,0.0)/amplitude);
430 burst = equalizeBurst(*vectorBurst,
431 TOA-chanRespOffset[timeslot],
Thomas Tsoud24cc2c2013-08-20 15:41:45 -0400432 mSPS,
dburgessb3a0ca42011-10-12 07:44:40 +0000433 *DFEForward[timeslot],
434 *DFEFeedback[timeslot]);
435 }
kurtis.heimerl06286132011-11-26 03:18:43 +0000436 wTime = rxBurst->getTime();
dburgessb3a0ca42011-10-12 07:44:40 +0000437 RSSI = (int) floor(20.0*log10(rxFullScale/amplitude.abs()));
438 LOG(DEBUG) << "RSSI: " << RSSI;
Thomas Tsoud24cc2c2013-08-20 15:41:45 -0400439 timingOffset = (int) round(TOA * 256.0 / mSPS);
dburgessb3a0ca42011-10-12 07:44:40 +0000440 }
441
442 //if (burst) LOG(DEBUG) << "burst: " << *burst << '\n';
443
444 delete rxBurst;
445
446 return burst;
447}
448
449void Transceiver::start()
450{
451 mControlServiceLoopThread->start((void * (*)(void*))ControlServiceLoopAdapter,(void*) this);
452}
453
454void Transceiver::reset()
455{
456 mTransmitPriorityQueue.clear();
457 //mTransmitFIFO->clear();
458 //mReceiveFIFO->clear();
459}
460
461
462void Transceiver::driveControl()
463{
464
465 int MAX_PACKET_LENGTH = 100;
466
467 // check control socket
468 char buffer[MAX_PACKET_LENGTH];
469 int msgLen = -1;
470 buffer[0] = '\0';
471
472 msgLen = mControlSocket.read(buffer);
473
474 if (msgLen < 1) {
475 return;
476 }
477
478 char cmdcheck[4];
479 char command[MAX_PACKET_LENGTH];
480 char response[MAX_PACKET_LENGTH];
481
482 sscanf(buffer,"%3s %s",cmdcheck,command);
483
484 writeClockInterface();
485
486 if (strcmp(cmdcheck,"CMD")!=0) {
487 LOG(WARNING) << "bogus message on control interface";
488 return;
489 }
490 LOG(INFO) << "command is " << buffer;
491
492 if (strcmp(command,"POWEROFF")==0) {
493 // turn off transmitter/demod
494 sprintf(response,"RSP POWEROFF 0");
495 }
496 else if (strcmp(command,"POWERON")==0) {
497 // turn on transmitter/demod
498 if (!mTxFreq || !mRxFreq)
499 sprintf(response,"RSP POWERON 1");
500 else {
501 sprintf(response,"RSP POWERON 0");
502 if (!mOn) {
503 // Prepare for thread start
504 mPower = -20;
505 mRadioInterface->start();
dburgessb3a0ca42011-10-12 07:44:40 +0000506
507 // Start radio interface threads.
Thomas Tsou92c16df2013-09-28 18:04:19 -0400508 mTxServiceLoopThread->start((void * (*)(void*))TxServiceLoopAdapter,(void*) this);
509 mRxServiceLoopThread->start((void * (*)(void*))RxServiceLoopAdapter,(void*) this);
dburgessb3a0ca42011-10-12 07:44:40 +0000510 mTransmitPriorityQueueServiceLoopThread->start((void * (*)(void*))TransmitPriorityQueueServiceLoopAdapter,(void*) this);
511 writeClockInterface();
512
513 mOn = true;
514 }
515 }
516 }
517 else if (strcmp(command,"SETMAXDLY")==0) {
518 //set expected maximum time-of-arrival
519 int maxDelay;
520 sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
521 mMaxExpectedDelay = maxDelay; // 1 GSM symbol is approx. 1 km
522 sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
523 }
524 else if (strcmp(command,"SETRXGAIN")==0) {
525 //set expected maximum time-of-arrival
526 int newGain;
527 sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain);
528 newGain = mRadioInterface->setRxGain(newGain);
ttsoue8dde022012-12-06 15:43:55 +0000529 mEnergyThreshold = INIT_ENERGY_THRSHD;
dburgessb3a0ca42011-10-12 07:44:40 +0000530 sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
531 }
532 else if (strcmp(command,"NOISELEV")==0) {
533 if (mOn) {
534 sprintf(response,"RSP NOISELEV 0 %d",
535 (int) round(20.0*log10(rxFullScale/mEnergyThreshold)));
536 }
537 else {
538 sprintf(response,"RSP NOISELEV 1 0");
539 }
540 }
541 else if (strcmp(command,"SETPOWER")==0) {
542 // set output power in dB
543 int dbPwr;
544 sscanf(buffer,"%3s %s %d",cmdcheck,command,&dbPwr);
545 if (!mOn)
546 sprintf(response,"RSP SETPOWER 1 %d",dbPwr);
547 else {
548 mPower = dbPwr;
kurtis.heimerl58d6a012011-11-26 03:17:38 +0000549 mRadioInterface->setPowerAttenuation(dbPwr);
dburgessb3a0ca42011-10-12 07:44:40 +0000550 sprintf(response,"RSP SETPOWER 0 %d",dbPwr);
551 }
552 }
553 else if (strcmp(command,"ADJPOWER")==0) {
554 // adjust power in dB steps
555 int dbStep;
556 sscanf(buffer,"%3s %s %d",cmdcheck,command,&dbStep);
557 if (!mOn)
558 sprintf(response,"RSP ADJPOWER 1 %d",mPower);
559 else {
560 mPower += dbStep;
561 sprintf(response,"RSP ADJPOWER 0 %d",mPower);
562 }
563 }
564#define FREQOFFSET 0//11.2e3
565 else if (strcmp(command,"RXTUNE")==0) {
566 // tune receiver
567 int freqKhz;
568 sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
569 mRxFreq = freqKhz*1.0e3+FREQOFFSET;
570 if (!mRadioInterface->tuneRx(mRxFreq)) {
571 LOG(ALERT) << "RX failed to tune";
572 sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
573 }
574 else
575 sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
576 }
577 else if (strcmp(command,"TXTUNE")==0) {
578 // tune txmtr
579 int freqKhz;
580 sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
581 //freqKhz = 890e3;
582 mTxFreq = freqKhz*1.0e3+FREQOFFSET;
583 if (!mRadioInterface->tuneTx(mTxFreq)) {
584 LOG(ALERT) << "TX failed to tune";
585 sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
586 }
587 else
588 sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
589 }
590 else if (strcmp(command,"SETTSC")==0) {
591 // set TSC
592 int TSC;
593 sscanf(buffer,"%3s %s %d",cmdcheck,command,&TSC);
594 if (mOn)
595 sprintf(response,"RSP SETTSC 1 %d",TSC);
596 else {
597 mTSC = TSC;
Thomas Tsou83e06892013-08-20 16:10:01 -0400598 generateMidamble(mSPS, TSC);
599 sprintf(response,"RSP SETTSC 0 %d", TSC);
dburgessb3a0ca42011-10-12 07:44:40 +0000600 }
601 }
602 else if (strcmp(command,"SETSLOT")==0) {
603 // set TSC
604 int corrCode;
605 int timeslot;
606 sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&timeslot,&corrCode);
607 if ((timeslot < 0) || (timeslot > 7)) {
608 LOG(WARNING) << "bogus message on control interface";
609 sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
610 return;
611 }
612 mChanType[timeslot] = (ChannelCombination) corrCode;
613 setModulus(timeslot);
614 sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
615
616 }
617 else {
618 LOG(WARNING) << "bogus command " << command << " on control interface.";
619 }
620
621 mControlSocket.write(response,strlen(response)+1);
622
623}
624
625bool Transceiver::driveTransmitPriorityQueue()
626{
627
628 char buffer[gSlotLen+50];
629
630 // check data socket
631 size_t msgLen = mDataSocket.read(buffer);
632
633 if (msgLen!=gSlotLen+1+4+1) {
634 LOG(ERR) << "badly formatted packet on GSM->TRX interface";
635 return false;
636 }
637
638 int timeSlot = (int) buffer[0];
639 uint64_t frameNum = 0;
640 for (int i = 0; i < 4; i++)
641 frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
642
643 /*
644 if (GSM::Time(frameNum,timeSlot) > mTransmitDeadlineClock + GSM::Time(51,0)) {
645 // stale burst
646 //LOG(DEBUG) << "FAST! "<< GSM::Time(frameNum,timeSlot);
647 //writeClockInterface();
648 }*/
649
650/*
651 DAB -- Just let these go through the demod.
652 if (GSM::Time(frameNum,timeSlot) < mTransmitDeadlineClock) {
653 // stale burst from GSM core
654 LOG(NOTICE) << "STALE packet on GSM->TRX interface at time "<< GSM::Time(frameNum,timeSlot);
655 return false;
656 }
657*/
658
659 // periodically update GSM core clock
660 LOG(DEBUG) << "mTransmitDeadlineClock " << mTransmitDeadlineClock
661 << " mLastClockUpdateTime " << mLastClockUpdateTime;
662 if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
663 writeClockInterface();
664
665
666 LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
667
668 int RSSI = (int) buffer[5];
669 static BitVector newBurst(gSlotLen);
670 BitVector::iterator itr = newBurst.begin();
671 char *bufferItr = buffer+6;
672 while (itr < newBurst.end())
673 *itr++ = *bufferItr++;
674
675 GSM::Time currTime = GSM::Time(frameNum,timeSlot);
676
677 addRadioVector(newBurst,RSSI,currTime);
678
679 LOG(DEBUG) "added burst - time: " << currTime << ", RSSI: " << RSSI; // << ", data: " << newBurst;
680
681 return true;
682
683
684}
685
686void Transceiver::driveReceiveFIFO()
687{
688
689 SoftVector *rxBurst = NULL;
690 int RSSI;
691 int TOA; // in 1/256 of a symbol
692 GSM::Time burstTime;
693
694 mRadioInterface->driveReceiveRadio();
695
696 rxBurst = pullRadioVector(burstTime,RSSI,TOA);
697
698 if (rxBurst) {
699
700 LOG(DEBUG) << "burst parameters: "
701 << " time: " << burstTime
702 << " RSSI: " << RSSI
703 << " TOA: " << TOA
704 << " bits: " << *rxBurst;
705
706 char burstString[gSlotLen+10];
707 burstString[0] = burstTime.TN();
708 for (int i = 0; i < 4; i++)
709 burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
710 burstString[5] = RSSI;
711 burstString[6] = (TOA >> 8) & 0x0ff;
712 burstString[7] = TOA & 0x0ff;
713 SoftVector::iterator burstItr = rxBurst->begin();
714
715 for (unsigned int i = 0; i < gSlotLen; i++) {
716 burstString[8+i] =(char) round((*burstItr++)*255.0);
717 }
718 burstString[gSlotLen+9] = '\0';
719 delete rxBurst;
720
721 mDataSocket.write(burstString,gSlotLen+10);
722 }
723
724}
725
726void Transceiver::driveTransmitFIFO()
727{
728
729 /**
730 Features a carefully controlled latency mechanism, to
731 assure that transmit packets arrive at the radio/USRP
732 before they need to be transmitted.
733
734 Deadline clock indicates the burst that needs to be
735 pushed into the FIFO right NOW. If transmit queue does
736 not have a burst, stick in filler data.
737 */
738
739
740 RadioClock *radioClock = (mRadioInterface->getClock());
741
742 if (mOn) {
743 //radioClock->wait(); // wait until clock updates
744 LOG(DEBUG) << "radio clock " << radioClock->get();
745 while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
746 // if underrun, then we're not providing bursts to radio/USRP fast
747 // enough. Need to increase latency by one GSM frame.
Thomas Tsou02d88d12013-04-05 15:36:30 -0400748 if (mRadioInterface->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000749 if (mRadioInterface->isUnderrun()) {
ttsou2173abf2012-08-08 00:51:31 +0000750 // only update latency at the defined frame interval
751 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000752 mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
753 LOG(INFO) << "new latency: " << mTransmitLatency;
754 mLatencyUpdateTime = radioClock->get();
755 }
756 }
757 else {
758 // if underrun hasn't occurred in the last sec (216 frames) drop
759 // transmit latency by a timeslot
ttsou2173abf2012-08-08 00:51:31 +0000760 if (mTransmitLatency > GSM::Time(USB_LATENCY_MIN)) {
kurtis.heimerle380af32011-11-26 03:18:55 +0000761 if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
762 mTransmitLatency.decTN();
763 LOG(INFO) << "reduced latency: " << mTransmitLatency;
764 mLatencyUpdateTime = radioClock->get();
765 }
766 }
767 }
dburgessb3a0ca42011-10-12 07:44:40 +0000768 }
dburgessb3a0ca42011-10-12 07:44:40 +0000769 // time to push burst to transmit FIFO
770 pushRadioVector(mTransmitDeadlineClock);
771 mTransmitDeadlineClock.incTN();
772 }
dburgessb3a0ca42011-10-12 07:44:40 +0000773 }
Thomas Tsou92c16df2013-09-28 18:04:19 -0400774
775 radioClock->wait();
dburgessb3a0ca42011-10-12 07:44:40 +0000776}
777
778
779
780void Transceiver::writeClockInterface()
781{
782 char command[50];
783 // FIXME -- This should be adaptive.
784 sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
785
786 LOG(INFO) << "ClockInterface: sending " << command;
787
788 mClockSocket.write(command,strlen(command)+1);
789
790 mLastClockUpdateTime = mTransmitDeadlineClock;
791
Thomas Tsou92c16df2013-09-28 18:04:19 -0400792}
dburgessb3a0ca42011-10-12 07:44:40 +0000793
Thomas Tsou92c16df2013-09-28 18:04:19 -0400794void *RxServiceLoopAdapter(Transceiver *transceiver)
dburgessb3a0ca42011-10-12 07:44:40 +0000795{
kurtis.heimerl6b495a52011-11-26 03:17:21 +0000796 transceiver->setPriority();
797
dburgessb3a0ca42011-10-12 07:44:40 +0000798 while (1) {
799 transceiver->driveReceiveFIFO();
Thomas Tsou92c16df2013-09-28 18:04:19 -0400800 pthread_testcancel();
801 }
802 return NULL;
803}
804
805void *TxServiceLoopAdapter(Transceiver *transceiver)
806{
807 while (1) {
dburgessb3a0ca42011-10-12 07:44:40 +0000808 transceiver->driveTransmitFIFO();
809 pthread_testcancel();
810 }
811 return NULL;
812}
813
814void *ControlServiceLoopAdapter(Transceiver *transceiver)
815{
816 while (1) {
817 transceiver->driveControl();
818 pthread_testcancel();
819 }
820 return NULL;
821}
822
823void *TransmitPriorityQueueServiceLoopAdapter(Transceiver *transceiver)
824{
825 while (1) {
826 bool stale = false;
827 // Flush the UDP packets until a successful transfer.
828 while (!transceiver->driveTransmitPriorityQueue()) {
829 stale = true;
830 }
831 if (stale) {
832 // If a packet was stale, remind the GSM stack of the clock.
833 transceiver->writeClockInterface();
834 }
835 pthread_testcancel();
836 }
837 return NULL;
838}