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/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;
+}