Transceiver52M: Setup dual sample rate transceiver

This patch applies oversampling, when selected with 4 sps,
to the downlink only, while running the receiver with
minimal sampling at 1 sps. These split sample rates allow
us to run a highly accurate downlink signal with minimal
distortion, while keeping receive path channel filtering
on the FPGA.

Without this patch, we oversample the receive path and
require a steep receive filter to get similar adjacent
channel suppression as the FPGA halfband / CIC filter
combination, which comes with a high computational cost.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
diff --git a/Transceiver52M/Transceiver.cpp b/Transceiver52M/Transceiver.cpp
index 03aa3c1..2557fec 100644
--- a/Transceiver52M/Transceiver.cpp
+++ b/Transceiver52M/Transceiver.cpp
@@ -55,7 +55,8 @@
 			 RadioInterface *wRadioInterface)
 	:mDataSocket(wBasePort+2,TRXAddress,wBasePort+102),
 	 mControlSocket(wBasePort+1,TRXAddress,wBasePort+101),
-	 mClockSocket(wBasePort,TRXAddress,wBasePort+100)
+	 mClockSocket(wBasePort,TRXAddress,wBasePort+100),
+	 mSPSTx(wSPS), mSPSRx(1)
 {
   GSM::Time startTime(random() % gHyperframe,0);
 
@@ -64,7 +65,6 @@
   mControlServiceLoopThread = new Thread(32768);       ///< thread to process control messages from GSM core
   mTransmitPriorityQueueServiceLoopThread = new Thread(32768);///< thread to process transmit bursts from GSM core
 
-  mSPS = wSPS;
   mRadioInterface = wRadioInterface;
   mTransmitLatency = wTransmitLatency;
   mTransmitDeadlineClock = startTime;
@@ -93,7 +93,7 @@
 
 bool Transceiver::init()
 {
-  if (!sigProcLibSetup(mSPS)) {
+  if (!sigProcLibSetup(mSPSTx)) {
     LOG(ALERT) << "Failed to initialize signal processing library";
     return false;
   }
@@ -102,7 +102,7 @@
   for (int i = 0; i < 8; i++) {
     signalVector* modBurst = modulateBurst(gDummyBurst,
 					   8 + (i % 4 == 0),
-					   mSPS);
+					   mSPSTx);
     if (!modBurst) {
       sigProcLibDestroy();
       LOG(ALERT) << "Failed to initialize filler table";
@@ -133,7 +133,7 @@
   // modulate and stick into queue 
   signalVector* modBurst = modulateBurst(burst,
 					 8 + (wTime.TN() % 4 == 0),
-					 mSPS);
+					 mSPSTx);
   scaleVector(*modBurst,txFullScale * pow(10,-RSSI/10));
   radioVector *newVec = new radioVector(*modBurst,wTime);
   mTransmitPriorityQueue.write(newVec);
@@ -144,7 +144,7 @@
 #ifdef TRANSMIT_LOGGING
 void Transceiver::unModulateVector(signalVector wVector) 
 {
-  SoftVector *burst = demodulateBurst(wVector, mSPS, 1.0, 0.0);
+  SoftVector *burst = demodulateBurst(wVector, mSPSTx, 1.0, 0.0);
   LOG(DEBUG) << "LOGGED BURST: " << *burst;
 
 /*
@@ -329,7 +329,7 @@
   float TOA = 0.0;
   float avgPwr = 0.0;
 #ifdef ENERGY_DETECT
-  if (!energyDetect(*vectorBurst, 20 * mSPS, mEnergyThreshold, &avgPwr)) {
+  if (!energyDetect(*vectorBurst, 20 * mSPSRx, mEnergyThreshold, &avgPwr)) {
      LOG(DEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->getTime();
      double framesElapsed = rxBurst->getTime()-prevFalseDetectionTime;
      if (framesElapsed > 50) {  // if we haven't had any false detections for a while, lower threshold
@@ -365,7 +365,7 @@
     success = analyzeTrafficBurst(*vectorBurst,
 				  mTSC,
 				  5.0,
-				  mSPS,
+				  mSPSRx,
 				  &amplitude,
 				  &TOA,
 				  mMaxExpectedDelay, 
@@ -398,11 +398,7 @@
   }
   else {
     // RACH burst
-    success = detectRACHBurst(*vectorBurst,
-			      6.0,
-			      mSPS,
-			      &amplitude,
-			      &TOA);
+    success = detectRACHBurst(*vectorBurst, 6.0, mSPSRx, &amplitude, &TOA);
     if (success) {
       LOG(DEBUG) << "FOUND RACH!!!!!! " << amplitude << " " << TOA;
       mEnergyThreshold -= (1.0F/10.0F);
@@ -421,22 +417,19 @@
   SoftVector *burst = NULL;
   if ((rxBurst) && (success)) {
     if ((corrType==RACH) || (!needDFE)) {
-      burst = demodulateBurst(*vectorBurst,
-			      mSPS,
-			      amplitude,TOA);
-    }
-    else { // TSC
+      burst = demodulateBurst(*vectorBurst, mSPSRx, amplitude, TOA);
+    } else {
       scaleVector(*vectorBurst,complex(1.0,0.0)/amplitude);
       burst = equalizeBurst(*vectorBurst,
 			    TOA-chanRespOffset[timeslot],
-			    mSPS,
+			    mSPSRx,
 			    *DFEForward[timeslot],
 			    *DFEFeedback[timeslot]);
     }
     wTime = rxBurst->getTime();
     RSSI = (int) floor(20.0*log10(rxFullScale/amplitude.abs()));
     LOG(DEBUG) << "RSSI: " << RSSI;
-    timingOffset = (int) round(TOA * 256.0 / mSPS);
+    timingOffset = (int) round(TOA * 256.0 / mSPSRx);
   }
 
   //if (burst) LOG(DEBUG) << "burst: " << *burst << '\n';
@@ -595,7 +588,7 @@
       sprintf(response,"RSP SETTSC 1 %d",TSC);
     else {
       mTSC = TSC;
-      generateMidamble(mSPS, TSC);
+      generateMidamble(mSPSRx, TSC);
       sprintf(response,"RSP SETTSC 0 %d", TSC);
     }
   }
diff --git a/Transceiver52M/Transceiver.h b/Transceiver52M/Transceiver.h
index 3e4f784..d243214 100644
--- a/Transceiver52M/Transceiver.h
+++ b/Transceiver52M/Transceiver.h
@@ -125,7 +125,8 @@
   /** send messages over the clock socket */
   void writeClockInterface(void);
 
-  int mSPS;                            ///< number of samples per GSM symbol
+  int mSPSTx;                          ///< number of samples per Tx symbol
+  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
diff --git a/Transceiver52M/UHDDevice.cpp b/Transceiver52M/UHDDevice.cpp
index 2bd767c..ad821de 100644
--- a/Transceiver52M/UHDDevice.cpp
+++ b/Transceiver52M/UHDDevice.cpp
@@ -66,14 +66,14 @@
 static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
 	{ USRP1, 1, 0.0 },
 	{ USRP1, 4, 0.0 },
-	{ USRP2, 1, 5.4394e-5 },
-	{ USRP2, 4, 0.0 },
-	{ B100,  1, 9.4778e-5 },
-	{ B100,  4, 2.9418e-5 },
-	{ B200,  1, 0.0 },
-	{ B200,  4, 9.8358e-5 },
-	{ UMTRX, 1, 9.4778e-5 },
-	{ UMTRX, 4, 0.0 },
+	{ USRP2, 1, 1.1815e-4 },
+	{ USRP2, 4, 7.7538e-5 },
+	{ B100,  1, 9.9692e-5 },
+	{ B100,  4, 6.5545e-5 },
+	{ B200,  1, 9.6000e-5 },
+	{ B200,  4, 6.4615e-5 },
+	{ UMTRX, 1, 9.9692e-5 },
+	{ UMTRX, 4, 7.3846e-5 },
 };
 
 static double get_dev_offset(enum uhd_dev_type type, int sps)
@@ -248,7 +248,7 @@
 	double getTxFreq() { return tx_freq; }
 	double getRxFreq() { return rx_freq; }
 
-	inline double getSampleRate() { return actual_smpl_rt; }
+	inline double getSampleRate() { return tx_rate; }
 	inline double numberRead() { return rx_pkt_cnt; }
 	inline double numberWritten() { return 0; }
 
@@ -271,7 +271,7 @@
 	enum uhd_dev_type dev_type;
 
 	int sps;
-	double desired_smpl_rt, actual_smpl_rt;
+	double tx_rate, rx_rate;
 
 	double tx_gain, tx_gain_min, tx_gain_max;
 	double rx_gain, rx_gain_min, rx_gain_max;
@@ -293,7 +293,7 @@
 	void init_gains();
 	void set_ref_clk(bool ext_clk);
 	int set_master_clk(double rate);
-	int set_rates(double rate);
+	int set_rates(double tx_rate, double rx_rate);
 	bool parse_dev_type();
 	bool flush_recv(size_t num_pkts);
 	int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
@@ -407,7 +407,7 @@
 	return 0;
 }
 
-int uhd_device::set_rates(double rate)
+int uhd_device::set_rates(double tx_rate, double rx_rate)
 {
 	double offset_limit = 1.0;
 	double tx_offset, rx_offset;
@@ -420,21 +420,22 @@
 
 	// Set sample rates
 	try {
-		usrp_dev->set_tx_rate(rate);
-		usrp_dev->set_rx_rate(rate);
+		usrp_dev->set_tx_rate(tx_rate);
+		usrp_dev->set_rx_rate(rx_rate);
 	} catch (const std::exception &ex) {
-		LOG(ALERT) << "UHD rate setting failed: " << rate;
+		LOG(ALERT) << "UHD rate setting failed";
 		LOG(ALERT) << ex.what();
 		return -1;
 	}
-	actual_smpl_rt = usrp_dev->get_tx_rate();
+	this->tx_rate = usrp_dev->get_tx_rate();
+	this->rx_rate = usrp_dev->get_rx_rate();
 
-	tx_offset = fabs(usrp_dev->get_tx_rate() - rate);
-	rx_offset = fabs(usrp_dev->get_rx_rate() - rate);
+	tx_offset = fabs(this->tx_rate - tx_rate);
+	rx_offset = fabs(this->rx_rate - rx_rate);
 	if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
 		LOG(ALERT) << "Actual sample rate differs from desired rate";
-		LOG(ALERT) << "Tx/Rx (" << usrp_dev->get_rx_rate() << "/"
-			   << usrp_dev->get_rx_rate() << ")";
+		LOG(ALERT) << "Tx/Rx (" << this->tx_rate << "/"
+			   << this->rx_rate << ")";
 		return -1;
 	}
 
@@ -552,13 +553,14 @@
 	rx_spp = rx_stream->get_max_num_samps();
 
 	// Set rates
-	desired_smpl_rt = select_rate(dev_type, sps);
-	if ((desired_smpl_rt > 0.0) && (set_rates(desired_smpl_rt) < 0))
+	double _tx_rate = select_rate(dev_type, sps);
+	double _rx_rate = _tx_rate / sps;
+	if ((_tx_rate > 0.0) && (set_rates(_tx_rate, _rx_rate) < 0))
 		return -1;
 
 	// Create receive buffer
 	size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
-	rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
+	rx_smpl_buf = new smpl_buf(buf_len, rx_rate);
 
 	// Set receive chain sample offset 
 	double offset = get_dev_offset(dev_type, sps);
@@ -566,7 +568,7 @@
 		LOG(ERR) << "Unsupported configuration, no correction applied";
 		ts_offset = 0;
 	} else  {
-		ts_offset = (TIMESTAMP) (offset * actual_smpl_rt);
+		ts_offset = (TIMESTAMP) (offset * rx_rate);
 	}
 
 	// Initialize and shadow gain values 
@@ -719,7 +721,7 @@
 	// Shift read time with respect to transmit clock
 	timestamp += ts_offset;
 
-	ts = convert_time(timestamp, actual_smpl_rt);
+	ts = convert_time(timestamp, rx_rate);
 	LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
 
 	// Check that timestamp is valid
@@ -788,7 +790,7 @@
 	metadata.has_time_spec = true;
 	metadata.start_of_burst = false;
 	metadata.end_of_burst = false;
-	metadata.time_spec = convert_time(timestamp, actual_smpl_rt);
+	metadata.time_spec = convert_time(timestamp, tx_rate);
 
 	// No control packets
 	if (isControl) {
diff --git a/Transceiver52M/USRPDevice.cpp b/Transceiver52M/USRPDevice.cpp
index 5c99003..275f1c4 100644
--- a/Transceiver52M/USRPDevice.cpp
+++ b/Transceiver52M/USRPDevice.cpp
@@ -63,10 +63,25 @@
   : skipRx(skipRx)
 {
   LOG(INFO) << "creating USRP device...";
+
+  this->sps = sps;
   decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps));
   actualSampleRate = masterClockRate/decimRate;
   rxGain = 0;
 
+  /*
+   * Undetermined delay b/w ping response timestamp and true
+   * receive timestamp. Values are empirically measured. With
+   * split sample rate Tx/Rx - 4/1 sps we need to need to
+   * compensate for advance rather than delay.
+   */
+  if (sps == 1)
+    pingOffset = 272;
+  else if (sps == 4)
+    pingOffset = 269 - 7500;
+  else
+    pingOffset = 0;
+
 #ifdef SWLOOPBACK 
   samplePeriod = 1.0e6/actualSampleRate;
   loopbackBufferSize = 0;
@@ -86,9 +101,10 @@
   m_uRx.reset();
   if (!skipRx) {
   try {
-    m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(0,decimRate,1,-1,
-                                                         usrp_standard_rx::FPGA_MODE_NORMAL,
-                                                         1024,16*8,rbf));
+    m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make(
+                                        0, decimRate * sps, 1, -1,
+                                        usrp_standard_rx::FPGA_MODE_NORMAL,
+                                        1024, 16 * 8, rbf));
 #ifdef HAVE_LIBUSRP_3_2
     m_uRx->set_fpga_master_clock_freq(masterClockRate);
 #endif
@@ -110,8 +126,9 @@
   }
 
   try {
-    m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(0,decimRate*2,1,-1,
-                                                         1024,16*8,rbf));
+    m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make(
+                                        0, decimRate * 2, 1, -1,
+                                        1024, 16 * 8, rbf));
 #ifdef HAVE_LIBUSRP_3_2
     m_uTx->set_fpga_master_clock_freq(masterClockRate);
 #endif
