| /* |
| * Copyright 2008, 2009, 2010, 2012 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/>. |
| */ |
| |
| #include <stdio.h> |
| #include "DriveLoop.h" |
| #include <Logger.h> |
| |
| using namespace GSM; |
| |
| DriveLoop::DriveLoop(int wBasePort, const char *TRXAddress, |
| RadioInterface *wRadioInterface, |
| int wChanM, int wC0, int wSPS, |
| GSM::Time wTransmitLatency) |
| :mClockSocket(wBasePort, TRXAddress, wBasePort + 100), mC0(wC0) |
| { |
| mChanM = wChanM; |
| mRadioDriveLoopThread = NULL; |
| mSPS = wSPS; |
| mRadioInterface = wRadioInterface; |
| |
| mStartTime = (random() % gHyperframe, 0); |
| |
| mTransmitDeadlineClock = mStartTime; |
| mLatencyUpdateTime = mStartTime; |
| mTransmitLatency = wTransmitLatency; |
| mLastClockUpdateTime = mStartTime; |
| |
| mRadioInterface->getClock()->set(mStartTime); |
| txFullScale = mRadioInterface->fullScaleInputValue(); |
| |
| mOn = false; |
| } |
| |
| DriveLoop::~DriveLoop() |
| { |
| if (mOn) { |
| mOn = false; |
| |
| if (mRadioDriveLoopThread) |
| delete mRadioDriveLoopThread; |
| } |
| |
| sigProcLibDestroy(); |
| } |
| |
| bool DriveLoop::init() |
| { |
| if (!sigProcLibSetup(mSPS)) { |
| LOG(ALERT) << "Failed to initialize signal processing library"; |
| return false; |
| } |
| |
| // initialize filler tables with dummy bursts on C0, empty bursts otherwise |
| for (int i = 0; i < 8; i++) { |
| signalVector* modBurst = modulateBurst(gDummyBurst, |
| 8 + (i % 4 == 0), mSPS); |
| if (!modBurst) { |
| sigProcLibDestroy(); |
| LOG(ALERT) << "Failed to initialize filler table"; |
| return false; |
| } |
| |
| scaleVector(*modBurst, txFullScale); |
| for (int j = 0; j < 102; j++) { |
| for (int n = 0; n < mChanM; n++) { |
| #ifndef TRX_LOAD_TESTING |
| if (n == mC0) |
| fillerTable[n][j][i] = new signalVector(*modBurst); |
| else |
| fillerTable[n][j][i] = new signalVector(modBurst->size()); |
| #else |
| fillerTable[n][j][i] = new signalVector(*modBurst); |
| #endif |
| } |
| } |
| delete modBurst; |
| |
| for (int n = 0; n < mChanM; n++) { |
| fillerModulus[n][i] = 26; |
| mChanType[n][i] = NONE; |
| } |
| } |
| |
| return true; |
| } |
| |
| void DriveLoop::start() |
| { |
| if (mOn) |
| return; |
| |
| mOn = true; |
| mRadioDriveLoopThread = new Thread(32768); |
| mRadioDriveLoopThread->start((void * (*)(void*))RadioDriveLoopAdapter, (void*) this); |
| } |
| |
| void DriveLoop::pushRadioVector(GSM::Time &nowTime) |
| { |
| int i; |
| radioVector *staleBurst; |
| radioVector *next; |
| |
| for (i = 0; i < mChanM; i++) { |
| // dump stale bursts, if any |
| while (staleBurst = mTransmitPriorityQueue[i].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"; |
| } |
| |
| int TN = nowTime.TN(); |
| int modFN = nowTime.FN() % fillerModulus[i][nowTime.TN()]; |
| |
| mTxBursts[i] = fillerTable[i][modFN][TN]; |
| mIsFiller[i] = true; |
| #ifndef TRX_LOAD_TESTING |
| mIsZero[i] = (mChanType[i][TN] == NONE); |
| #else |
| mIsZero[i] = false; |
| #endif |
| |
| // if queue contains data at the desired timestamp, stick it into FIFO |
| if (next = (radioVector*) mTransmitPriorityQueue[i].getCurrentBurst(nowTime)) { |
| LOG(DEBUG) << "transmitFIFO: wrote burst " << next << " at time: " << nowTime; |
| mTxBursts[i] = next; |
| mIsFiller[i] = false; |
| mIsZero[i] = false; |
| } |
| } |
| |
| mRadioInterface->driveTransmitRadio(mTxBursts, mIsZero); |
| |
| for (i = 0; i < mChanM; i++) { |
| if (!mIsFiller[i]) |
| delete mTxBursts[i]; |
| } |
| } |
| |
| void DriveLoop::setModulus(int channel, int timeslot) |
| { |
| switch (mChanType[channel][timeslot]) { |
| case NONE: |
| case I: |
| case II: |
| case III: |
| case FILL: |
| fillerModulus[channel][timeslot] = 26; |
| break; |
| case IV: |
| case VI: |
| case V: |
| fillerModulus[channel][timeslot] = 51; |
| break; |
| //case V: |
| case VII: |
| fillerModulus[channel][timeslot] = 102; |
| break; |
| default: |
| break; |
| } |
| } |
| |
| DriveLoop::CorrType DriveLoop::expectedCorrType(int channel, GSM::Time currTime) |
| { |
| unsigned burstTN = currTime.TN(); |
| unsigned burstFN = currTime.FN(); |
| |
| switch (mChanType[channel][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; |
| } |
| } |
| |
| void DriveLoop::driveReceiveFIFO() |
| { |
| SoftVector *rxBurst = NULL; |
| int RSSI; |
| int TOA; // in 1/256 of a symbol |
| GSM::Time burstTime; |
| |
| mRadioInterface->driveReceiveRadio(); |
| } |
| |
| /* |
| * 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. |
| */ |
| void DriveLoop::driveTransmitFIFO() |
| { |
| int i; |
| |
| RadioClock *radioClock = (mRadioInterface->getClock()); |
| while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) { |
| 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 DriveLoop::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 *RadioDriveLoopAdapter(DriveLoop *drive) |
| { |
| drive->setPriority(); |
| |
| while (drive->on()) { |
| drive->driveReceiveFIFO(); |
| drive->driveTransmitFIFO(); |
| pthread_testcancel(); |
| } |
| |
| return NULL; |
| } |