Transceiver52M: Add 64 MHz resampling option with B100

Move B100 to the resampling interface with default
clocking. This temporarily resolves undetermined
FPGA clocking issues. This also provides extensible
support for multiple clocking rates and resampling
ratios.

Signed-off-by: Thomas Tsou <tom@tsou.cc>
diff --git a/Transceiver52M/UHDDevice.cpp b/Transceiver52M/UHDDevice.cpp
index f302ba0..f4b7cf4 100644
--- a/Transceiver52M/UHDDevice.cpp
+++ b/Transceiver52M/UHDDevice.cpp
@@ -32,8 +32,9 @@
 #include "config.h"
 #endif
 
-#define BXXX_CLK_RT      52e6
-#define BXXX_BASE_RT     GSMRATE
+#define B2XX_CLK_RT      52e6
+#define B2XX_BASE_RT     GSMRATE
+#define B100_BASE_RT     400000
 #define USRP2_BASE_RT    390625
 #define TX_AMPL          0.3
 #define SAMPLE_BUF_SZ    (1 << 20)
@@ -66,10 +67,10 @@
 static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
 	{ USRP1, 1, 0.0 },
 	{ USRP1, 4, 0.0 },
-	{ USRP2, 1, 1.1815e-4 },
-	{ USRP2, 4, 7.7538e-5 },
-	{ B100,  1, 9.9692e-5 },
-	{ B100,  4, 6.5545e-5 },
+	{ USRP2, 1, 1.2184e-4 },
+	{ USRP2, 4, 8.0230e-5 },
+	{ B100,  1, 1.2104e-4 },
+	{ B100,  4, 7.9307e-5 },
 	{ B2XX,  1, 9.9692e-5 },
 	{ B2XX,  4, 6.9248e-5 },
 	{ UMTRX, 1, 9.9692e-5 },
@@ -108,6 +109,7 @@
 	case USRP2:
 		return USRP2_BASE_RT * sps;
 	case B100:
+		return B100_BASE_RT * sps;
 	case B2XX:
 	case UMTRX:
 		return GSMRATE * sps;
@@ -412,9 +414,9 @@
 	double offset_limit = 1.0;
 	double tx_offset, rx_offset;
 
-	// B100/200 are the only device where we set FPGA clocking
-	if ((dev_type == B100) || (dev_type == B2XX)) {
-		if (set_master_clk(BXXX_CLK_RT) < 0)
+	// B2XX is the only device where we set FPGA clocking
+	if (dev_type == B2XX) {
+		if (set_master_clk(B2XX_CLK_RT) < 0)
 			return -1;
 	}
 
@@ -580,8 +582,12 @@
 	// Print configuration
 	LOG(INFO) << "\n" << usrp_dev->get_pp_string();
 
-	if (dev_type == USRP2)
-		return RESAMP;
+	switch (dev_type) {
+	case B100:
+		return RESAMP_64M;
+	case USRP2:
+		return RESAMP_100M;
+	}
 
 	return NORMAL;
 }
diff --git a/Transceiver52M/radioDevice.h b/Transceiver52M/radioDevice.h
index 485d037..07ffd63 100644
--- a/Transceiver52M/radioDevice.h
+++ b/Transceiver52M/radioDevice.h
@@ -34,7 +34,7 @@
   enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
 
   /* Radio interface types */
-  enum RadioInterfaceType { NORMAL, RESAMP };
+  enum RadioInterfaceType { NORMAL, RESAMP_64M, RESAMP_100M };
 
   static RadioDevice *make(int sps, bool skipRx = false);
 
diff --git a/Transceiver52M/radioInterface.cpp b/Transceiver52M/radioInterface.cpp
index e9fcd49..a633738 100644
--- a/Transceiver52M/radioInterface.cpp
+++ b/Transceiver52M/radioInterface.cpp
@@ -51,8 +51,11 @@
   close();
 }
 
