diff options
author | dburgess <dburgess@19bc5d8c-e614-43d4-8b26-e1612bc8e597> | 2011-10-12 07:44:40 +0000 |
---|---|---|
committer | dburgess <dburgess@19bc5d8c-e614-43d4-8b26-e1612bc8e597> | 2011-10-12 07:44:40 +0000 |
commit | b3a0ca42db0bd08c58b9370a1398528016e6953f (patch) | |
tree | 1e81558498b765f0faac4c8588c18fbc4bfc8dfb /Transceiver52M/Transceiver.cpp | |
parent | ec3d77d0eaa12c102893490766557dd4d4efd029 (diff) |
Adding in the missing Transceiver52M directory
git-svn-id: http://wush.net/svn/range/software/public/openbts/trunk@2307 19bc5d8c-e614-43d4-8b26-e1612bc8e597
Diffstat (limited to 'Transceiver52M/Transceiver.cpp')
-rw-r--r-- | Transceiver52M/Transceiver.cpp | 798 |
1 files changed, 798 insertions, 0 deletions
diff --git a/Transceiver52M/Transceiver.cpp b/Transceiver52M/Transceiver.cpp new file mode 100644 index 0000000..7cb2128 --- /dev/null +++ b/Transceiver52M/Transceiver.cpp @@ -0,0 +1,798 @@ +/* +* Copyright 2008, 2009, 2010 Free Software Foundation, Inc. +* +* This software is distributed under the terms of the GNU Public License. +* See the COPYING file in the main directory for details. +* +* This use of this software may be subject to additional restrictions. +* See the LEGAL file in the main directory for details. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see <http://www.gnu.org/licenses/>. +*/ + + +/* + Compilation switches + TRANSMIT_LOGGING write every burst on the given slot to a log +*/ + + +#include <stdio.h> +#include "Transceiver.h" +#include <Logger.h> + + + +Transceiver::Transceiver(int wBasePort, + const char *TRXAddress, + int wSamplesPerSymbol, + GSM::Time wTransmitLatency, + RadioInterface *wRadioInterface) + :mDataSocket(wBasePort+2,TRXAddress,wBasePort+102), + mControlSocket(wBasePort+1,TRXAddress,wBasePort+101), + mClockSocket(wBasePort,TRXAddress,wBasePort+100) +{ + //GSM::Time startTime(0,0); + //GSM::Time startTime(gHyperframe/2 - 4*216*60,0); + GSM::Time startTime(random() % gHyperframe,0); + + mFIFOServiceLoopThread = new Thread(32768); ///< thread to push bursts into transmit FIFO + mControlServiceLoopThread = new Thread(32768); ///< thread to process control messages from GSM core + mTransmitPriorityQueueServiceLoopThread = new Thread(32768);///< thread to process transmit bursts from GSM core + + + mSamplesPerSymbol = wSamplesPerSymbol; + mRadioInterface = wRadioInterface; + mTransmitLatency = wTransmitLatency; + mTransmitDeadlineClock = startTime; + mLastClockUpdateTime = startTime; + mLatencyUpdateTime = startTime; + mRadioInterface->getClock()->set(startTime); + mMaxExpectedDelay = 0; + + // generate pulse and setup up signal processing library + gsmPulse = generateGSMPulse(2,mSamplesPerSymbol); + LOG(DEBUG) << "gsmPulse: " << *gsmPulse; + sigProcLibSetup(mSamplesPerSymbol); + + txFullScale = mRadioInterface->fullScaleInputValue(); + rxFullScale = mRadioInterface->fullScaleOutputValue(); + + // initialize filler tables with dummy bursts, initialize other per-timeslot variables + for (int i = 0; i < 8; i++) { + signalVector* modBurst = modulateBurst(gDummyBurst,*gsmPulse, + 8 + (i % 4 == 0), + mSamplesPerSymbol); + scaleVector(*modBurst,txFullScale); + fillerModulus[i]=26; + for (int j = 0; j < 102; j++) { + fillerTable[j][i] = new signalVector(*modBurst); + } + delete modBurst; + mChanType[i] = NONE; + channelResponse[i] = NULL; + DFEForward[i] = NULL; + DFEFeedback[i] = NULL; + channelEstimateTime[i] = startTime; + } + + mOn = false; + mTxFreq = 0.0; + mRxFreq = 0.0; + mPower = -10; + mEnergyThreshold = 5.0; // based on empirical data + prevFalseDetectionTime = startTime; +} + +Transceiver::~Transceiver() +{ + delete gsmPulse; + sigProcLibDestroy(); + mTransmitPriorityQueue.clear(); +} + + +void Transceiver::addRadioVector(BitVector &burst, + int RSSI, + GSM::Time &wTime) +{ + // modulate and stick into queue + signalVector* modBurst = modulateBurst(burst,*gsmPulse, + 8 + (wTime.TN() % 4 == 0), + mSamplesPerSymbol); + scaleVector(*modBurst,txFullScale * pow(10,-RSSI/10)); + radioVector *newVec = new radioVector(*modBurst,wTime); + mTransmitPriorityQueue.write(newVec); + + delete modBurst; +} + +#ifdef TRANSMIT_LOGGING +void Transceiver::unModulateVector(signalVector wVector) +{ + SoftVector *burst = demodulateBurst(wVector, + *gsmPulse, + mSamplesPerSymbol, + 1.0,0.0); + LOG(DEBUG) << "LOGGED BURST: " << *burst; + +/* + unsigned char burstStr[gSlotLen+1]; + SoftVector::iterator burstItr = burst->begin(); + for (int i = 0; i < gSlotLen; i++) { + // FIXME: Demod bits are inverted! + burstStr[i] = (unsigned char) ((*burstItr++)*255.0); + } + burstStr[gSlotLen]='\0'; + LOG(DEBUG) << "LOGGED BURST: " << burstStr; +*/ + delete burst; +} +#endif + +void Transceiver::pushRadioVector(GSM::Time &nowTime) +{ + + // dump stale bursts, if any + while (radioVector* staleBurst = mTransmitPriorityQueue.getStaleBurst(nowTime)) { + // Even if the burst is stale, put it in the fillter table. + // (It might be an idle pattern.) + LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface"; + const GSM::Time& nextTime = staleBurst->time(); + int TN = nextTime.TN(); + int modFN = nextTime.FN() % fillerModulus[TN]; + delete fillerTable[modFN][TN]; + fillerTable[modFN][TN] = staleBurst; + } + + int TN = nowTime.TN(); + int modFN = nowTime.FN() % fillerModulus[nowTime.TN()]; + + // if queue contains data at the desired timestamp, stick it into FIFO + if (radioVector *next = (radioVector*) mTransmitPriorityQueue.getCurrentBurst(nowTime)) { + LOG(DEBUG) << "transmitFIFO: wrote burst " << next << " at time: " << nowTime; + delete fillerTable[modFN][TN]; + fillerTable[modFN][TN] = new signalVector(*(next)); + mRadioInterface->driveTransmitRadio(*(next),(mChanType[TN]==NONE)); //fillerTable[modFN][TN])); + delete next; +#ifdef TRANSMIT_LOGGING + if (nowTime.TN()==TRANSMIT_LOGGING) { + unModulateVector(*(fillerTable[modFN][TN])); + } +#endif + return; + } + + // otherwise, pull filler data, and push to radio FIFO + mRadioInterface->driveTransmitRadio(*(fillerTable[modFN][TN]),(mChanType[TN]==NONE)); +#ifdef TRANSMIT_LOGGING + if (nowTime.TN()==TRANSMIT_LOGGING) + unModulateVector(*fillerTable[modFN][TN]); +#endif + +} + +void Transceiver::setModulus(int timeslot) +{ + switch (mChanType[timeslot]) { + case NONE: + case I: + case II: + case III: + case FILL: + fillerModulus[timeslot] = 26; + break; + case IV: + case VI: + case V: + fillerModulus[timeslot] = 51; + break; + //case V: + case VII: + fillerModulus[timeslot] = 102; + break; + default: + break; + } +} + + +Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime) +{ + + unsigned burstTN = currTime.TN(); + unsigned burstFN = currTime.FN(); + + switch (mChanType[burstTN]) { + case NONE: + return OFF; + break; + case FILL: + return IDLE; + break; + case I: + return TSC; + /*if (burstFN % 26 == 25) + return IDLE; + else + return TSC;*/ + break; + case II: + if (burstFN % 2 == 1) + return IDLE; + else + return TSC; + break; + case III: + return TSC; + break; + case IV: + case VI: + return RACH; + break; + case V: { + int mod51 = burstFN % 51; + if ((mod51 <= 36) && (mod51 >= 14)) + return RACH; + else if ((mod51 == 4) || (mod51 == 5)) + return RACH; + else if ((mod51 == 45) || (mod51 == 46)) + return RACH; + else + return TSC; + break; + } + case VII: + if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12)) + return IDLE; + else + return TSC; + break; + case LOOPBACK: + if ((burstFN % 51 <= 50) && (burstFN % 51 >=48)) + return IDLE; + else + return TSC; + break; + default: + return OFF; + break; + } + +} + +SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, + int &RSSI, + int &timingOffset) +{ + bool needDFE = (mMaxExpectedDelay > 1); + + radioVector *rxBurst = (radioVector *) mReceiveFIFO->get(); + + if (!rxBurst) return NULL; + + LOG(DEBUG) << "receiveFIFO: read radio vector at time: " << rxBurst->time() << ", new size: " << mReceiveFIFO->size(); + + int timeslot = rxBurst->time().TN(); + + CorrType corrType = expectedCorrType(rxBurst->time()); + + if ((corrType==OFF) || (corrType==IDLE)) { + delete rxBurst; + return NULL; + } + + // check to see if received burst has sufficient + signalVector *vectorBurst = rxBurst; + complex amplitude = 0.0; + float TOA = 0.0; + float avgPwr = 0.0; + if (!energyDetect(*vectorBurst,20*mSamplesPerSymbol,mEnergyThreshold,&avgPwr)) { + LOG(DEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->time(); + double framesElapsed = rxBurst->time()-prevFalseDetectionTime; + if (framesElapsed > 50) { // if we haven't had any false detections for a while, lower threshold + mEnergyThreshold -= 10.0/10.0; + prevFalseDetectionTime = rxBurst->time(); + } + delete rxBurst; + return NULL; + } + LOG(DEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->time(); + + // run the proper correlator + bool success = false; + if (corrType==TSC) { + LOG(DEBUG) << "looking for TSC at time: " << rxBurst->time(); + signalVector *channelResp; + double framesElapsed = rxBurst->time()-channelEstimateTime[timeslot]; + bool estimateChannel = false; + if ((framesElapsed > 50) || (channelResponse[timeslot]==NULL)) { + if (channelResponse[timeslot]) delete channelResponse[timeslot]; + if (DFEForward[timeslot]) delete DFEForward[timeslot]; + if (DFEFeedback[timeslot]) delete DFEFeedback[timeslot]; + channelResponse[timeslot] = NULL; + DFEForward[timeslot] = NULL; + DFEFeedback[timeslot] = NULL; + estimateChannel = true; + } + if (!needDFE) estimateChannel = false; + float chanOffset; + success = analyzeTrafficBurst(*vectorBurst, + mTSC, + 3.0, + mSamplesPerSymbol, + &litude, + &TOA, + mMaxExpectedDelay, + estimateChannel, + &channelResp, + &chanOffset); + if (success) { + LOG(DEBUG) << "FOUND TSC!!!!!! " << amplitude << " " << TOA; + mEnergyThreshold -= 1.0F/10.0F; + if (mEnergyThreshold < 0.0) mEnergyThreshold = 0.0; + SNRestimate[timeslot] = amplitude.norm2()/(mEnergyThreshold*mEnergyThreshold+1.0); // this is not highly accurate + if (estimateChannel) { + LOG(DEBUG) << "estimating channel..."; + channelResponse[timeslot] = channelResp; + chanRespOffset[timeslot] = chanOffset; + chanRespAmplitude[timeslot] = amplitude; + scaleVector(*channelResp, complex(1.0,0.0)/amplitude); + designDFE(*channelResp, SNRestimate[timeslot], 7, &DFEForward[timeslot], &DFEFeedback[timeslot]); + channelEstimateTime[timeslot] = rxBurst->time(); + LOG(DEBUG) << "SNR: " << SNRestimate[timeslot] << ", DFE forward: " << *DFEForward[timeslot] << ", DFE backward: " << *DFEFeedback[timeslot]; + } + } + else { + double framesElapsed = rxBurst->time()-prevFalseDetectionTime; + LOG(DEBUG) << "wTime: " << rxBurst->time() << ", pTime: " << prevFalseDetectionTime << ", fElapsed: " << framesElapsed; + mEnergyThreshold += 10.0F/10.0F*exp(-framesElapsed); + prevFalseDetectionTime = rxBurst->time(); + channelResponse[timeslot] = NULL; + } + } + else { + // RACH burst + success = detectRACHBurst(*vectorBurst, + 5.0, // detection threshold + mSamplesPerSymbol, + &litude, + &TOA); + if (success) { + LOG(DEBUG) << "FOUND RACH!!!!!! " << amplitude << " " << TOA; + mEnergyThreshold -= (1.0F/10.0F); + if (mEnergyThreshold < 0.0) mEnergyThreshold = 0.0; + channelResponse[timeslot] = NULL; + } + else { + double framesElapsed = rxBurst->time()-prevFalseDetectionTime; + mEnergyThreshold += (1.0F/10.0F)*exp(-framesElapsed); + prevFalseDetectionTime = rxBurst->time(); + } + } + LOG(DEBUG) << "energy Threshold = " << mEnergyThreshold; + + // demodulate burst + SoftVector *burst = NULL; + if ((rxBurst) && (success)) { + if ((corrType==RACH) || (!needDFE)) { + burst = demodulateBurst(*vectorBurst, + *gsmPulse, + mSamplesPerSymbol, + amplitude,TOA); + } + else { // TSC + scaleVector(*vectorBurst,complex(1.0,0.0)/amplitude); + burst = equalizeBurst(*vectorBurst, + TOA-chanRespOffset[timeslot], + mSamplesPerSymbol, + *DFEForward[timeslot], + *DFEFeedback[timeslot]); + } + wTime = rxBurst->time(); + // FIXME: what is full scale for the USRP? we get more that 12 bits of resolution... + RSSI = (int) floor(20.0*log10(rxFullScale/amplitude.abs())); + LOG(DEBUG) << "RSSI: " << RSSI; + timingOffset = (int) round(TOA*256.0/mSamplesPerSymbol); + } + + //if (burst) LOG(DEBUG) << "burst: " << *burst << '\n'; + + delete rxBurst; + + return burst; +} + +void Transceiver::start() +{ + mControlServiceLoopThread->start((void * (*)(void*))ControlServiceLoopAdapter,(void*) this); +} + +void Transceiver::reset() +{ + mTransmitPriorityQueue.clear(); + //mTransmitFIFO->clear(); + //mReceiveFIFO->clear(); +} + + +void Transceiver::driveControl() +{ + + int MAX_PACKET_LENGTH = 100; + + // check control socket + char buffer[MAX_PACKET_LENGTH]; + int msgLen = -1; + buffer[0] = '\0'; + + msgLen = mControlSocket.read(buffer); + + if (msgLen < 1) { + return; + } + + char cmdcheck[4]; + char command[MAX_PACKET_LENGTH]; + char response[MAX_PACKET_LENGTH]; + + sscanf(buffer,"%3s %s",cmdcheck,command); + + writeClockInterface(); + + if (strcmp(cmdcheck,"CMD")!=0) { + LOG(WARNING) << "bogus message on control interface"; + return; + } + LOG(INFO) << "command is " << buffer; + + if (strcmp(command,"POWEROFF")==0) { + // turn off transmitter/demod + sprintf(response,"RSP POWEROFF 0"); + } + else if (strcmp(command,"POWERON")==0) { + // turn on transmitter/demod + if (!mTxFreq || !mRxFreq) + sprintf(response,"RSP POWERON 1"); + else { + sprintf(response,"RSP POWERON 0"); + if (!mOn) { + // Prepare for thread start + mPower = -20; + mRadioInterface->start(); + generateRACHSequence(*gsmPulse,mSamplesPerSymbol); + + // Start radio interface threads. + mFIFOServiceLoopThread->start((void * (*)(void*))FIFOServiceLoopAdapter,(void*) this); + mTransmitPriorityQueueServiceLoopThread->start((void * (*)(void*))TransmitPriorityQueueServiceLoopAdapter,(void*) this); + writeClockInterface(); + + mOn = true; + } + } + } + else if (strcmp(command,"SETMAXDLY")==0) { + //set expected maximum time-of-arrival + int maxDelay; + sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay); + mMaxExpectedDelay = maxDelay; // 1 GSM symbol is approx. 1 km + sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay); + } + else if (strcmp(command,"SETRXGAIN")==0) { + //set expected maximum time-of-arrival + int newGain; + sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain); + newGain = mRadioInterface->setRxGain(newGain); + sprintf(response,"RSP SETRXGAIN 0 %d",newGain); + } + else if (strcmp(command,"NOISELEV")==0) { + if (mOn) { + sprintf(response,"RSP NOISELEV 0 %d", + (int) round(20.0*log10(rxFullScale/mEnergyThreshold))); + } + else { + sprintf(response,"RSP NOISELEV 1 0"); + } + } + else if (strcmp(command,"SETPOWER")==0) { + // set output power in dB + int dbPwr; + sscanf(buffer,"%3s %s %d",cmdcheck,command,&dbPwr); + if (!mOn) + sprintf(response,"RSP SETPOWER 1 %d",dbPwr); + else { + mPower = dbPwr; + mRadioInterface->setPowerAttenuation(pow(10.0,dbPwr/10.0)); + sprintf(response,"RSP SETPOWER 0 %d",dbPwr); + } + } + else if (strcmp(command,"ADJPOWER")==0) { + // adjust power in dB steps + int dbStep; + sscanf(buffer,"%3s %s %d",cmdcheck,command,&dbStep); + if (!mOn) + sprintf(response,"RSP ADJPOWER 1 %d",mPower); + else { + mPower += dbStep; + sprintf(response,"RSP ADJPOWER 0 %d",mPower); + } + } +#define FREQOFFSET 0//11.2e3 + else if (strcmp(command,"RXTUNE")==0) { + // tune receiver + int freqKhz; + sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz); + mRxFreq = freqKhz*1.0e3+FREQOFFSET; + if (!mRadioInterface->tuneRx(mRxFreq)) { + LOG(ALERT) << "RX failed to tune"; + sprintf(response,"RSP RXTUNE 1 %d",freqKhz); + } + else + sprintf(response,"RSP RXTUNE 0 %d",freqKhz); + } + else if (strcmp(command,"TXTUNE")==0) { + // tune txmtr + int freqKhz; + sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz); + //freqKhz = 890e3; + mTxFreq = freqKhz*1.0e3+FREQOFFSET; + if (!mRadioInterface->tuneTx(mTxFreq)) { + LOG(ALERT) << "TX failed to tune"; + sprintf(response,"RSP TXTUNE 1 %d",freqKhz); + } + else + sprintf(response,"RSP TXTUNE 0 %d",freqKhz); + } + else if (strcmp(command,"SETTSC")==0) { + // set TSC + int TSC; + sscanf(buffer,"%3s %s %d",cmdcheck,command,&TSC); + if (mOn) + sprintf(response,"RSP SETTSC 1 %d",TSC); + else { + mTSC = TSC; + generateMidamble(*gsmPulse,mSamplesPerSymbol,TSC); + sprintf(response,"RSP SETTSC 0 %d",TSC); + } + } + else if (strcmp(command,"SETSLOT")==0) { + // set TSC + int corrCode; + int timeslot; + sscanf(buffer,"%3s %s %d %d",cmdcheck,command,×lot,&corrCode); + if ((timeslot < 0) || (timeslot > 7)) { + LOG(WARNING) << "bogus message on control interface"; + sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode); + return; + } + mChanType[timeslot] = (ChannelCombination) corrCode; + setModulus(timeslot); + sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode); + + } + else { + LOG(WARNING) << "bogus command " << command << " on control interface."; + } + + mControlSocket.write(response,strlen(response)+1); + +} + +bool Transceiver::driveTransmitPriorityQueue() +{ + + char buffer[gSlotLen+50]; + + // check data socket + size_t msgLen = mDataSocket.read(buffer); + + if (msgLen!=gSlotLen+1+4+1) { + LOG(ERR) << "badly formatted packet on GSM->TRX interface"; + return false; + } + + int timeSlot = (int) buffer[0]; + uint64_t frameNum = 0; + for (int i = 0; i < 4; i++) + frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]); + + /* + if (GSM::Time(frameNum,timeSlot) > mTransmitDeadlineClock + GSM::Time(51,0)) { + // stale burst + //LOG(DEBUG) << "FAST! "<< GSM::Time(frameNum,timeSlot); + //writeClockInterface(); + }*/ + +/* + DAB -- Just let these go through the demod. + if (GSM::Time(frameNum,timeSlot) < mTransmitDeadlineClock) { + // stale burst from GSM core + LOG(NOTICE) << "STALE packet on GSM->TRX interface at time "<< GSM::Time(frameNum,timeSlot); + return false; + } +*/ + + // periodically update GSM core clock + LOG(DEBUG) << "mTransmitDeadlineClock " << mTransmitDeadlineClock + << " mLastClockUpdateTime " << mLastClockUpdateTime; + if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) + writeClockInterface(); + + + LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot); + + int RSSI = (int) buffer[5]; + static BitVector newBurst(gSlotLen); + BitVector::iterator itr = newBurst.begin(); + char *bufferItr = buffer+6; + while (itr < newBurst.end()) + *itr++ = *bufferItr++; + + GSM::Time currTime = GSM::Time(frameNum,timeSlot); + + addRadioVector(newBurst,RSSI,currTime); + + LOG(DEBUG) "added burst - time: " << currTime << ", RSSI: " << RSSI; // << ", data: " << newBurst; + + return true; + + +} + +void Transceiver::driveReceiveFIFO() +{ + + SoftVector *rxBurst = NULL; + int RSSI; + int TOA; // in 1/256 of a symbol + GSM::Time burstTime; + + mRadioInterface->driveReceiveRadio(); + + rxBurst = pullRadioVector(burstTime,RSSI,TOA); + + if (rxBurst) { + + LOG(DEBUG) << "burst parameters: " + << " time: " << burstTime + << " RSSI: " << RSSI + << " TOA: " << TOA + << " bits: " << *rxBurst; + + char burstString[gSlotLen+10]; + burstString[0] = burstTime.TN(); + for (int i = 0; i < 4; i++) + burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff; + burstString[5] = RSSI; + burstString[6] = (TOA >> 8) & 0x0ff; + burstString[7] = TOA & 0x0ff; + SoftVector::iterator burstItr = rxBurst->begin(); + + for (unsigned int i = 0; i < gSlotLen; i++) { + burstString[8+i] =(char) round((*burstItr++)*255.0); + } + burstString[gSlotLen+9] = '\0'; + delete rxBurst; + + mDataSocket.write(burstString,gSlotLen+10); + } + +} + +void Transceiver::driveTransmitFIFO() +{ + + /** + Features a carefully controlled latency mechanism, to + assure that transmit packets arrive at the radio/USRP + before they need to be transmitted. + + Deadline clock indicates the burst that needs to be + pushed into the FIFO right NOW. If transmit queue does + not have a burst, stick in filler data. + */ + + + RadioClock *radioClock = (mRadioInterface->getClock()); + + if (mOn) { + //radioClock->wait(); // wait until clock updates + LOG(DEBUG) << "radio clock " << radioClock->get(); + while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) { + // if underrun, then we're not providing bursts to radio/USRP fast + // enough. Need to increase latency by one GSM frame. + if (mRadioInterface->isUnderrun()) { + // only do latency update every 10 frames, so we don't over update + if (radioClock->get() > mLatencyUpdateTime + GSM::Time(10,0)) { + mTransmitLatency = mTransmitLatency + GSM::Time(1,0); + LOG(INFO) << "new latency: " << mTransmitLatency; + mLatencyUpdateTime = radioClock->get(); + } + } + else { + // if underrun hasn't occurred in the last sec (216 frames) drop + // transmit latency by a timeslot + if (mTransmitLatency > GSM::Time(1,1)) { + if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) { + mTransmitLatency.decTN(); + LOG(INFO) << "reduced latency: " << mTransmitLatency; + mLatencyUpdateTime = radioClock->get(); + } + } + } + // time to push burst to transmit FIFO + pushRadioVector(mTransmitDeadlineClock); + mTransmitDeadlineClock.incTN(); + } + + } + // FIXME -- This should not be a hard spin. + // But any delay here causes us to throw omni_thread_fatal. + //else radioClock->wait(); +} + + + +void Transceiver::writeClockInterface() +{ + char command[50]; + // FIXME -- This should be adaptive. + sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2)); + + LOG(INFO) << "ClockInterface: sending " << command; + + mClockSocket.write(command,strlen(command)+1); + + mLastClockUpdateTime = mTransmitDeadlineClock; + +} + + + + +void *FIFOServiceLoopAdapter(Transceiver *transceiver) +{ + while (1) { + transceiver->driveReceiveFIFO(); + transceiver->driveTransmitFIFO(); + pthread_testcancel(); + } + return NULL; +} + +void *ControlServiceLoopAdapter(Transceiver *transceiver) +{ + while (1) { + transceiver->driveControl(); + pthread_testcancel(); + } + return NULL; +} + +void *TransmitPriorityQueueServiceLoopAdapter(Transceiver *transceiver) +{ + while (1) { + bool stale = false; + // Flush the UDP packets until a successful transfer. + while (!transceiver->driveTransmitPriorityQueue()) { + stale = true; + } + if (stale) { + // If a packet was stale, remind the GSM stack of the clock. + transceiver->writeClockInterface(); + } + pthread_testcancel(); + } + return NULL; +} |