EDGE: Add interfaces to enable EDGE transceiver

Create EDGE slot type in the Transceiver. When EDGE mode is enabled
for a particular slot, blind detection will be performed by
correlating against EDGE followed by normal bursts if no EDGE burst
is found.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
diff --git a/Transceiver52M/Transceiver.cpp b/Transceiver52M/Transceiver.cpp
index 2be7ab0..255e120 100644
--- a/Transceiver52M/Transceiver.cpp
+++ b/Transceiver52M/Transceiver.cpp
@@ -367,7 +367,12 @@
     return;
   }
 
-  burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
+  /* Use the number of bits as the EDGE burst indicator */
+  if (bits.size() == EDGE_BURST_NBITS)
+    burst = modulateEdgeBurst(bits, mSPSTx);
+  else
+    burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
+
   scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
 
   radio_burst = new radioVector(wTime, burst);
@@ -561,6 +566,13 @@
   float threshold = 5.0, rc = 0;
 
   switch (type) {
+  case EDGE:
+    rc = detectEdgeBurst(burst, mTSC, threshold, mSPSRx,
+                         amp, toa, mMaxExpectedDelay);
+    if (rc > 0)
+      break;
+    else
+      type = TSC;
   case TSC:
     rc = analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx,
                              amp, toa, mMaxExpectedDelay);
@@ -573,6 +585,8 @@
     LOG(ERR) << "Invalid correlation type";
   }
 
+  if (rc > 0)
+    return type;
 
   return rc;
 }
@@ -583,8 +597,11 @@
  */
 SoftVector *Transceiver::demodulate(TransceiverState *state,
                                     signalVector &burst, complex amp,
-                                    float toa)
+                                    float toa, CorrType type)
 {
+  if (type == EDGE)
+	  return demodEdgeBurst(burst, mSPSRx, amp, toa);
+
   return demodulateBurst(burst, mSPSRx, amp, toa);
 }
 
@@ -606,7 +623,7 @@
                                          double &timingOffset, double &noise,
                                          size_t chan)
 {
-  int success;
+  int rc;
   complex amp;
   float toa, pow, max = -1.0, avg = 0.0;
   int max_i = -1;
@@ -677,13 +694,14 @@
   }
 
   /* Detect normal or RACH bursts */
-  success = detectBurst(state, *burst, amp, toa, type);
+  rc = detectBurst(state, *burst, amp, toa, type);
 
