From cca5d93f661b27a1895dc12c624cd1a93b0a9c05 Mon Sep 17 00:00:00 2001 From: Eric Date: Tue, 25 Jul 2023 18:44:03 +0200 Subject: [PATCH] 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 --- CommonLibs/config_defs.h | 1 + CommonLibs/trx_vty.c | 22 ++++++++++++ Transceiver52M/Makefile.am | 4 ++- Transceiver52M/Transceiver.cpp | 61 +++++++++++++++++++++++++++++++-- Transceiver52M/osmo-trx.cpp | 36 +++++++++++++++---- Transceiver52M/radioInterface.h | 6 ++-- 6 files changed, 118 insertions(+), 12 deletions(-) diff --git a/CommonLibs/config_defs.h b/CommonLibs/config_defs.h index bf49bcfa..07a3981c 100644 --- a/CommonLibs/config_defs.h +++ b/CommonLibs/config_defs.h @@ -67,4 +67,5 @@ struct trx_cfg { double ul_gain; double dl_gain; } overrides; + bool use_va; }; diff --git a/CommonLibs/trx_vty.c b/CommonLibs/trx_vty.c index 55142efa..bd1b0ce9 100644 --- a/CommonLibs/trx_vty.c +++ b/CommonLibs/trx_vty.c @@ -339,6 +339,25 @@ DEFUN_ATTR(cfg_dl_gain_override, cfg_dl_gain_override_cmd, 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 @@ static int config_write_trx(struct vty *vty) 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 @@ int trx_vty_init(struct trx_ctx* trx) 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 f8556f0c..e82111ea 100644 --- a/Transceiver52M/Makefile.am +++ b/Transceiver52M/Makefile.am @@ -40,7 +40,9 @@ COMMON_SOURCES = \ 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 c9894f07..fed42894 100644 --- a/Transceiver52M/Transceiver.cpp +++ b/Transceiver52M/Transceiver.cpp @@ -29,6 +29,7 @@ #include #include "Transceiver.h" #include +#include extern "C" { #include "osmo_signal.h" @@ -208,6 +209,8 @@ bool Transceiver::init() return false; } + initvita(); + mDataSockets.resize(mChans, -1); @@ -614,6 +617,44 @@ double Transceiver::rssiOffset(size_t chan) 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 *>(&burst.begin()[0]); + std::complex 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 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi) 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 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi) 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 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi) 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 89a577be..8f5cc85d 100644 --- a/Transceiver52M/osmo-trx.cpp +++ b/Transceiver52M/osmo-trx.cpp @@ -77,6 +77,24 @@ static struct ctrl_handle *g_ctrlh; static RadioDevice *usrp; static RadioInterface *radio; +/* adjusts read timestamp offset to make the viterbi equalizer happy by including the start tail bits */ +template +class rif_va_wrapper : public B { + bool use_va; + + public: + template + rif_va_wrapper(bool use_va, Args &&...args) : B(std::forward(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 @@ RadioInterface *makeRadioInterface(struct trx_ctx *trx, 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(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(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(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 @@ int trx_validate_config(struct trx_ctx *trx) 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 cd40ddfd..b05af787 100644 --- a/Transceiver52M/radioInterface.h +++ b/Transceiver52M/radioInterface.h @@ -75,7 +75,7 @@ private: public: /** start the interface */ - bool start(); + virtual bool start(); bool stop(); /** initialization */ @@ -151,7 +151,7 @@ private: 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 @@ private: 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();