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
This commit is contained in:
Eric Wild 2023-07-25 18:44:03 +02:00 committed by laforge
parent a5a2275a08
commit cca5d93f66
6 changed files with 118 additions and 12 deletions

View File

@ -67,4 +67,5 @@ struct trx_cfg {
double ul_gain;
double dl_gain;
} overrides;
bool use_va;
};

View File

@ -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);

View File

@ -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) \

View File

@ -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 @@ 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<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 @@ 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;

View File

@ -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 <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 @@ 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<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 @@ 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;
}

View File

@ -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();