-  /* Alert an error and exit */
-  if (success <= 0) {
-    if (success == -SIGERR_CLIP) {
+  if (rc > 0) {
+    type = (CorrType) rc;
+  } else if (rc <= 0) {
+    if (rc == -SIGERR_CLIP) {
       LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
-    } else if (success != SIGERR_NONE) {
+    } else if (rc != SIGERR_NONE) {
       LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
     }
 
@@ -693,7 +711,7 @@
 
   timingOffset = toa / mSPSRx;
 
-  bits = demodulate(state, *burst, amp, toa);
+  bits = demodulate(state, *burst, amp, toa, type);
 
   delete radio_burst;
   return bits;
@@ -916,6 +934,19 @@
   }
 }
 
+void Transceiver::logRxBurst(SoftVector *burst, GSM::Time time, double dbm,
+                             double rssi, double noise, double toa)
+{
+  LOG(DEBUG) << std::fixed << std::right
+    << " time: "   << time
+    << " RSSI: "   << std::setw(5) << std::setprecision(1) << rssi
+                   << "dBFS/" << std::setw(6) << -dbm << "dBm"
+    << " noise: "  << std::setw(5) << std::setprecision(1) << noise
+                   << "dBFS/" << std::setw(6) << -(noise + rssiOffset) << "dBm"
+    << " TOA: "    << std::setw(5) << std::setprecision(2) << toa
+    << " bits: "   << *burst;
+}
+
 void Transceiver::driveReceiveFIFO(size_t chan)
 {
   SoftVector *rxBurst = NULL;
@@ -926,37 +957,39 @@
   double noise; // noise level in dBFS
   GSM::Time burstTime;
   bool isRssiValid; // are RSSI, noise and burstTime valid
+  unsigned nbits = gSlotLen;
 
   rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan);
+  if (!rxBurst)
+    return;
 
-  if (rxBurst) { 
-    dBm = RSSI+rssiOffset;
-    TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
+  /*
+   * EDGE demodulator returns 444 (148 * 3) bits
+   */
+  if (rxBurst->size() == gSlotLen * 3)
+    nbits = gSlotLen * 3;
 
-    LOG(DEBUG) << std::fixed << std::right
-      << " time: "   << burstTime
-      << " RSSI: "   << std::setw(5) << std::setprecision(1) << RSSI << "dBFS/" << std::setw(6) << -dBm << "dBm"
-      << " noise: "  << std::setw(5) << std::setprecision(1) << noise << "dBFS/" << std::setw(6) << -(noise+rssiOffset) << "dBm"
-      << " TOA: "    << std::setw(5) << std::setprecision(2) << TOA
-      << " bits: "   << *rxBurst;
+  dBm = RSSI + rssiOffset;
+  logRxBurst(rxBurst, burstTime, dBm, RSSI, noise, TOA);
 
-    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] = (int)dBm;
-    burstString[6] = (TOAint >> 8) & 0x0ff;
-    burstString[7] = TOAint & 0x0ff;
-    SoftVector::iterator burstItr = rxBurst->begin();
+  TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
 
-    for (unsigned int i = 0; i < gSlotLen; i++) {
-      burstString[8+i] =(char) round((*burstItr++)*255.0);
-    }
-    burstString[gSlotLen+9] = '\0';
-    delete rxBurst;
+  char burstString[nbits + 10];
+  burstString[0] = burstTime.TN();
+  for (int i = 0; i < 4; i++)
+    burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
+  burstString[5] = (int)dBm;
+  burstString[6] = (TOAint >> 8) & 0x0ff;
+  burstString[7] = TOAint & 0x0ff;
+  SoftVector::iterator burstItr = rxBurst->begin();
 
-    mDataSockets[chan]->write(burstString,gSlotLen+10);
-  }
+  for (unsigned i = 0; i < nbits; i++)
+    burstString[8 + i] = (char) round((*burstItr++) * 255.0);
+
+  burstString[nbits + 9] = '\0';
+  delete rxBurst;
+
+  mDataSockets[chan]->write(burstString, nbits + 10);
 }
 
 void Transceiver::driveTxFIFO()
diff --git a/Transceiver52M/Transceiver.h b/Transceiver52M/Transceiver.h
index 2fd1aea..fa58053 100644
--- a/Transceiver52M/Transceiver.h
+++ b/Transceiver52M/Transceiver.h
@@ -147,6 +147,7 @@
     OFF,               ///< timeslot is off
     TSC,	       ///< timeslot should contain a normal burst
     RACH,	       ///< timeslot should contain an access burst
+    EDGE,	       ///< timeslot should contain an EDGE burst
     IDLE	       ///< timeslot is an idle (or dummy) burst
   } CorrType;
 
@@ -214,7 +215,7 @@
 
   /** Demodulate burst and output soft bits */
   SoftVector *demodulate(TransceiverState *state, signalVector &burst,
-                         complex amp, float toa);
+                         complex amp, float toa, CorrType type);
 
   int mSPSTx;                          ///< number of samples per Tx symbol
   int mSPSRx;                          ///< number of samples per Rx symbol
@@ -272,6 +273,8 @@
   /** set priority on current thread */
   void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); }
 
+  void logRxBurst(SoftVector *burst, GSM::Time time, double dbm,
+                  double rssi, double noise, double toa);
 };
 
 void *RxUpperLoopAdapter(TransceiverChannel *);