@@ -341,7 +358,7 @@
         uint32_t word2 = usrp_to_host_u32(tmpBuf[2]);
 	if ((word2 >> 16) == ((0x01 << 8) | 0x02)) {
           timestamp -= timestampOffset;
-	  timestampOffset = pktTimestamp - pingTimestamp + PINGOFFSET;
+	  timestampOffset = pktTimestamp - pingTimestamp + pingOffset;
 	  LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset;
           timestamp += timestampOffset;
 	  isAligned = true;
diff --git a/Transceiver52M/USRPDevice.h b/Transceiver52M/USRPDevice.h
index f5bc939..01cf261 100644
--- a/Transceiver52M/USRPDevice.h
+++ b/Transceiver52M/USRPDevice.h
@@ -60,6 +60,7 @@
   usrp_subdev_spec rxSubdevSpec;
   usrp_subdev_spec txSubdevSpec;
 
+  int sps;
   double actualSampleRate;	///< the actual USRP sampling rate
   unsigned int decimRate;	///< the USRP decimation rate
 
@@ -87,7 +88,8 @@
   TIMESTAMP timestampOffset;       ///< timestamp offset b/w Tx and Rx blocks
   TIMESTAMP latestWriteTimestamp;  ///< timestamp of most recent ping command
   TIMESTAMP pingTimestamp;	   ///< timestamp of most recent ping response
-  static const TIMESTAMP PINGOFFSET = 272;  ///< undetermined delay b/w ping response timestamp and true receive timestamp
+
+  long long  pingOffset;
   unsigned long hi32Timestamp;
   unsigned long lastPktTimestamp;
 
diff --git a/Transceiver52M/radioInterface.cpp b/Transceiver52M/radioInterface.cpp
index f39a470..e9fcd49 100644
--- a/Transceiver52M/radioInterface.cpp
+++ b/Transceiver52M/radioInterface.cpp
@@ -23,13 +23,15 @@
 */
 
 #include "radioInterface.h"
+#include "Resampler.h"
 #include <Logger.h>
 
 extern "C" {
 #include "convert.h"
 }
 
-bool started = false;
+#define INCHUNK		(625 * SAMPSPERSYM)
+#define OUTCHUNK	(625 * SAMPSPERSYM)
 
 RadioInterface::RadioInterface(RadioDevice *wRadio,
 			       int wReceiveOffset,
@@ -37,7 +39,7 @@
 			       GSM::Time wStartTime)
   : underrun(false), sendCursor(0), recvCursor(0), mOn(false),
     mRadio(wRadio), receiveOffset(wReceiveOffset),
-    sps(wSPS), powerScaling(1.0),
+    mSPSTx(wSPS), mSPSRx(1), powerScaling(1.0),
     loadTest(false), sendBuffer(NULL), recvBuffer(NULL),
     convertRecvBuffer(NULL), convertSendBuffer(NULL)
 {
@@ -209,8 +211,8 @@
   // while there's enough data in receive buffer, form received 
   //    GSM bursts and pass up to Transceiver
   // Using the 157-156-156-156 symbols per timeslot format.
-  while (rcvSz > (symbolsPerSlot + (tN % 4 == 0)) * sps) {
-    signalVector rxVector((symbolsPerSlot + (tN % 4 == 0)) * sps);
+  while (rcvSz > (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx) {
+    signalVector rxVector((symbolsPerSlot + (tN % 4 == 0)) * mSPSRx);
     unRadioifyVector((float *) (recvBuffer->begin() + readSz), rxVector);
     GSM::Time tmpTime = rcvClock;
     if (rcvClock.FN() >= 0) {
@@ -228,8 +230,8 @@
     }
     mClock.incTN(); 
     rcvClock.incTN();
-    readSz += (symbolsPerSlot+(tN % 4 == 0)) * sps;
-    rcvSz -= (symbolsPerSlot+(tN % 4 == 0)) * sps;
+    readSz += (symbolsPerSlot+(tN % 4 == 0)) * mSPSRx;
+    rcvSz -= (symbolsPerSlot+(tN % 4 == 0)) * mSPSRx;
 
     tN = rcvClock.TN();
   }
@@ -267,33 +269,35 @@
     return -1;
 }
 
-/* Receive a timestamped chunk from the device */ 
+/* Receive a timestamped chunk from the device */
 void RadioInterface::pullBuffer()
 {
   bool local_underrun;
-  int num_recv;
+  int num_recv, len = OUTCHUNK / mSPSTx;
+  float *output;
 
-  /* Outer buffer access size is fixed */ 
+  /* Outer buffer access size is fixed */
   num_recv = mRadio->readSamples(convertRecvBuffer,
-                                 OUTCHUNK,
+                                 len,
                                  &overrun,
                                  readTimestamp,
                                  &local_underrun);
-  if (num_recv != OUTCHUNK) {
+  if (num_recv != len) {
           LOG(ALERT) << "Receive error " << num_recv;
           return;
   }
 
-  convert_short_float((float *) (recvBuffer->begin() + recvCursor),
-                      convertRecvBuffer, 2 * OUTCHUNK);
+  output = (float *) (recvBuffer->begin() + recvCursor);
+
+  convert_short_float(output, convertRecvBuffer, 2 * len);
 
   underrun |= local_underrun;
-  readTimestamp += num_recv;
 
+  readTimestamp += num_recv;
   recvCursor += num_recv;
 }
 
-/* Send timestamped chunk to the device with arbitrary size */ 
+/* Send timestamped chunk to the device with arbitrary size */
 void RadioInterface::pushBuffer()
 {
   int num_sent;
diff --git a/Transceiver52M/radioInterface.h b/Transceiver52M/radioInterface.h
index 2a83417..98d0f9d 100644
--- a/Transceiver52M/radioInterface.h
+++ b/Transceiver52M/radioInterface.h
@@ -23,8 +23,6 @@
 
 /** samples per GSM symbol */
 #define SAMPSPERSYM 4
-#define INCHUNK    (625)
-#define OUTCHUNK   (625)
 
 static const unsigned gSlotLen = 148;      ///< number of symbols per slot, not counting guard periods
 
@@ -38,7 +36,9 @@
   VectorFIFO  mReceiveFIFO;		      ///< FIFO that holds receive  bursts
 
   RadioDevice *mRadio;			      ///< the USRP object
- 
+
+  int mSPSTx;
+  int mSPSRx;
   signalVector *sendBuffer;
   signalVector *recvBuffer;
   unsigned sendCursor;
@@ -54,7 +54,6 @@
 
   RadioClock mClock;                          ///< the basestation clock!
 
-  int sps;                                    ///< samples per GSM symbol
   int receiveOffset;                          ///< offset b/w transmit and receive GSM timestamps, in timeslots
 
   bool mOn;				      ///< indicates radio is on
diff --git a/Transceiver52M/radioInterfaceResamp.cpp b/Transceiver52M/radioInterfaceResamp.cpp
index 03cde30..40bf32d 100644
--- a/Transceiver52M/radioInterfaceResamp.cpp
+++ b/Transceiver52M/radioInterfaceResamp.cpp
@@ -28,14 +28,6 @@
 #include "convert.h"
 }
 
-/* New chunk sizes for resampled rate */
-#ifdef INCHUNK
-  #undef INCHUNK
-#endif
-#ifdef OUTCHUNK
-  #undef OUTCHUNK
-#endif
-
 /* Resampling parameters for 100 MHz clocking */
 #define RESAMP_INRATE			52
 #define RESAMP_OUTRATE			75
@@ -104,7 +96,7 @@
 		cutoff = RESAMP_TX4_FILTER;
 
 	dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
-	if (!dnsampler->init(cutoff)) {
+	if (!dnsampler->init()) {
 		LOG(ALERT) << "Rx resampler failed to initialize";
 		return false;
 	}
@@ -121,10 +113,10 @@
 	 * and requires headroom equivalent to the filter length. Low
 	 * rate buffers are allocated in the main radio interface code.
 	 */
-	innerSendBuffer = new signalVector(INCHUNK * 20, RESAMP_FILT_LEN);
+	innerSendBuffer = new signalVector(INCHUNK * 20, upsampler->len());
 	outerSendBuffer = new signalVector(OUTCHUNK * 20);
 
-	outerRecvBuffer = new signalVector(OUTCHUNK * 2, RESAMP_FILT_LEN);
+	outerRecvBuffer = new signalVector(OUTCHUNK * 2, dnsampler->len());
 	innerRecvBuffer = new signalVector(INCHUNK * 20);
 
 	convertSendBuffer = new short[OUTCHUNK * 2 * 20];
diff --git a/Transceiver52M/sigProcLib.cpp b/Transceiver52M/sigProcLib.cpp
index ab771ac..aa35647 100644
--- a/Transceiver52M/sigProcLib.cpp
+++ b/Transceiver52M/sigProcLib.cpp
@@ -44,9 +44,11 @@
 static const float M_2PI_F = (float)(2.0*M_PI);
 static const float M_1_2PI_F = 1/M_2PI_F;
 
-/** Static vectors that contain a precomputed +/- f_b/4 sinusoid */ 
-signalVector *GMSKRotation = NULL;
-signalVector *GMSKReverseRotation = NULL;
+/* Precomputed rotation vectors */
+static signalVector *GMSKRotationN = NULL;
+static signalVector *GMSKReverseRotationN = NULL;
+static signalVector *GMSKRotation1 = NULL;
+static signalVector *GMSKReverseRotation1 = NULL;
 
 /*
  * RACH and midamble correlation waveforms. Store the buffer separately
@@ -67,7 +69,7 @@
 
   signalVector *sequence;
   void         *buffer;
-  float        TOA;
+  float        toa;
   complex      gain;
 };
 
@@ -101,6 +103,7 @@
 CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
 CorrelationSequence *gRACHSequence = NULL;
 PulseSequence *GSMPulse = NULL;
+PulseSequence *GSMPulse1 = NULL;
 
 void sigProcLibDestroy()
 {
@@ -109,15 +112,21 @@
     gMidambles[i] = NULL;
   }
 
-  delete GMSKRotation;
-  delete GMSKReverseRotation;
+  delete GMSKRotationN;
+  delete GMSKReverseRotationN;
+  delete GMSKRotation1;
+  delete GMSKReverseRotation1;
   delete gRACHSequence;
   delete GSMPulse;
+  delete GSMPulse1;
 
-  GMSKRotation = NULL;
-  GMSKReverseRotation = NULL;
+  GMSKRotationN = NULL;
+  GMSKRotation1 = NULL;
+  GMSKReverseRotationN = NULL;
+  GMSKReverseRotation1 = NULL;
   gRACHSequence = NULL;
   GSMPulse = NULL;
+  GSMPulse1 = NULL;
 }
 
 // dB relative to 1.0.
@@ -250,38 +259,38 @@
 
 void initGMSKRotationTables(int sps)
 {
-  GMSKRotation = new signalVector(157 * sps);
-  GMSKReverseRotation = new signalVector(157 * sps);
-  signalVector::iterator rotPtr = GMSKRotation->begin();
-  signalVector::iterator revPtr = GMSKReverseRotation->begin();
+  GMSKRotationN = new signalVector(157 * sps);
+  GMSKReverseRotationN = new signalVector(157 * sps);
+  signalVector::iterator rotPtr = GMSKRotationN->begin();
+  signalVector::iterator revPtr = GMSKReverseRotationN->begin();
   float phase = 0.0;
-  while (rotPtr != GMSKRotation->end()) {
+  while (rotPtr != GMSKRotationN->end()) {
     *rotPtr++ = expjLookup(phase);
     *revPtr++ = expjLookup(-phase);
     phase += M_PI_F / 2.0F / (float) sps;
   }
-}
 
-bool sigProcLibSetup(int sps)
-{
-  if ((sps != 1) && (sps != 4))
-    return false;
-
-  initTrigTables();
-  initGMSKRotationTables(sps);
-  generateGSMPulse(sps, 2);
-
-  if (!generateRACHSequence(sps)) {
-    sigProcLibDestroy();
-    return false;
+  GMSKRotation1 = new signalVector(157);
+  GMSKReverseRotation1 = new signalVector(157);
+  rotPtr = GMSKRotation1->begin();
+  revPtr = GMSKReverseRotation1->begin();
+  phase = 0.0;
+  while (rotPtr != GMSKRotation1->end()) {
+    *rotPtr++ = expjLookup(phase);
+    *revPtr++ = expjLookup(-phase);
+    phase += M_PI_F / 2.0F;
   }
-
-  return true;
 }
 
-void GMSKRotate(signalVector &x) {
-  signalVector::iterator xPtr = x.begin();
-  signalVector::iterator rotPtr = GMSKRotation->begin();
+static void GMSKRotate(signalVector &x, int sps)
+{
+  signalVector::iterator rotPtr, xPtr = x.begin();
+
+  if (sps == 1)
+    rotPtr = GMSKRotation1->begin();
+  else
+    rotPtr = GMSKRotationN->begin();
+
   if (x.isRealOnly()) {
     while (xPtr < x.end()) {
       *xPtr = *rotPtr++ * (xPtr->real());
@@ -296,9 +305,15 @@
   }
 }
 
-void GMSKReverseRotate(signalVector &x) {
-  signalVector::iterator xPtr= x.begin();
-  signalVector::iterator rotPtr = GMSKReverseRotation->begin();
+static void GMSKReverseRotate(signalVector &x, int sps)
+{
+  signalVector::iterator rotPtr, xPtr= x.begin();
+
+  if (sps == 1)
+    rotPtr = GMSKReverseRotation1->begin();
+  else
+    rotPtr = GMSKReverseRotationN->begin();
+
   if (x.isRealOnly()) {
     while (xPtr < x.end()) {
       *xPtr = *rotPtr++ * (xPtr->real());
@@ -414,10 +429,13 @@
   return y;
 }
 
-bool generateC1Pulse(int sps)
+static bool generateC1Pulse(int sps, PulseSequence *pulse)
 {
   int len;
 
+  if (!pulse)
+    return false;
+
   switch (sps) {
   case 4:
     len = 8;
@@ -426,20 +444,20 @@
     return false;
   }
 
-  GSMPulse->c1_buffer = convolve_h_alloc(len);
-  GSMPulse->c1 = new signalVector((complex *)
-                                  GSMPulse->c1_buffer, 0, len);
-  GSMPulse->c1->isRealOnly(true);
+  pulse->c1_buffer = convolve_h_alloc(len);
+  pulse->c1 = new signalVector((complex *)
+                                  pulse->c1_buffer, 0, len);
+  pulse->c1->isRealOnly(true);
 
   /* Enable alignment for SSE usage */
-  GSMPulse->c1->setAligned(true);
+  pulse->c1->setAligned(true);
 
-  signalVector::iterator xP = GSMPulse->c1->begin();
+  signalVector::iterator xP = pulse->c1->begin();
 
   switch (sps) {
   case 4:
     /* BT = 0.30 */
-    *xP++ = 0.0; 
+    *xP++ = 0.0;
     *xP++ = 8.16373112e-03;
     *xP++ = 2.84385729e-02;
     *xP++ = 5.64158904e-02;
@@ -452,18 +470,17 @@
   return true;
 }
 
-void generateGSMPulse(int sps, int symbolLength)
+static PulseSequence *generateGSMPulse(int sps, int symbolLength)
 {
   int len;
   float arg, avg, center;
-
-  delete GSMPulse;
+  PulseSequence *pulse;
 
   /* Store a single tap filter used for correlation sequence generation */
-  GSMPulse = new PulseSequence();
-  GSMPulse->empty = new signalVector(1);
-  GSMPulse->empty->isRealOnly(true);
-  *(GSMPulse->empty->begin()) = 1.0f;
+  pulse = new PulseSequence();
+  pulse->empty = new signalVector(1);
+  pulse->empty->isRealOnly(true);
+  *(pulse->empty->begin()) = 1.0f;
 
   /*
    * For 4 samples-per-symbol use a precomputed single pulse Laurent
@@ -481,15 +498,14 @@
       len = 4;
   }
 
-  GSMPulse->c0_buffer = convolve_h_alloc(len);
-  GSMPulse->c0 = new signalVector((complex *)
-                                  GSMPulse->c0_buffer, 0, len);
-  GSMPulse->c0->isRealOnly(true);
+  pulse->c0_buffer = convolve_h_alloc(len);
+  pulse->c0 = new signalVector((complex *) pulse->c0_buffer, 0, len);
+  pulse->c0->isRealOnly(true);
 
   /* Enable alingnment for SSE usage */
-  GSMPulse->c0->setAligned(true);
+  pulse->c0->setAligned(true);
 
-  signalVector::iterator xP = GSMPulse->c0->begin();
+  signalVector::iterator xP = pulse->c0->begin();
 
   if (sps == 4) {
     *xP++ = 0.0;
@@ -508,7 +524,7 @@
     *xP++ = 1.03184855e-01;
     *xP++ = 2.84385729e-02;
     *xP++ = 4.46348606e-03;
-    generateC1Pulse(sps);
+    generateC1Pulse(sps, pulse);
   } else {
     center = (float) (len - 1.0) / 2.0;
 
@@ -519,11 +535,13 @@
 			 0.527 * arg * arg * arg * arg);
     }
 
-    avg = sqrtf(vectorNorm2(*GSMPulse->c0) / sps);
-    xP = GSMPulse->c0->begin();
-    for (int i = 0; i < len; i++) 
+    avg = sqrtf(vectorNorm2(*pulse->c0) / sps);
+    xP = pulse->c0->begin();
+    for (int i = 0; i < len; i++)
       *xP++ /= avg;
   }
+
+  return pulse;
 }
 
 signalVector* frequencyShift(signalVector *y,
@@ -612,7 +630,7 @@
   signalVector *pulse, rotated, *shaped;
   signalVector::iterator itr;
 
-  pulse = GSMPulse->empty;
+  pulse = GSMPulse1->empty;
   burst_len = sps * (wBurst.size() + guardPeriodLength);
   rotated = signalVector(burst_len);
   itr = rotated.begin();
@@ -622,7 +640,7 @@
     itr += sps;
   }
 
-  GMSKRotate(rotated);
+  GMSKRotate(rotated, sps);
   rotated.isRealOnly(false);
 
   /* Dummy filter operation */
@@ -675,7 +693,7 @@
   *c0_itr = 2.0 * (0x01 & 0x01) - 1.0;
 
   /* Generate C0 phase coefficients */
-  GMSKRotate(c0_burst);
+  GMSKRotate(c0_burst, sps);
   c0_burst.isRealOnly(false);
 
   c0_itr = c0_burst.begin();
@@ -724,7 +742,11 @@
   signalVector *pulse, burst, *shaped;
   signalVector::iterator burst_itr;
 
-  pulse = GSMPulse->c0;
+  if (sps == 1)
+    pulse = GSMPulse1->c0;
+  else
+    pulse = GSMPulse->c0;
+
   burst_len = sps * (bits.size() + guard_len);
 
   burst = signalVector(burst_len);
@@ -737,7 +759,7 @@
     burst_itr += sps;
   }
 
-  GMSKRotate(burst);
+  GMSKRotate(burst, sps);
   burst.isRealOnly(false);
 
   /* Single Gaussian pulse approximation shaping */
@@ -1020,6 +1042,7 @@
 bool generateMidamble(int sps, int tsc)
 {
   bool status = true;
+  float toa;
   complex *data = NULL;
   signalVector *autocorr = NULL, *midamble = NULL;
   signalVector *midMidamble = NULL, *_midMidamble = NULL;
@@ -1067,7 +1090,16 @@
   gMidambles[tsc] = new CorrelationSequence;
   gMidambles[tsc]->buffer = data;
   gMidambles[tsc]->sequence = _midMidamble;
-  gMidambles[tsc]->gain = peakDetect(*autocorr,&gMidambles[tsc]->TOA, NULL);
+  gMidambles[tsc]->gain = peakDetect(*autocorr, &toa, NULL);
+
+  /* For 1 sps only
+   *     (Half of correlation length - 1) + midpoint of pulse shape + remainder
+   *     13.5 = (16 / 2 - 1) + 1.5 + (26 - 10) / 2
+   */
+  if (sps == 1)
+    gMidambles[tsc]->toa = toa - 13.5;
+  else
+    gMidambles[tsc]->toa = 0;
 
 release:
   delete autocorr;
@@ -1086,6 +1118,7 @@
 bool generateRACHSequence(int sps)
 {
   bool status = true;
+  float toa;
   complex *data = NULL;
   signalVector *autocorr = NULL;
   signalVector *seq0 = NULL, *seq1 = NULL, *_seq1 = NULL;
@@ -1119,7 +1152,16 @@
   gRACHSequence = new CorrelationSequence;
   gRACHSequence->sequence = _seq1;
   gRACHSequence->buffer = data;
-  gRACHSequence->gain = peakDetect(*autocorr,&gRACHSequence->TOA, NULL);
+  gRACHSequence->gain = peakDetect(*autocorr, &toa, NULL);
+
+  /* For 1 sps only
+   *     (Half of correlation length - 1) + midpoint of pulse shaping filer
+   *     20.5 = (40 / 2 - 1) + 1.5
+   */
+  if (sps == 1)
+    gRACHSequence->toa = toa - 20.5;
+  else
+    gRACHSequence->toa = 0.0;
 
 release:
   delete autocorr;
@@ -1224,6 +1266,9 @@
   if (sps == 4)
     *amp = *amp * complex(0.0, 1.0);
 
+  /* Compensate for residuate time lag */
+  *toa = *toa - sync->toa;
+
   return 1;
 }
 
@@ -1367,7 +1412,7 @@
 
   // shift up by a quarter of a frequency
   // ignore starting phase, since spec allows for discontinuous phase
-  GMSKReverseRotate(*shapedBurst);
+  GMSKReverseRotate(*shapedBurst, sps);
 
   // run through slicer
   if (sps > 1) {
@@ -1510,8 +1555,8 @@
 
   signalVector::iterator dPtr = postForward->begin();
   signalVector::iterator dBackPtr;
-  signalVector::iterator rotPtr = GMSKRotation->begin();
-  signalVector::iterator revRotPtr = GMSKReverseRotation->begin();
+  signalVector::iterator rotPtr = GMSKRotationN->begin();
+  signalVector::iterator revRotPtr = GMSKReverseRotationN->begin();
 
   signalVector *DFEoutput = new signalVector(postForward->size());
   signalVector::iterator DFEItr = DFEoutput->begin();
@@ -1550,3 +1595,23 @@
 
   return burstBits;
 }
+
+bool sigProcLibSetup(int sps)
+{
+  if ((sps != 1) && (sps != 4))
+    return false;
+
+  initTrigTables();
+  initGMSKRotationTables(sps);
+
+  GSMPulse1 = generateGSMPulse(1, 2);
+  if (sps > 1)
+    GSMPulse = generateGSMPulse(sps, 2);
+
+  if (!generateRACHSequence(1)) {
+    sigProcLibDestroy();
+    return false;
+  }
+
+  return true;
+}
diff --git a/Transceiver52M/sigProcLib.h b/Transceiver52M/sigProcLib.h
index 109ffa8..516d352 100644
--- a/Transceiver52M/sigProcLib.h
+++ b/Transceiver52M/sigProcLib.h
@@ -142,14 +142,6 @@
                        unsigned step = 1, int offset = 0);
 
 /** 
-	Generate the GSM pulse. 
-	@param sps The number of samples per GSM symbol.
-	@param symbolLength The size of the pulse.
-	@return The GSM pulse.
-*/
-void generateGSMPulse(int sps, int symbolLength);
-
-/** 
         Frequency shift a vector.
 	@param y The frequency shifted vector.
 	@param x The vector to-be-shifted.