-bool RadioInterface::init()
+bool RadioInterface::init(int type)
 {
+  if (type != RadioDevice::NORMAL)
+    return false;
+
   close();
 
   sendBuffer = new signalVector(OUTCHUNK * 20);
diff --git a/Transceiver52M/radioInterface.h b/Transceiver52M/radioInterface.h
index 98d0f9d..84de1e5 100644
--- a/Transceiver52M/radioInterface.h
+++ b/Transceiver52M/radioInterface.h
@@ -21,9 +21,6 @@
 #include "radioVector.h"
 #include "radioClock.h"
 
-/** samples per GSM symbol */
-#define SAMPSPERSYM 4
-
 static const unsigned gSlotLen = 148;      ///< number of symbols per slot, not counting guard periods
 
 /** class to interface the transceiver with the USRP */
@@ -86,13 +83,13 @@
   void start();
 
   /** intialization */
-  virtual bool init();
+  virtual bool init(int type);
   virtual void close();
 
   /** constructor */
   RadioInterface(RadioDevice* wRadio = NULL,
 		 int receiveOffset = 3,
-		 int wSPS = SAMPSPERSYM,
+		 int wSPS = 4,
 		 GSM::Time wStartTime = GSM::Time(0));
     
   /** destructor */
@@ -172,11 +169,11 @@
 
   RadioInterfaceResamp(RadioDevice* wRadio = NULL,
 		       int receiveOffset = 3,
-		       int wSPS = SAMPSPERSYM,
+		       int wSPS = 4,
 		       GSM::Time wStartTime = GSM::Time(0));
 
   ~RadioInterfaceResamp();
 
-  bool init();
+  bool init(int type);
   void close();
 };
diff --git a/Transceiver52M/radioInterfaceResamp.cpp b/Transceiver52M/radioInterfaceResamp.cpp
index 40bf32d..f857df9 100644
--- a/Transceiver52M/radioInterfaceResamp.cpp
+++ b/Transceiver52M/radioInterfaceResamp.cpp
@@ -28,9 +28,13 @@
 #include "convert.h"
 }
 
+/* Resampling parameters for 64 MHz clocking */
+#define RESAMP_64M_INRATE			65
+#define RESAMP_64M_OUTRATE			96
+
 /* Resampling parameters for 100 MHz clocking */
-#define RESAMP_INRATE			52
-#define RESAMP_OUTRATE			75
+#define RESAMP_100M_INRATE			52
+#define RESAMP_100M_OUTRATE			75
 
 /*
  * Resampling filter bandwidth scaling factor
@@ -41,11 +45,13 @@
  */
 #define RESAMP_TX4_FILTER		0.45
 
-#define INCHUNK				(RESAMP_INRATE * 4)
-#define OUTCHUNK			(RESAMP_OUTRATE * 4)
-
 static Resampler *upsampler = NULL;
 static Resampler *dnsampler = NULL;
+static int resamp_inrate = 0;
+static int resamp_inchunk = 0;
+static int resamp_outrate = 0;
+static int resamp_outchunk = 0;
+
 short *convertRecvBuffer = NULL;
 short *convertSendBuffer = NULL;
 
@@ -86,22 +92,40 @@
 }
 
 /* Initialize I/O specific objects */
