transceiver: add experimental viterbi equalizer support

The VA is already being used by the ms side and is part of the original
gsm design. It only works for gmsk, 4sps, and needs a bit of rx burst
scaling and burst shifting.

Change-Id: I9d7a4ff72e323832a94d885d5714fcde01ceeb3d
diff --git a/CommonLibs/config_defs.h b/CommonLibs/config_defs.h
index bf49bcf..07a3981 100644
--- a/CommonLibs/config_defs.h
+++ b/CommonLibs/config_defs.h
@@ -67,4 +67,5 @@
 		double ul_gain;
 		double dl_gain;
 	} overrides;
+	bool use_va;
 };
diff --git a/CommonLibs/trx_vty.c b/CommonLibs/trx_vty.c
index 55142ef..bd1b0ce 100644
--- a/CommonLibs/trx_vty.c
+++ b/CommonLibs/trx_vty.c
@@ -339,6 +339,25 @@
 	return CMD_SUCCESS;
 }
 
+DEFUN_ATTR(cfg_use_viterbi, cfg_use_viterbi_cmd,
+	"viterbi-eq (disable|enable)",
+	"Use viterbi equalizer for gmsk (default=disable)\n"
+	"Disable VA\n"
+	"Enable VA\n",
+	CMD_ATTR_HIDDEN)
+{
+	struct trx_ctx *trx = trx_from_vty(vty);
+
+	if (strcmp("disable", argv[0]) == 0)
+		trx->cfg.use_va = false;
+	else if (strcmp("enable", argv[0]) == 0)
+		trx->cfg.use_va = true;
+	else
+		return CMD_WARNING;
+
+	return CMD_SUCCESS;
+}
+
 DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
 	"swap-channels (disable|enable)",
 	"Swap primary and secondary channels of the PHY (if any)\n"
@@ -700,6 +719,8 @@
 		vty_out(vty, " dl-gain-override %f%s", trx->cfg.overrides.dl_gain, VTY_NEWLINE);
 	if (trx->cfg.overrides.ul_gain_override)
 		vty_out(vty, " ul-gain-override %f%s", trx->cfg.overrides.ul_gain, VTY_NEWLINE);
+	if (trx->cfg.use_va)
+		vty_out(vty, " viterbi-eq %s%s", trx->cfg.use_va ? "enable" : "disable", VTY_NEWLINE);
 	trx_rate_ctr_threshold_write_config(vty, " ");
 
 	for (i = 0; i < trx->cfg.num_chans; i++) {
@@ -869,6 +890,7 @@
 	install_element(TRX_NODE, &cfg_dl_freq_override_cmd);
 	install_element(TRX_NODE, &cfg_ul_gain_override_cmd);
 	install_element(TRX_NODE, &cfg_dl_gain_override_cmd);
+	install_element(TRX_NODE, &cfg_use_viterbi_cmd);
 	install_node(&chan_node, dummy_config_write);
 	install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
 	install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am
index f8556f0..e82111e 100644
--- a/Transceiver52M/Makefile.am
+++ b/Transceiver52M/Makefile.am
@@ -40,7 +40,9 @@
 	ChannelizerBase.cpp \
 	Channelizer.cpp \
 	Synthesis.cpp \
-	proto_trxd.c
+	proto_trxd.c \
+	grgsm_vitac/grgsm_vitac.cpp \
+	grgsm_vitac/viterbi_detector.cc
 
 libtransceiver_common_la_SOURCES = \
 	$(COMMON_SOURCES) \
diff --git a/Transceiver52M/Transceiver.cpp b/Transceiver52M/Transceiver.cpp
index c9894f0..fed4289 100644
--- a/Transceiver52M/Transceiver.cpp
+++ b/Transceiver52M/Transceiver.cpp
@@ -29,6 +29,7 @@
 #include <fstream>
 #include "Transceiver.h"
 #include <Logger.h>
+#include <grgsm_vitac/grgsm_vitac.h>
 
 extern "C" {
 #include "osmo_signal.h"
@@ -208,6 +209,8 @@
     return false;
   }
 
+  initvita();
+
   mDataSockets.resize(mChans, -1);
 
 
@@ -614,6 +617,44 @@
   return mRadioInterface->rssiOffset(chan) + cfg->rssi_offset;
 }
 
+static SoftVector *demodAnyBurst_va(const signalVector &burst, CorrType type, int sps, int rach_max_toa, int tsc)
+{
+	auto conved_beg = reinterpret_cast<const std::complex<float> *>(&burst.begin()[0]);
+	std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
+	float ncmax;
+	const unsigned burst_len_bits = 148 + 8;
+	char demodded_softbits[burst_len_bits];
+	SoftVector *bits = new SoftVector(burst_len_bits);
+
+	if (type == CorrType::TSC) {
+		auto rach_burst_start = get_norm_chan_imp_resp(conved_beg, chan_imp_resp, &ncmax, tsc);
+		rach_burst_start = std::max(rach_burst_start, 0);
+		detect_burst_nb(conved_beg, chan_imp_resp, rach_burst_start, demodded_softbits);
+	} else {
+		auto normal_burst_start = get_access_imp_resp(conved_beg, chan_imp_resp, &ncmax, 0);
+		normal_burst_start = std::max(normal_burst_start, 0);
+		detect_burst_ab(conved_beg, chan_imp_resp, normal_burst_start, demodded_softbits, rach_max_toa);
+	}
+
+	float *s = &bits->begin()[0];
+	for (unsigned int i = 0; i < 148; i++)
+		s[i] = demodded_softbits[i] * -1;
+	for (unsigned int i = 148; i < burst_len_bits; i++)
+		s[i] = 0;
+	return bits;
+}
+
+#define USE_VA
+
+#ifdef USE_VA
+// signalvector is owning despite claiming not to, but we can pretend, too..
+static void dummy_free(void *wData){};
+static void *dummy_alloc(size_t newSize)
+{
+	return 0;
+};
+#endif
+
 /*
  * Pull bursts from the FIFO and handle according to the slot
  * and burst correlation type. Equalzation is currently disabled.
@@ -634,6 +675,9 @@
   TransceiverState *state = &mStates[chan];
   bool ctr_changed = false;
   double rssi_offset;
+  static complex burst_shift_buffer[625];
+  static signalVector shift_vec(burst_shift_buffer, 0, 625, dummy_alloc, dummy_free);
+  signalVector *shvec_ptr = &shift_vec;
 
   /* Blocking FIFO read */
   radioVector *radio_burst = mReceiveFIFO[chan]->read();
