aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Tsou <ttsou@vt.edu>2012-02-28 15:41:50 -0500
committerAlexander Chemeris <Alexander.Chemeris@gmail.com>2013-06-24 01:50:59 +0400
commit996f426c16d7cf804fc6692e4eb4c59589e82691 (patch)
tree1afb953de58ca51662f33df3abbfddf0838d47d2
parent711e6afddf46a3ad231521ff44836b0613a17743 (diff)
multi-arfcn, trx: split transceiver to handle multiple channels
This patch separates the 'Transceiver' into a multi-channel I/O component and single channel component. The latter may may have multiple instances. The receive FIFO is converted to a thread-safe queue. The 'TransceiverIO' continuously drives the receive and transmit loops. In this process, bursts are driven into thread-safe FIFO's and read from the priority queues. Filler bursts are inserted if no transmit data is available. Each 'Transceiver' instance attaches to the I/O object and creates its own threads and sockets, which include blocking on the receive FIFO for the attached channel. Each instance also handles its own control loop and clock indications. Signed-off-by: Thomas Tsou <ttsou@vt.edu>
-rw-r--r--Transceiver52M/DriveLoop.cpp264
-rw-r--r--Transceiver52M/DriveLoop.h185
-rw-r--r--Transceiver52M/Transceiver.cpp439
-rw-r--r--Transceiver52M/Transceiver.h65
-rw-r--r--Transceiver52M/radioVector.cpp17
-rw-r--r--Transceiver52M/radioVector.h11
6 files changed, 566 insertions, 415 deletions
diff --git a/Transceiver52M/DriveLoop.cpp b/Transceiver52M/DriveLoop.cpp
new file mode 100644
index 0000000..997d009
--- /dev/null
+++ b/Transceiver52M/DriveLoop.cpp
@@ -0,0 +1,264 @@
+/*
+* 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>
+
+DriveLoop::DriveLoop(int wSamplesPerSymbol,
+ GSM::Time wTransmitLatency,
+ RadioInterface *wRadioInterface)
+{
+ mRadioDriveLoopThread = new Thread(32768);
+
+ mSamplesPerSymbol = wSamplesPerSymbol;
+ mRadioInterface = wRadioInterface;
+
+ GSM::Time startTime(random() % gHyperframe, 0);
+ mTransmitDeadlineClock = startTime;
+ mLatencyUpdateTime = startTime;
+ mTransmitLatency = wTransmitLatency;
+
+ mRadioInterface->getClock()->set(startTime);
+
+ // generate pulse and setup up signal processing library
+ gsmPulse = generateGSMPulse(2, mSamplesPerSymbol);
+ LOG(DEBUG) << "gsmPulse: " << *gsmPulse;
+ sigProcLibSetup(mSamplesPerSymbol);
+
+ txFullScale = mRadioInterface->fullScaleInputValue();
+
+ // 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);
+ for (int j = 0; j < 102; j++) {
+ for (int n = 0; n < CHAN_M; n++)
+ fillerTable[n][j][i] = new signalVector(*modBurst);
+ }
+ delete modBurst;
+
+ for (int n = 0; n < CHAN_M; n++) {
+ fillerModulus[n][i] = 26;
+ mChanType[n][i] = NONE;
+ }
+ }
+
+ mOn = false;
+}
+
+DriveLoop::~DriveLoop()
+{
+ delete gsmPulse;
+ sigProcLibDestroy();
+}
+
+void DriveLoop::start()
+{
+ mOn = true;
+ mRadioDriveLoopThread->start((void * (*)(void*))RadioDriveLoopAdapter, (void*) this);
+}
+
+void DriveLoop::pushRadioVector(GSM::Time &nowTime)
+{
+ int i;
+ radioVector *staleBurst;
+ radioVector *next;
+
+ for (i = 0; i < CHAN_M; 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";
+ const GSM::Time& nextTime = staleBurst->getTime();
+ int TN = nextTime.TN();
+ int modFN = nextTime.FN() % fillerModulus[i][TN];
+ delete fillerTable[i][modFN][TN];
+ fillerTable[i][modFN][TN] = staleBurst;
+ }
+
+ int TN = nowTime.TN();
+ int modFN = nowTime.FN() % fillerModulus[i][nowTime.TN()];
+
+ mTxBursts[i] = fillerTable[i][modFN][TN];
+ mIsFiller[i] = true;
+ mIsZero[i] = (mChanType[i][TN] == NONE);
+
+ // 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;
+ delete fillerTable[i][modFN][TN];
+ fillerTable[i][modFN][TN] = new signalVector(*(next));
+ mTxBursts[i] = next;
+ mIsFiller[i] = false;
+ }
+ }
+
+ mRadioInterface->driveTransmitRadio(mTxBursts, mIsZero);
+
+ for (i = 0; i < CHAN_M; 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::reset()
+{
+}
+
+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;
+
+ if (!mOn)
+ return;
+
+ 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 *RadioDriveLoopAdapter(DriveLoop *transceiver)
+{
+ transceiver->setPriority();
+
+ while (1) {
+ transceiver->driveReceiveFIFO();
+ transceiver->driveTransmitFIFO();
+ pthread_testcancel();
+ }
+ return NULL;
+}
diff --git a/Transceiver52M/DriveLoop.h b/Transceiver52M/DriveLoop.h
new file mode 100644
index 0000000..7a59caa
--- /dev/null
+++ b/Transceiver52M/DriveLoop.h
@@ -0,0 +1,185 @@
+/*
+* Copyright 2008, 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/>.
+
+*/
+
+
+
+/*
+ Compilation switches
+ TRANSMIT_LOGGING write every burst on the given slot to a log
+*/
+
+#ifndef _DRIVELOOP_H_
+#define _DRIVELOOP_H_
+
+#include "radioInterface.h"
+#include "Interthread.h"
+#include "GSMCommon.h"
+#include "Sockets.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+/** Define this to be the slot number to be logged. */
+//#define TRANSMIT_LOGGING 1
+
+/** The Transceiver class, responsible for physical layer of basestation */
+class DriveLoop {
+
+private:
+
+ GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
+ GSM::Time mLatencyUpdateTime; ///< last time latency was updated
+
+ UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
+
+ VectorQueue mTransmitPriorityQueue[CHAN_M]; ///< priority queue of transmit bursts received from GSM core
+
+ Thread *mRadioDriveLoopThread; ///< thread to push/pull bursts into transmit/receive FIFO
+
+ GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
+
+ RadioInterface *mRadioInterface; ///< associated radioInterface object
+ double txFullScale; ///< full scale input to radio
+ double rxFullScale; ///< full scale output to radio
+
+
+ /** unmodulate a modulated burst */
+#ifdef TRANSMIT_LOGGING
+ void unModulateVector(signalVector wVector);
+#endif
+
+ /** modulate and add a burst to the transmit queue */
+ void addRadioVector(BitVector &burst, int RSSI, GSM::Time &wTime);
+
+ /** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
+ void pushRadioVector(GSM::Time &nowTime);
+
+ /** Pull and demodulate a burst from the receive FIFO */
+ SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
+
+ /** send messages over the clock socket */
+ void writeClockInterface(void);
+
+ signalVector *gsmPulse; ///< the GSM shaping pulse for modulation
+
+ int mSamplesPerSymbol; ///< number of samples per GSM symbol
+
+ bool mOn; ///< flag to indicate that transceiver is powered on
+ int fillerModulus[CHAN_M][8]; ///< modulus values of all timeslots, in frames
+ signalVector *fillerTable[CHAN_M][102][8]; ///< table of modulated filler waveforms for all timeslots
+
+ signalVector *mTxBursts[CHAN_M];
+ bool mIsFiller[CHAN_M];
+ bool mIsZero[CHAN_M];
+
+public:
+
+ /** Transceiver constructor
+ @param wBasePort base port number of UDP sockets
+ @param TRXAddress IP address of the TRX manager, as a string
+ @param wSamplesPerSymbol number of samples per GSM symbol
+ @param wTransmitLatency initial setting of transmit latency
+ @param radioInterface associated radioInterface object
+ */
+ DriveLoop(int wSamplesPerSymbol,
+ GSM::Time wTransmitLatency,
+ RadioInterface *wRadioInterface);
+
+ /** Destructor */
+ ~DriveLoop();
+
+ /** start the Transceiver */
+ void start();
+
+ VectorQueue *priorityQueue(int m) { return &mTransmitPriorityQueue[m]; }
+
+ GSM::Time *deadlineClock() { return &mTransmitDeadlineClock; }
+
+ /** Codes for burst types of received bursts*/
+ typedef enum {
+ OFF, ///< timeslot is off
+ TSC, ///< timeslot should contain a normal burst
+ RACH, ///< timeslot should contain an access burst
+ IDLE ///< timeslot is an idle (or dummy) burst
+ } CorrType;
+
+ /** Codes for channel combinations */
+ typedef enum {
+ FILL, ///< Channel is transmitted, but unused
+ I, ///< TCH/FS
+ II, ///< TCH/HS, idle every other slot
+ III, ///< TCH/HS
+ IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH
+ V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
+ VI, ///< CCCH+BCCH, uplink RACH
+ VII, ///< SDCCH/8 + SACCH/8
+ NONE, ///< Channel is inactive, default
+ LOOPBACK ///< similar go VII, used in loopback testing
+ } ChannelCombination;
+
+ /** Set modulus for specific timeslot */
+ void setModulus(int channel, int timeslot);
+
+ /** return the expected burst type for the specified timestamp */
+ CorrType expectedCorrType(int channel, GSM::Time currTime);
+
+ void setTimeslot(int m, int timeslot, ChannelCombination comb)
+ {
+ mChanType[m][timeslot] = comb;
+ }
+
+private:
+
+ ChannelCombination mChanType[CHAN_M][8]; ///< channel types for all timeslots
+
+protected:
+
+ /** drive reception and demodulation of GSM bursts */
+ void driveReceiveFIFO();
+
+ /** drive transmission of GSM bursts */
+ void driveTransmitFIFO();
+
+ /** drive handling of control messages from GSM core */
+ void driveControl();
+
+ /**
+ drive modulation and sorting of GSM bursts from GSM core
+ @return true if a burst was transferred successfully
+ */
+ bool driveTransmitPriorityQueue();
+
+ friend void *RadioDriveLoopAdapter(DriveLoop *);
+
+ void reset();
+
+ /** set priority on current thread */
+ void setPriority() { mRadioInterface->setPriority(); }
+
+};
+
+/** FIFO thread loop */
+void *RadioDriveLoopAdapter(DriveLoop *);
+
+#endif /* _DRIVELOOP_H_ */
diff --git a/Transceiver52M/Transceiver.cpp b/Transceiver52M/Transceiver.cpp
index 017b641..6cfcf32 100644
--- a/Transceiver52M/Transceiver.cpp
+++ b/Transceiver52M/Transceiver.cpp
@@ -1,5 +1,5 @@
/*
-* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
+* 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.
@@ -51,17 +51,15 @@ using namespace GSM;
Transceiver::Transceiver(int wBasePort,
const char *TRXAddress,
int wSamplesPerSymbol,
- GSM::Time wTransmitLatency,
- RadioInterface *wRadioInterface)
+ RadioInterface *wRadioInterface,
+ DriveLoop *wDriveLoop,
+ int wChannel)
:mDataSocket(wBasePort+2,TRXAddress,wBasePort+102),
mControlSocket(wBasePort+1,TRXAddress,wBasePort+101),
mClockSocket(wBasePort,TRXAddress,wBasePort+100),
- mTSC(-1)
+ mDriveLoop(wDriveLoop), mTransmitPriorityQueue(NULL),
+ mChannel(wChannel), mTSC(-1)
{
- //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
@@ -69,37 +67,24 @@ Transceiver::Transceiver(int wBasePort,
mSamplesPerSymbol = wSamplesPerSymbol;
mRadioInterface = wRadioInterface;
- mTransmitLatency = wTransmitLatency;
- mTransmitDeadlineClock = startTime;
- mLastClockUpdateTime = startTime;
- mLatencyUpdateTime = startTime;
- mRadioInterface->getClock()->set(startTime);
+ mLastClockUpdateTime = GSM::Time(0, 0);
mMaxExpectedDelay = 0;
+ mTransmitDeadlineClock = wDriveLoop->deadlineClock();
+
// 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
+ // initialize 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;
+ channelEstimateTime[i] = GSM::Time(0, 0);
}
mOn = false;
@@ -107,14 +92,15 @@ Transceiver::Transceiver(int wBasePort,
mRxFreq = 0.0;
mPower = -10;
mEnergyThreshold = INIT_ENERGY_THRSHD;
- prevFalseDetectionTime = startTime;
+ prevFalseDetectionTime = GSM::Time(0, 0);
+
+ mRadioLocked = mRadioInterface->started();
}
Transceiver::~Transceiver()
{
delete gsmPulse;
- sigProcLibDestroy();
- mTransmitPriorityQueue.clear();
+ mTransmitPriorityQueue->clear();
}
@@ -128,199 +114,18 @@ void Transceiver::addRadioVector(BitVector &burst,
mSamplesPerSymbol);
scaleVector(*modBurst,txFullScale * pow(10,-RSSI/10));
radioVector *newVec = new radioVector(*modBurst,wTime);
- mTransmitPriorityQueue.write(newVec);
+ 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
- unsigned BCCH_SCH_FCCH_CCCH_Frames[26] = {0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,30,31,40,41};
-
- while (radioVector* staleBurst = mTransmitPriorityQueue.getStaleBurst(nowTime)) {
- // Even if the burst is stale, put it in the fillter table.
- // (It might be an idle pattern.)
- // Now we do it only for BEACON channels.
- LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
- const GSM::Time& nextTime = staleBurst->getTime();
- int TN = nextTime.TN();
- int modFN = nextTime.FN() % fillerModulus[TN];
- if (TN==0) {
- for (unsigned i =0; i < 26; i++) {
- if(BCCH_SCH_FCCH_CCCH_Frames[i] == modFN) {
- delete fillerTable[modFN][TN];
- fillerTable[modFN][TN] = staleBurst;
- break;
- }
- }
- }
- }
-
- 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;
- if (TN==0) {
- for (unsigned i =0; i < 26; i++) {
- if(BCCH_SCH_FCCH_CCCH_Frames[i] == modFN) {
- delete fillerTable[modFN][TN];
- fillerTable[modFN][TN] = new signalVector(*(next));
- break;
- }
- }
- }
- 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;
- case XIII:
- fillerModulus[timeslot] = 52;
- 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:
- 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 XIII: {
- int mod52 = burstFN % 52;
- if ((mod52 == 12) || (mod52 == 38))
- return RACH;
- else if ((mod52 == 25) || (mod52 == 51))
- 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();
+ radioVector *rxBurst = (radioVector *) mReceiveFIFO->read();
if (!rxBurst) return NULL;
@@ -328,9 +133,9 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
int timeslot = rxBurst->getTime().TN();
- CorrType corrType = expectedCorrType(rxBurst->getTime());
+ DriveLoop::CorrType corrType = mDriveLoop->expectedCorrType(mChannel, rxBurst->getTime());
- if ((corrType==OFF) || (corrType==IDLE)) {
+ if ((corrType == DriveLoop::OFF) || (corrType == DriveLoop::IDLE)) {
delete rxBurst;
return NULL;
}
@@ -340,6 +145,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
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->getTime();
double framesElapsed = rxBurst->getTime()-prevFalseDetectionTime;
@@ -357,7 +163,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
// run the proper correlator
bool success = false;
- if (corrType==TSC) {
+ if (corrType == DriveLoop::TSC) {
LOG(DEBUG) << "looking for TSC at time: " << rxBurst->getTime();
signalVector *channelResp;
double framesElapsed = rxBurst->getTime()-channelEstimateTime[timeslot];
@@ -431,7 +237,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
// demodulate burst
SoftVector *burst = NULL;
if ((rxBurst) && (success)) {
- if ((corrType==RACH) || (!needDFE)) {
+ if ((corrType == DriveLoop::RACH) || (!needDFE)) {
burst = demodulateBurst(*vectorBurst,
*gsmPulse,
mSamplesPerSymbol,
@@ -458,6 +264,44 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
return burst;
}
+void Transceiver::pullFIFO()
+{
+ SoftVector *rxBurst = NULL;
+ int RSSI;
+ int TOA; // in 1/256 of a symbol
+ GSM::Time burstTime;
+
+ 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::start()
{
mControlServiceLoopThread->start((void * (*)(void*))ControlServiceLoopAdapter,(void*) this);
@@ -465,9 +309,7 @@ void Transceiver::start()
void Transceiver::reset()
{
- mTransmitPriorityQueue.clear();
- //mTransmitFIFO->clear();
- //mReceiveFIFO->clear();
+ mTransmitPriorityQueue->clear();
}
@@ -492,7 +334,7 @@ void Transceiver::driveControl()
char response[MAX_PACKET_LENGTH];
sscanf(buffer,"%3s %s",cmdcheck,command);
-
+
writeClockInterface();
if (strcmp(cmdcheck,"CMD")!=0) {
@@ -514,7 +356,9 @@ void Transceiver::driveControl()
if (!mOn) {
// Prepare for thread start
mPower = -20;
- mRadioInterface->start();
+ if (mRadioInterface->start())
+ mDriveLoop->start();
+
generateRACHSequence(*gsmPulse,mSamplesPerSymbol);
// Start radio interface threads.
@@ -539,6 +383,8 @@ void Transceiver::driveControl()
sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain);
newGain = mRadioInterface->setRxGain(newGain);
mEnergyThreshold = INIT_ENERGY_THRSHD;
+ if (!mRadioLocked)
+ newGain = mRadioInterface->setRxGain(newGain);
sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
}
else if (strcmp(command,"NOISELEV")==0) {
@@ -558,7 +404,8 @@ void Transceiver::driveControl()
sprintf(response,"RSP SETPOWER 1 %d",dbPwr);
else {
mPower = dbPwr;
- mRadioInterface->setPowerAttenuation(dbPwr);
+ if (!mRadioLocked)
+ mRadioInterface->setPowerAttenuation(dbPwr);
sprintf(response,"RSP SETPOWER 0 %d",dbPwr);
}
}
@@ -579,12 +426,18 @@ void Transceiver::driveControl()
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
+ mRadioLocked = mRadioInterface->started();
+
+ if (!mRadioLocked) {
+ 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 {
+ sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
+ }
}
else if (strcmp(command,"TXTUNE")==0) {
// tune txmtr
@@ -592,12 +445,17 @@ void Transceiver::driveControl()
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);
+ mRadioLocked = mRadioInterface->started();
+ if (!mRadioLocked) {
+ 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 {
+ sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
}
- else
- sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
}
else if (strcmp(command,"SETTSC")==0) {
// set TSC
@@ -621,8 +479,8 @@ void Transceiver::driveControl()
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
return;
}
- mChanType[timeslot] = (ChannelCombination) corrCode;
- setModulus(timeslot);
+ mDriveLoop->setTimeslot(mChannel, timeslot, (DriveLoop::ChannelCombination) corrCode);
+ mDriveLoop->setModulus(mChannel, timeslot);
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
}
@@ -670,9 +528,9 @@ bool Transceiver::driveTransmitPriorityQueue()
*/
// periodically update GSM core clock
- LOG(DEBUG) << "mTransmitDeadlineClock " << mTransmitDeadlineClock
- << " mLastClockUpdateTime " << mLastClockUpdateTime;
- if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
+ LOG(DEBUG) << "mTransmitDeadlineClock " << *mTransmitDeadlineClock
+ << " mLastClockUpdateTime " << mLastClockUpdateTime;
+ if (*mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
writeClockInterface();
@@ -695,115 +553,19 @@ bool Transceiver::driveTransmitPriorityQueue()
}
-
-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->getWindowType() == RadioDevice::TX_WINDOW_USRP1) {
- if (mRadioInterface->isUnderrun()) {
- // only update latency at the defined frame interval
- if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
- 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(USB_LATENCY_MIN)) {
- 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));
+ sprintf(command,"IND CLOCK %llu",
+ (unsigned long long) (mTransmitDeadlineClock->FN() + 2));
LOG(INFO) << "ClockInterface: sending " << command;
mClockSocket.write(command,strlen(command)+1);
- mLastClockUpdateTime = mTransmitDeadlineClock;
-
+ mLastClockUpdateTime = *mTransmitDeadlineClock;
}
@@ -811,11 +573,8 @@ void Transceiver::writeClockInterface()
void *FIFOServiceLoopAdapter(Transceiver *transceiver)
{
- transceiver->setPriority();
-
while (1) {
- transceiver->driveReceiveFIFO();
- transceiver->driveTransmitFIFO();
+ transceiver->pullFIFO();
pthread_testcancel();
}
return NULL;
diff --git a/Transceiver52M/Transceiver.h b/Transceiver52M/Transceiver.h
index b23c3fa..fed2df9 100644
--- a/Transceiver52M/Transceiver.h
+++ b/Transceiver52M/Transceiver.h
@@ -1,5 +1,5 @@
/*
-* Copyright 2008 Free Software Foundation, Inc.
+* Copyright 2008, 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.
@@ -29,6 +29,7 @@
TRANSMIT_LOGGING write every burst on the given slot to a log
*/
+#include "DriveLoop.h"
#include "radioInterface.h"
#include "Interthread.h"
#include "GSMCommon.h"
@@ -44,59 +45,28 @@
class Transceiver {
private:
-
- GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
- GSM::Time mLatencyUpdateTime; ///< last time latency was updated
+ DriveLoop *mDriveLoop;
UDPSocket mDataSocket; ///< socket for writing to/reading from GSM core
UDPSocket mControlSocket; ///< socket for writing/reading control commands from GSM core
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
- VectorQueue mTransmitPriorityQueue; ///< priority queue of transmit bursts received from GSM core
- VectorFIFO* mTransmitFIFO; ///< radioInterface FIFO of transmit bursts
+ VectorQueue *mTransmitPriorityQueue; ///< priority queue of transmit bursts received from GSM core
VectorFIFO* mReceiveFIFO; ///< radioInterface FIFO of receive bursts
Thread *mFIFOServiceLoopThread; ///< thread to push/pull bursts into transmit/receive FIFO
Thread *mControlServiceLoopThread; ///< thread to process control messages from GSM core
Thread *mTransmitPriorityQueueServiceLoopThread;///< thread to process transmit bursts from GSM core
- GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
+ int mChannel; ///< channelizer attach number between 0 and 'M-1'
+
+ GSM::Time *mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
RadioInterface *mRadioInterface; ///< associated radioInterface object
double txFullScale; ///< full scale input to radio
double rxFullScale; ///< full scale output to radio
- /** Codes for burst types of received bursts*/
- typedef enum {
- OFF, ///< timeslot is off
- TSC, ///< timeslot should contain a normal burst
- RACH, ///< timeslot should contain an access burst
- IDLE ///< timeslot is an idle (or dummy) burst
- } CorrType;
-
-
- /** Codes for channel combinations */
- typedef enum {
- FILL, ///< Channel is transmitted, but unused
- I, ///< TCH/FS
- II, ///< TCH/HS, idle every other slot
- III, ///< TCH/HS
- IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH
- V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
- VI, ///< CCCH+BCCH, uplink RACH
- VII, ///< SDCCH/8 + SACCH/8
- VIII, ///< TCH/F + FACCH/F + SACCH/M
- IX, ///< TCH/F + SACCH/M
- X, ///< TCH/FD + SACCH/MD
- XI, ///< PBCCH+PCCCH+PDTCH+PACCH+PTCCH
- XII, ///< PCCCH+PDTCH+PACCH+PTCCH
- XIII, ///< PDTCH+PACCH+PTCCH
- NONE, ///< Channel is inactive, default
- LOOPBACK ///< similar go VII, used in loopback testing
- } ChannelCombination;
-
-
/** unmodulate a modulated burst */
#ifdef TRANSMIT_LOGGING
void unModulateVector(signalVector wVector);
@@ -115,29 +85,22 @@ private:
int &RSSI,
int &timingOffset);
- /** Set modulus for specific timeslot */
- void setModulus(int timeslot);
-
- /** return the expected burst type for the specified timestamp */
- CorrType expectedCorrType(GSM::Time currTime);
-
/** send messages over the clock socket */
void writeClockInterface(void);
+ void pullFIFO(void); ///< blocking call on receive FIFO
+
signalVector *gsmPulse; ///< the GSM shaping pulse for modulation
int mSamplesPerSymbol; ///< number of samples per GSM symbol
bool mOn; ///< flag to indicate that transceiver is powered on
- ChannelCombination mChanType[8]; ///< channel types for all timeslots
double mTxFreq; ///< the transmit frequency
double mRxFreq; ///< the receive frequency
int mPower; ///< the transmit power in dB
int mTSC; ///< the midamble sequence code
double mEnergyThreshold; ///< threshold to determine if received data is potentially a GSM burst
GSM::Time prevFalseDetectionTime; ///< last timestamp of a false energy detection
- int fillerModulus[8]; ///< modulus values of all timeslots, in frames
- signalVector *fillerTable[102][8]; ///< table of modulated filler waveforms for all timeslots
unsigned mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
GSM::Time channelEstimateTime[8]; ///< last timestamp of each timeslot's channel estimate
@@ -148,6 +111,8 @@ private:
float chanRespOffset[8]; ///< most recent timing offset, e.g. TOA, of all timeslots
complex chanRespAmplitude[8]; ///< most recent channel amplitude of all timeslots
+ bool mRadioLocked;
+
public:
/** Transceiver constructor
@@ -160,9 +125,9 @@ public:
Transceiver(int wBasePort,
const char *TRXAddress,
int wSamplesPerSymbol,
- GSM::Time wTransmitLatency,
- RadioInterface *wRadioInterface);
-
+ RadioInterface *wRadioInterface,
+ DriveLoop *wDriveLoop,
+ int wChannel);
/** Destructor */
~Transceiver();
@@ -173,7 +138,7 @@ public:
void receiveFIFO(VectorFIFO *wFIFO) { mReceiveFIFO = wFIFO;}
/** attach the radioInterface transmit FIFO */
- void transmitFIFO(VectorFIFO *wFIFO) { mTransmitFIFO = wFIFO;}
+ void transmitQueue(VectorQueue *wQ) { mTransmitPriorityQueue = wQ; }
protected:
diff --git a/Transceiver52M/radioVector.cpp b/Transceiver52M/radioVector.cpp
index f20d97a..dcdb375 100644
--- a/Transceiver52M/radioVector.cpp
+++ b/Transceiver52M/radioVector.cpp
@@ -2,7 +2,7 @@
* Written by Thomas Tsou <ttsou@vt.edu>
* Based on code by Harvind S Samra <hssamra@kestrelsp.com>
*
- * Copyright 2011 Free Software Foundation, Inc.
+ * Copyright 2011, 2012 Free Software Foundation, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -41,21 +41,6 @@ bool radioVector::operator>(const radioVector& other) const
return mTime > other.mTime;
}
-unsigned VectorFIFO::size()
-{
- return mQ.size();
-}
-
-void VectorFIFO::put(radioVector *ptr)
-{
- mQ.put((void*) ptr);
-}
-
-radioVector *VectorFIFO::get()
-{
- return (radioVector*) mQ.get();
-}
-
GSM::Time VectorQueue::nextTime() const
{
GSM::Time retVal;
diff --git a/Transceiver52M/radioVector.h b/Transceiver52M/radioVector.h
index 8de4493..c41a8a8 100644
--- a/Transceiver52M/radioVector.h
+++ b/Transceiver52M/radioVector.h
@@ -2,7 +2,7 @@
* Written by Thomas Tsou <ttsou@vt.edu>
* Based on code by Harvind S Samra <hssamra@kestrelsp.com>
*
- * Copyright 2011 Free Software Foundation, Inc.
+ * Copyright 2011, 2012 Free Software Foundation, Inc.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
@@ -37,14 +37,7 @@ private:
GSM::Time mTime;
};
-class VectorFIFO {
-public:
- unsigned size();
- void put(radioVector *ptr);
- radioVector *get();
-
-private:
- PointerFIFO mQ;
+class VectorFIFO : public InterthreadQueue<radioVector> {
};
class VectorQueue : public InterthreadPriorityQueue<radioVector> {