Transceiver52M: Separate transceiver per-slot state information

Collect the slot information into an indpendent state object. This
will allow us to easily create multiple instances of internal state
variables without having to replicate the transceiver object itself.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
diff --git a/Transceiver52M/Transceiver.cpp b/Transceiver52M/Transceiver.cpp
index 7a1160d..fd6a008 100644
--- a/Transceiver52M/Transceiver.cpp
+++ b/Transceiver52M/Transceiver.cpp
@@ -42,6 +42,38 @@
 /* Number of running values use in noise average */
 #define NOISE_CNT			20
 
+TransceiverState::TransceiverState()
+{
+  for (int i = 0; i < 8; i++) {
+    chanType[i] = Transceiver::NONE;
+    fillerModulus[i] = 26;
+    chanResponse[i] = NULL;
+    DFEForward[i] = NULL;
+    DFEFeedback[i] = NULL;
+
+    for (int n = 0; n < 102; n++)
+      fillerTable[n][i] = NULL;
+  }
+}
+
+TransceiverState::~TransceiverState()
+{
+  for (int i = 0; i < 8; i++) {
+    delete chanResponse[i];
+    delete DFEForward[i];
+    delete DFEFeedback[i];
+
+    for (int n = 0; n < 102; n++)
+      delete fillerTable[n][i];
+  }
+}
+
+void TransceiverState::init(size_t slot, signalVector *burst)
+{
+  for (int i = 0; i < 102; i++)
+    fillerTable[i][slot] = new signalVector(*burst);
+}
+
 Transceiver::Transceiver(int wBasePort,
 			 const char *TRXAddress,
 			 int wSPS,
@@ -88,6 +120,8 @@
 
 bool Transceiver::init()
 {
+  signalVector *burst;
+
   if (!sigProcLibSetup(mSPSTx)) {
     LOG(ALERT) << "Failed to initialize signal processing library";
     return false;
@@ -97,29 +131,13 @@
   mCtrlSocket = new UDPSocket(mBasePort + 1, mAddr.c_str(), mBasePort + 101);
   mDataSocket = new UDPSocket(mBasePort + 2, mAddr.c_str(), mBasePort + 102);
 
-  // initialize filler tables with dummy bursts
-  for (int i = 0; i < 8; i++) {
-    signalVector* modBurst = modulateBurst(gDummyBurst,
-					   8 + (i % 4 == 0),
-					   mSPSTx);
-    if (!modBurst) {
-      sigProcLibDestroy();
-      LOG(ALERT) << "Failed to initialize filler table";
-      return false;
-    }
+  for (size_t n = 0; n < 8; n++) {
+    burst = modulateBurst(gDummyBurst, 8 + (n % 4 == 0), mSPSTx);
+    scaleVector(*burst, txFullScale);
+    mState.init(n, burst);
+    mState.chanEstimateTime[n] = mTransmitDeadlineClock;
 
-    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] = mTransmitDeadlineClock;
+    delete burst;
   }
 
   return true;
@@ -150,49 +168,50 @@
     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];
-    delete fillerTable[modFN][TN];
-    fillerTable[modFN][TN] = staleBurst;
+    int modFN = nextTime.FN() % mState.fillerModulus[TN];
+    delete mState.fillerTable[modFN][TN];
+    mState.fillerTable[modFN][TN] = staleBurst;
   }
   
   int TN = nowTime.TN();
-  int modFN = nowTime.FN() % fillerModulus[nowTime.TN()];
+  int modFN = nowTime.FN() % mState.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 mState.fillerTable[modFN][TN];
+    mState.fillerTable[modFN][TN] = new signalVector(*(next));
+    mRadioInterface->driveTransmitRadio(*(next), mState.chanType[TN] == NONE);
     delete next;
     return;
   }
 
   // otherwise, pull filler data, and push to radio FIFO