-bool RadioInterfaceResamp::init()
+bool RadioInterfaceResamp::init(int type)
 {
 	float cutoff = 1.0f;
 
 	close();
 
+	switch (type) {
+	case RadioDevice::RESAMP_64M:
+		resamp_inrate = RESAMP_64M_INRATE;
+		resamp_outrate = RESAMP_64M_OUTRATE;
+		break;
+	case RadioDevice::RESAMP_100M:
+		resamp_inrate = RESAMP_100M_INRATE;
+		resamp_outrate = RESAMP_100M_OUTRATE;
+		break;
+	case RadioDevice::NORMAL:
+	default:
+		LOG(ALERT) << "Invalid device configuration";
+		return false;
+	}
+
+	resamp_inchunk = resamp_inrate * 4;
+	resamp_outchunk = resamp_outrate * 4;
+
 	if (mSPSTx == 4)
 		cutoff = RESAMP_TX4_FILTER;
 
-	dnsampler = new Resampler(RESAMP_INRATE, RESAMP_OUTRATE);
+	dnsampler = new Resampler(resamp_inrate, resamp_outrate);
 	if (!dnsampler->init()) {
 		LOG(ALERT) << "Rx resampler failed to initialize";
 		return false;
 	}
 
-	upsampler = new Resampler(RESAMP_OUTRATE, RESAMP_INRATE);
+	upsampler = new Resampler(resamp_outrate, resamp_inrate);
 	if (!upsampler->init(cutoff)) {
 		LOG(ALERT) << "Tx resampler failed to initialize";
 		return false;
@@ -113,14 +137,16 @@
 	 * and requires headroom equivalent to the filter length. Low
 	 * rate buffers are allocated in the main radio interface code.
 	 */
-	innerSendBuffer = new signalVector(INCHUNK * 20, upsampler->len());
-	outerSendBuffer = new signalVector(OUTCHUNK * 20);
+	innerSendBuffer = new signalVector(resamp_inchunk * 20,
+					   upsampler->len());
+	outerSendBuffer = new signalVector(resamp_outchunk * 20);
 
-	outerRecvBuffer = new signalVector(OUTCHUNK * 2, dnsampler->len());
-	innerRecvBuffer = new signalVector(INCHUNK * 20);
+	outerRecvBuffer = new signalVector(resamp_outchunk * 2,
+					   dnsampler->len());
+	innerRecvBuffer = new signalVector(resamp_inchunk * 20);
 
-	convertSendBuffer = new short[OUTCHUNK * 2 * 20];
-	convertRecvBuffer = new short[OUTCHUNK * 2 * 2];
+	convertSendBuffer = new short[resamp_outchunk * 2 * 20];
+	convertRecvBuffer = new short[resamp_outchunk * 2 * 2];
 
 	sendBuffer = innerSendBuffer;
 	recvBuffer = innerRecvBuffer;
@@ -133,8 +159,8 @@
 {
 	bool local_underrun;
 	int rc, num_recv;
-	int inner_len = INCHUNK;
-	int outer_len = OUTCHUNK;
+	int inner_len = resamp_inchunk;
+	int outer_len = resamp_outchunk;
 
 	/* Outer buffer access size is fixed */
 	num_recv = mRadio->readSamples(convertRecvBuffer,
@@ -170,15 +196,15 @@
 	int rc, chunks, num_sent;
 	int inner_len, outer_len;
 
-	if (sendCursor < INCHUNK)
+	if (sendCursor < resamp_inchunk)
 		return;
 
-	chunks = sendCursor / INCHUNK;
+	chunks = sendCursor / resamp_inchunk;
 	if (chunks > 8)
 		chunks = 8;
 
-	inner_len = chunks * INCHUNK;
-	outer_len = chunks * OUTCHUNK;
+	inner_len = chunks * resamp_inchunk;
+	outer_len = chunks * resamp_outchunk;
 
 	/* Always send from the beginning of the buffer */
 	rc = upsampler->rotate((float *) innerSendBuffer->begin(), inner_len,
diff --git a/Transceiver52M/runTransceiver.cpp b/Transceiver52M/runTransceiver.cpp
index 5401c22..9a20ee6 100644
--- a/Transceiver52M/runTransceiver.cpp
+++ b/Transceiver52M/runTransceiver.cpp
@@ -38,6 +38,15 @@
 
 #define CONFIGDB            "/etc/OpenBTS/OpenBTS.db"
 
+/* Samples-per-symbol for downlink path
+ *     4 - Uses precision modulator (more computation, less distortion)
+ *     1 - Uses minimized modulator (less computation, more distortion)
+ *
+ *     Other values are invalid. Receive path (uplink) is always
+ *     downsampled to 1 sps
+ */
+#define SPS                 4
+
 using namespace std;
 
 ConfigurationTable gConfig(CONFIGDB);
@@ -138,7 +147,7 @@
 
   srandom(time(NULL));
 
-  RadioDevice *usrp = RadioDevice::make(SAMPSPERSYM);
+  RadioDevice *usrp = RadioDevice::make(SPS);
   int radioType = usrp->open(deviceArgs);
   if (radioType < 0) {
     LOG(ALERT) << "Transceiver exiting..." << std::endl;
@@ -148,21 +157,22 @@
   RadioInterface* radio;
   switch (radioType) {
   case RadioDevice::NORMAL:
-    radio = new RadioInterface(usrp, 3, SAMPSPERSYM, false);
+    radio = new RadioInterface(usrp, 3, SPS, false);
     break;
-  case RadioDevice::RESAMP:
-    radio = new RadioInterfaceResamp(usrp, 3, SAMPSPERSYM, false);
+  case RadioDevice::RESAMP_64M:
+  case RadioDevice::RESAMP_100M:
+    radio = new RadioInterfaceResamp(usrp, 3, SPS, false);
     break;
   default:
     LOG(ALERT) << "Unsupported configuration";
     return EXIT_FAILURE;
   }
-  if (!radio->init()) {
+  if (!radio->init(radioType)) {
     LOG(ALERT) << "Failed to initialize radio interface";
   }
 
   Transceiver *trx = new Transceiver(trxPort, trxAddr.c_str(),
-                                     SAMPSPERSYM, GSM::Time(3,0), radio);
+                                     SPS, GSM::Time(3,0), radio);
   if (!trx->init()) {
     LOG(ALERT) << "Failed to initialize transceiver";
   }