@@ -713,8 +757,15 @@
   max_toa = (type == RACH || type == EXT_RACH) ?
             mMaxExpectedDelayAB : mMaxExpectedDelayNB;
 
+  if (cfg->use_va) {
+    // shifted burst copy to make the old demod and detection happy
+    std::copy(burst->begin() + 20, burst->end() - 20, shift_vec.begin());
+  } else {
+    shvec_ptr = burst;
+  }
+
   /* Detect normal or RACH bursts */
-  rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
+  rc = detectAnyBurst(*shvec_ptr, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
   if (rc <= 0) {
     if (rc == -SIGERR_CLIP) {
       LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst";
@@ -728,7 +779,13 @@
     goto ret_idle;
   }
 
-  rxBurst = demodAnyBurst(*burst, (CorrType) rc, cfg->rx_sps, &ebp);
+  if (cfg->use_va) {
+    scaleVector(*burst, { (1. / (float)((1 << 14) - 1)), 0 });
+    rxBurst = demodAnyBurst_va(*burst, (CorrType)rc, cfg->rx_sps, max_toa, mTSC);
+  } else {
+    rxBurst = demodAnyBurst(*shvec_ptr, (CorrType)rc, cfg->rx_sps, &ebp);
+  }
+
   bi->toa = ebp.toa;
   bi->tsc = ebp.tsc;
   bi->ci = ebp.ci;
diff --git a/Transceiver52M/osmo-trx.cpp b/Transceiver52M/osmo-trx.cpp
index 89a577b..8f5cc85 100644
--- a/Transceiver52M/osmo-trx.cpp
+++ b/Transceiver52M/osmo-trx.cpp
@@ -77,6 +77,24 @@
 static RadioDevice *usrp;
 static RadioInterface *radio;
 
+/* adjusts read timestamp offset to make the viterbi equalizer happy by including the start tail bits */
+template <typename B>
+class rif_va_wrapper : public B {
+	bool use_va;
+
+    public:
+	template <typename... Args>
+	rif_va_wrapper(bool use_va, Args &&...args) : B(std::forward<Args>(args)...), use_va(use_va)
+	{
+	}
+	bool start() override
+	{
+		auto rv = B::start();
+		B::readTimestamp -= use_va ? 20 : 0;
+		return rv;
+	};
+};
+
 /* Create radio interface
  *     The interface consists of sample rate changes, frequency shifts,
  *     channel multiplexing, and other conversions. The transceiver core
@@ -91,17 +109,17 @@
 
 	switch (type) {
 	case RadioDevice::NORMAL:
-		radio = new RadioInterface(usrp, trx->cfg.tx_sps,
-					   trx->cfg.rx_sps, trx->cfg.num_chans);
+		radio = new rif_va_wrapper<RadioInterface>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps,
+							   trx->cfg.num_chans);
 		break;
 	case RadioDevice::RESAMP_64M:
 	case RadioDevice::RESAMP_100M:
-		radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
-						 trx->cfg.rx_sps);
+		radio = new rif_va_wrapper<RadioInterfaceResamp>(trx->cfg.use_va, usrp, trx->cfg.tx_sps,
+								 trx->cfg.rx_sps);
 		break;
 	case RadioDevice::MULTI_ARFCN:
-		radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
-						trx->cfg.rx_sps, trx->cfg.num_chans);
+		radio = new rif_va_wrapper<RadioInterfaceMulti>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps,
+								trx->cfg.num_chans);
 		break;
 	default:
 		LOG(ALERT) << "Unsupported radio interface configuration";
@@ -465,6 +483,12 @@
 		return -1;
 	}
 
+	if (trx->cfg.use_va &&
+	    (trx->cfg.egprs || trx->cfg.multi_arfcn || trx->cfg.tx_sps != 4 || trx->cfg.rx_sps != 4)) {
+		LOG(ERROR) << "Viterbi equalizer only works for gmsk with 4 tx/rx samples per symbol!";
+		return -1;
+	}
+
 	return 0;
 }
 
diff --git a/Transceiver52M/radioInterface.h b/Transceiver52M/radioInterface.h
index cd40ddf..b05af78 100644
--- a/Transceiver52M/radioInterface.h
+++ b/Transceiver52M/radioInterface.h
@@ -75,7 +75,7 @@
 public:
 
   /** start the interface */
-  bool start();
+  virtual bool start();
   bool stop();
 
   /** initialization */
@@ -151,7 +151,7 @@
 
 public:
   RadioInterfaceResamp(RadioDevice* wDevice, size_t tx_sps, size_t rx_sps);
-  ~RadioInterfaceResamp();
+  virtual ~RadioInterfaceResamp();
 
   bool init(int type);
   void close();
@@ -184,7 +184,7 @@
 public:
   RadioInterfaceMulti(RadioDevice* radio, size_t tx_sps,
                       size_t rx_sps, size_t chans = 1);
-  ~RadioInterfaceMulti();
+  virtual ~RadioInterfaceMulti();
 
   bool init(int type);
   void close();