diff --git a/Transceiver52M/osmo-trx.cpp b/Transceiver52M/osmo-trx.cpp
index c8fd9a8..e7b2a16 100644
--- a/Transceiver52M/osmo-trx.cpp
+++ b/Transceiver52M/osmo-trx.cpp
@@ -81,6 +81,7 @@
 	double offset;
 	double rssi_offset;
 	bool swap_channels;
+	bool edge;
 };
 
 ConfigurationTable gConfig;
@@ -130,7 +131,7 @@
  */
 bool trx_setup_config(struct trx_config *config)
 {
-	std::string refstr, fillstr, divstr;
+	std::string refstr, fillstr, divstr, edgestr;
 
 	if (!testConfig())
 		return false;
@@ -170,6 +171,7 @@
 	if (config->diversity)
 		config->chans = 2;
 
+	edgestr = config->edge ? "Enabled" : "Disabled";
 	refstr = config->extref ? "Enabled" : "Disabled";
 	divstr = config->diversity ? "Enabled" : "Disabled";
 	switch (config->filler) {
@@ -192,6 +194,7 @@
 	ost << "   TRX Address............. " << config->addr << std::endl;
 	ost << "   Channels................ " << config->chans << std::endl;
 	ost << "   Tx Samples-per-Symbol... " << config->tx_sps << std::endl;
+	ost << "   EDGE support............ " << edgestr << std::endl;
 	ost << "   External Reference...... " << refstr << std::endl;
 	ost << "   C0 Filler Table......... " << fillstr << std::endl;
 	ost << "   Diversity............... " << divstr << std::endl;
@@ -215,6 +218,10 @@
 {
 	RadioInterface *radio = NULL;
 
+	if ((config->rx_sps != 1) && (type != RadioDevice::NORMAL)) {
+		LOG(ALERT) << "Unsupported radio interface configuration";
+	}
+
 	switch (type) {
 	case RadioDevice::NORMAL:
 		radio = new RadioInterface(usrp, config->tx_sps,
@@ -301,6 +308,7 @@
 		"  -l    Logging level (%s)\n"
 		"  -i    IP address of GSM core\n"
 		"  -p    Base port number\n"
+		"  -e    Enable EDGE receiver\n"
 		"  -d    Enable dual channel diversity receiver\n"
 		"  -x    Enable external 10 MHz reference\n"
 		"  -s    Samples-per-symbol (1 or 4)\n"
@@ -328,8 +336,9 @@
 	config->offset = 0.0;
 	config->rssi_offset = 0.0;
 	config->swap_channels = false;
+	config->edge = false;
 
-	while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:R:S")) != -1) {
+	while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:R:Se")) != -1) {
 		switch (option) {
 		case 'h':
 			print_help();
@@ -375,6 +384,10 @@
 		case 'S':
 			config->swap_channels = true;
 			break;
+		case 'e':
+			config->edge = true;
+			config->rx_sps = 4;
+			break;
 		default:
 			print_help();
 			exit(0);
@@ -387,6 +400,12 @@
 		exit(0);
 	}
 
+	if (config->edge && (config->tx_sps != 4)) {
+		printf("EDGE only supported at 4 samples per symbol\n\n");
+		print_help();
+		exit(0);
+	}
+
 	if (config->rtsc > 7) {
 		printf("Invalid training sequence %i\n\n", config->rtsc);
 		print_help();
diff --git a/Transceiver52M/sigProcLib.h b/Transceiver52M/sigProcLib.h
index 2dcc97d..4f9f849 100644
--- a/Transceiver52M/sigProcLib.h
+++ b/Transceiver52M/sigProcLib.h
@@ -20,6 +20,11 @@
 #include "BitVector.h"
 #include "signalVector.h"
 
+/* Burst lengths */
+#define NORMAL_BURST_NBITS		148
+#define EDGE_BURST_NBITS		444
+#define EDGE_BURST_NSYMS		(EDGE_BURST_NBITS / 3)
+
 /** Convolution type indicator */
 enum ConvType {
   START_ONLY,