-  mRadioInterface->driveTransmitRadio(*(fillerTable[modFN][TN]),(mChanType[TN]==NONE));
+  mRadioInterface->driveTransmitRadio(*(mState.fillerTable[modFN][TN]),
+                                      mState.chanType[TN]==NONE);
 }
 
 void Transceiver::setModulus(int timeslot)
 {
-  switch (mChanType[timeslot]) {
+  switch (mState.chanType[timeslot]) {
   case NONE:
   case I:
   case II:
   case III:
   case FILL:
-    fillerModulus[timeslot] = 26;
+    mState.fillerModulus[timeslot] = 26;
     break;
   case IV:
   case VI:
   case V:
-    fillerModulus[timeslot] = 51;
+    mState.fillerModulus[timeslot] = 51;
     break;
     //case V: 
   case VII:
-    fillerModulus[timeslot] = 102;
+    mState.fillerModulus[timeslot] = 102;
     break;
   case XIII:
-    fillerModulus[timeslot] = 52;
+    mState.fillerModulus[timeslot] = 52;
     break;
   default:
     break;
@@ -206,7 +225,7 @@
   unsigned burstTN = currTime.TN();
   unsigned burstFN = currTime.FN();
 
-  switch (mChanType[burstTN]) {
+  switch (mState.chanType[burstTN]) {
   case NONE:
     return OFF;
     break;
@@ -275,10 +294,10 @@
 				      int &RSSI,
 				      int &timingOffset)
 {
-  bool needDFE = false;
-  bool success = false;
-  complex amplitude = 0.0;
+  bool needDFE = false, success = false, estimateChannel = false;
+  complex amp = 0.0;
   float TOA = 0.0, avg = 0.0;
+  signalVector *chanResponse;
 
   radioVector *rxBurst = (radioVector *) mReceiveFIFO->get();
 
@@ -304,52 +323,55 @@
   // run the proper correlator
   if (corrType==TSC) {
     LOG(DEBUG) << "looking for TSC at time: " << rxBurst->getTime();
-    signalVector *channelResp;
-    double framesElapsed = rxBurst->getTime()-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;
+    double framesElapsed = rxBurst->getTime() - mState.chanEstimateTime[timeslot];
+    if ((framesElapsed > 50) || (!mState.chanResponse[timeslot])) {
+      delete mState.chanResponse[timeslot];
+      delete mState.DFEForward[timeslot];
+      delete mState.DFEFeedback[timeslot];
+
+      mState.chanResponse[timeslot] = NULL;
+      mState.DFEForward[timeslot] = NULL;
+      mState.DFEFeedback[timeslot] = NULL;
+
+      if (needDFE)
+        estimateChannel = true;
     }
-    if (!needDFE) estimateChannel = false;
+
     float chanOffset;
     success = analyzeTrafficBurst(*vectorBurst,
 				  mTSC,
 				  5.0,
 				  mSPSRx,
-				  &amplitude,
+				  &amp,
 				  &TOA,
 				  mMaxExpectedDelay, 
 				  estimateChannel,
-				  &channelResp,
+				  &chanResponse,
 				  &chanOffset);
     if (success) {
-      SNRestimate[timeslot] = amplitude.norm2()/(mNoiseLev*mNoiseLev+1.0); // this is not highly accurate
+      mState.SNRestimate[timeslot] = amp.norm2() / (mNoiseLev * mNoiseLev+1.0);
       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->getTime();  
-         LOG(DEBUG) << "SNR: " << SNRestimate[timeslot] << ", DFE forward: " << *DFEForward[timeslot] << ", DFE backward: " << *DFEFeedback[timeslot];
+         mState.chanResponse[timeslot] = chanResponse;
+       	 mState.chanRespOffset[timeslot] = chanOffset;
+         mState.chanRespAmplitude[timeslot] = amp;
+	 scaleVector(*chanResponse, complex(1.0,0.0) / amp);
+
+         designDFE(*chanResponse, mState.SNRestimate[timeslot], 7,
+                   &mState.DFEForward[timeslot],
+                   &mState.DFEFeedback[timeslot]);
+
+         mState.chanEstimateTime[timeslot] = rxBurst->getTime();
       }
     }
     else {
-      channelResponse[timeslot] = NULL;
+      mState.chanResponse[timeslot] = NULL;
       mNoises.insert(avg);
     }
   }
   else {
     // RACH burst
-    if (success = detectRACHBurst(*vectorBurst, 6.0, mSPSRx, &amplitude, &TOA))
-      channelResponse[timeslot] = NULL;
+    if (success = detectRACHBurst(*vectorBurst, 6.0, mSPSRx, &amp, &TOA))
+      mState.chanResponse[timeslot] = NULL;
     else
       mNoises.insert(avg);
   }
@@ -358,14 +380,14 @@
   SoftVector *burst = NULL;
   if ((rxBurst) && (success)) {
     if ((corrType==RACH) || (!needDFE)) {
-      burst = demodulateBurst(*vectorBurst, mSPSRx, amplitude, TOA);
+      burst = demodulateBurst(*vectorBurst, mSPSRx, amp, TOA);
     } else {
-      scaleVector(*vectorBurst,complex(1.0,0.0)/amplitude);
+      scaleVector(*vectorBurst,complex(1.0,0.0) / amp);
       burst = equalizeBurst(*vectorBurst,
-			    TOA-chanRespOffset[timeslot],
-			    mSPSRx,
-			    *DFEForward[timeslot],
-			    *DFEFeedback[timeslot]);
+                            TOA - mState.chanRespOffset[timeslot],
+                            mSPSRx,
+                            *mState.DFEForward[timeslot],
+                            *mState.DFEFeedback[timeslot]);
     }
     wTime = rxBurst->getTime();
     RSSI = (int) floor(20.0*log10(rxFullScale/avg));
@@ -542,7 +564,7 @@
       sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
       return;
     }     
-    mChanType[timeslot] = (ChannelCombination) corrCode;
+    mState.chanType[timeslot] = corrCode;
     setModulus(timeslot);
     sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
 
diff --git a/Transceiver52M/Transceiver.h b/Transceiver52M/Transceiver.h
index 4b549ef..1f8bed2 100644
--- a/Transceiver52M/Transceiver.h
+++ b/Transceiver52M/Transceiver.h
@@ -30,6 +30,54 @@
 #include <sys/types.h>
 #include <sys/socket.h>
 
+class Transceiver;
+
+/** Channel descriptor for transceiver object and channel number pair */
+struct TransceiverChannel {
+  TransceiverChannel(Transceiver *trx, int num)
+  {
+    this->trx = trx;
+    this->num = num;
+  }
+
+  ~TransceiverChannel()
+  {
+  }
+
+  Transceiver *trx;
+  size_t num;
+};
+
+/** Internal transceiver state variables */
+struct TransceiverState {
+  TransceiverState();
+  ~TransceiverState();
+
+  /* Initialize a multiframe slot in the filler table */
+  void init(size_t slot, signalVector *burst);
+
+  int chanType[8];
+
+  /* Last timestamp of each timeslot's channel estimate */
+  GSM::Time chanEstimateTime[8];
+
+  /* The filler table */
+  signalVector *fillerTable[102][8];
+  int fillerModulus[8];
+
+  /* Most recent channel estimate of all timeslots */
+  signalVector *chanResponse[8];
+
+  /* Most recent DFE feedback filter of all timeslots */
+  signalVector *DFEForward[8];
+  signalVector *DFEFeedback[8];
+
+  /* Most recent SNR, timing, and channel amplitude estimates */
+  float SNRestimate[8];
+  float chanRespOffset[8];
+  complex chanRespAmplitude[8];
+};
+
 /** The Transceiver class, responsible for physical layer of basestation */
 class Transceiver {
 private:
@@ -66,27 +114,6 @@
     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;
-
   float mNoiseLev;      ///< Average noise level
   noiseVector mNoises;  ///< Vector holding running noise measurements
 
@@ -116,22 +143,13 @@
   int mSPSRx;                          ///< number of samples per Rx 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
   unsigned mTSC;                       ///< the midamble sequence code
-  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
-  signalVector *channelResponse[8];    ///< most recent channel estimate of all timeslots
-  float        SNRestimate[8];         ///< most recent SNR estimate of all timeslots
-  signalVector *DFEForward[8];         ///< most recent DFE feedforward filter of all timeslots
-  signalVector *DFEFeedback[8];        ///< most recent DFE feedback filter of all timeslots
-  float        chanRespOffset[8];      ///< most recent timing offset, e.g. TOA, of all timeslots
-  complex      chanRespAmplitude[8];   ///< most recent channel amplitude of all timeslots
+  TransceiverState mState;
 
 public:
 
@@ -161,6 +179,26 @@
   /** attach the radioInterface transmit FIFO */
   void transmitFIFO(VectorFIFO *wFIFO) { mTransmitFIFO = wFIFO;}
 
+  /** 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;
+
 protected:
 
   /** drive reception and demodulation of GSM bursts */