fixeria has uploaded this change for review. ( https://gerrit.osmocom.org/c/osmo-trx/+/43115?usp=email )
Change subject: Transceiver52M: migrate to libosmo-trx ......................................................................
Transceiver52M: migrate to libosmo-trx
Replace the local TRXC/TRXD implementation with libosmo-trx's shared osmo_trxc_msg/osmo_trxd_burst_{ind,req} API, including SETSLOT parsing via osmo_trxc_setslot_parse()/_build(). Take a chance to fix SETSLOT to always send a response, distinguishing bogus input from unsupported VAMOS channel combinations.
Only the TRXC/TRXD build/parse API is used here, not the osmo_trx_ep endpoint API: osmo-trx runs its socket I/O on dedicated threads that don't drive osmo_select_main(), which osmo_trx_ep depends on.
Change-Id: I458ec85c56e2101d073c8c35f11c147191c4ea0c Related: OS#5283 --- M Transceiver52M/Makefile.am M Transceiver52M/Transceiver.cpp M Transceiver52M/Transceiver.h D Transceiver52M/proto_trxd.c D Transceiver52M/proto_trxd.h 5 files changed, 292 insertions(+), 480 deletions(-)
git pull ssh://gerrit.osmocom.org:29418/osmo-trx refs/changes/15/43115/1
diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am index 74152b3..794f8b4 100644 --- a/Transceiver52M/Makefile.am +++ b/Transceiver52M/Makefile.am @@ -23,9 +23,28 @@
SUBDIRS = arch device
-AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device/common -AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) -AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) +AM_CPPFLAGS = \ + -Wall \ + $(STD_DEFINES_AND_INCLUDES) \ + -I${srcdir}/arch/common \ + -I${srcdir}/device/common \ + -I$(top_srcdir)/libosmo-trx/include \ + -I$(top_builddir)/libosmo-trx/include \ + $(NULL) + +AM_CXXFLAGS = \ + -lpthread \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOCTRL_CFLAGS) \ + $(LIBOSMOVTY_CFLAGS) \ + $(NULL) + +AM_CFLAGS = \ + -lpthread \ + $(LIBOSMOCORE_CFLAGS) \ + $(LIBOSMOCTRL_CFLAGS) \ + $(LIBOSMOVTY_CFLAGS) \ + $(NULL)
noinst_LTLIBRARIES = libtransceiver_common.la
@@ -37,7 +56,6 @@ sigProcLib.cpp \ signalVector.cpp \ Transceiver.cpp \ - proto_trxd.c \ grgsm_vitac/grgsm_vitac.cpp \ grgsm_vitac/viterbi_detector.cc
@@ -67,8 +85,7 @@ sigProcLib.h \ signalVector.h \ Transceiver.h \ - Resampler.h \ - proto_trxd.h + Resampler.h
if ENABLE_MULTI_ARFCN noinst_HEADERS += \ @@ -80,6 +97,7 @@
COMMON_LDADD = \ libtransceiver_common.la \ + $(top_builddir)/libosmo-trx/src/libosmotrx.la \ $(ARCH_LA) \ $(GSM_LA) \ $(COMMON_LA) \ diff --git a/Transceiver52M/Transceiver.cpp b/Transceiver52M/Transceiver.cpp index 46d06db..20a568a 100644 --- a/Transceiver52M/Transceiver.cpp +++ b/Transceiver52M/Transceiver.cpp @@ -33,12 +33,14 @@
extern "C" { #include "osmo_signal.h" -#include "proto_trxd.h"
#include <osmocom/core/utils.h> #include <osmocom/core/socket.h> #include <osmocom/core/bits.h> +#include <osmocom/core/msgb.h> #include <osmocom/vty/cpu_sched_vty.h> +#include <osmocom/trx/trxc.h> +#include <osmocom/trx/trxd.h> }
#ifdef HAVE_CONFIG_H @@ -655,6 +657,21 @@ }; #endif
+/* Convert a soft bit normalized to 0..1 (1.0 = confident '1') into the + * sbit_t domain (-127..127) used by osmo_trxd_burst_ind, matching the wire + * encoding of osmo_trxd_burst_ind_build()/soft_bits_build() (byte = 127 - + * sbit): a value of 1.0 rounds to a raw byte of 255, which the TRXD wire + * format reserves as -127 (see soft_bits_parse()), so clamp accordingly. */ +static inline sbit_t float_soft_bit_to_sbit(float x) +{ + int v = 127 - (int) lround(x * 255.0); + if (v > 127) + v = 127; + if (v < -127) + v = -127; + return (sbit_t) v; +} + /* * Pull bursts from the FIFO and handle according to the slot * and burst correlation type. Equalzation is currently disabled. @@ -662,7 +679,7 @@ * -ENOENT: timeslot is off (fn and tn in bi are filled), * -EIO: read error */ -int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi) +int Transceiver::pullRadioVector(size_t chan, struct osmo_trxd_burst_ind *bi) { int rc; struct estim_burst_params ebp; @@ -675,6 +692,7 @@ TransceiverState *state = &mStates[chan]; bool ctr_changed = false; double rssi_offset; + float soft_bits[OSMO_TRXD_BURST_LEN_MAX]; static complex burst_shift_buffer[625]; static signalVector shift_vec(burst_shift_buffer, 0, 625, dummy_alloc, dummy_free); signalVector *shvec_ptr = &shift_vec; @@ -691,17 +709,12 @@ CorrType type = expectedCorrType(burstTime, chan);
/* Initialize struct bi */ - bi->nbits = 0; + memset(bi, 0, sizeof(*bi)); + bi->flags = OSMO_TRXD_F_MOD_TYPE | OSMO_TRXD_F_TS_INFO | OSMO_TRXD_F_CI_CB; bi->fn = burstTime.FN(); bi->tn = burstTime.TN(); - bi->rssi = 0.0; - bi->toa = 0.0; - bi->noise = 0.0; - bi->idle = false; - bi->modulation = MODULATION_GMSK; - bi->tss = 0; /* TODO: we only support tss 0 right now */ - bi->tsc = 0; - bi->ci = 0.0; + bi->mod = OSMO_TRXD_MOD_T_GMSK; + bi->tsc_set = 0; /* TODO: we only support tsc_set 0 right now */
/* Debug: dump bursts to disk */ /* bits 0-7 - chan 0 timeslots @@ -748,8 +761,7 @@ }
rssi_offset = rssiOffset(chan); - bi->rssi = 20.0 * log10(rxFullScale / avg) + rssi_offset; - bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssi_offset; + bi->rssi = (int8_t) lround(-(20.0 * log10(rxFullScale / avg) + rssi_offset));
if (type == IDLE) goto ret_idle; @@ -786,21 +798,23 @@ rxBurst = demodAnyBurst(*shvec_ptr, (CorrType)rc, cfg->rx_sps, &ebp); }
- bi->toa = ebp.toa; + bi->toa256 = (int16_t) lround(ebp.toa * 256.0); bi->tsc = ebp.tsc; - bi->ci = ebp.ci; + bi->ci_cb = (int16_t) ((ebp.ci * 10) + 0.5);
/* EDGE demodulator returns 444 (gSlotLen * 3) bits */ if (rxBurst->size() == EDGE_BURST_NBITS) { - bi->modulation = MODULATION_8PSK; - bi->nbits = EDGE_BURST_NBITS; + bi->mod = OSMO_TRXD_MOD_T_8PSK; + bi->burst_len = EDGE_BURST_NBITS; } else { /* size() here is actually gSlotLen + 8, due to guard periods */ - bi->modulation = MODULATION_GMSK; - bi->nbits = gSlotLen; + bi->mod = OSMO_TRXD_MOD_T_GMSK; + bi->burst_len = gSlotLen; }
- // Convert -1..+1 soft bits to 0..1 soft bits - vectorSlicer(bi->rx_burst, rxBurst->begin(), bi->nbits); + // Convert -1..+1 soft bits to 0..1 soft bits, then to the sbit_t wire domain + vectorSlicer(soft_bits, rxBurst->begin(), bi->burst_len); + for (size_t i = 0; i < bi->burst_len; i++) + bi->burst[i] = float_soft_bit_to_sbit(soft_bits[i]);
delete rxBurst; delete radio_burst; @@ -809,7 +823,8 @@ ret_idle: if (ctr_changed) dispatch_trx_rate_ctr_change(state, chan); - bi->idle = true; + bi->flags |= OSMO_TRXD_F_NOPE_IND; + bi->burst_len = 0; delete radio_burst; return 0; } @@ -820,36 +835,6 @@ mTxPriorityQueues[i].clear(); }
- -/** - * Matches a buffer with a command. - * @param buf a buffer to look command in - * @param cmd a command to look in buffer - * @param params pointer to arguments, or NULL - * @return true if command matches, otherwise false - */ -static bool match_cmd(const char *buf, - const char *cmd, const char **params) -{ - size_t cmd_len = strlen(cmd); - - /* Check a command itself */ - if (strncmp(buf, cmd, cmd_len)) - return false; - - /* A command has arguments */ - if (params != NULL) { - /* Make sure there is a space */ - if (buf[cmd_len] != ' ') - return false; - - /* Update external pointer */ - *params = buf + cmd_len + 1; - } - - return true; -} - void Transceiver::ctrl_sock_send(ctrl_msg& m, int chan) { ctrl_sock_state& s = mCtrlSockets[chan]; @@ -896,17 +881,15 @@
int Transceiver::ctrl_sock_handle_rx(int chan) { + struct osmo_trxc_msg cmd, rsp; ctrl_msg cmd_received; ctrl_msg cmd_to_send; - char *buffer = cmd_received.data; - char *response = cmd_to_send.data; - const size_t response_size = sizeof(cmd_to_send.data); - const char *command, *params; int msgLen; + int rc; ctrl_sock_state& s = mCtrlSockets[chan];
/* Attempt to read from control socket */ - msgLen = read(s.conn_bfd.fd, buffer, sizeof(cmd_received.data)-1); + msgLen = read(s.conn_bfd.fd, cmd_received.data, sizeof(cmd_received.data) - 1); if (msgLen < 0 && errno == EAGAIN) return 0; /* Try again later */ if (msgLen <= 0) { @@ -914,184 +897,196 @@ return -EIO; }
- - /* Zero-terminate received string */ - buffer[msgLen] = '\0'; - - /* Verify a command signature */ - if (strncmp(buffer, "CMD ", 4)) { + rc = osmo_trxc_msg_parse(&cmd, cmd_received.data, msgLen); + if (rc < 0 || cmd.type != OSMO_TRXC_MT_CMD) { LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface"; return -EIO; }
- /* Set command pointer */ - command = buffer + 4; - LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'"; + memset(&rsp, 0, sizeof(rsp)); + rsp.type = OSMO_TRXC_MT_RSP; + rsp.status = 1; /* default: NACK, cleared to 0 on the success path of each verb below */ + snprintf(rsp.cmd, sizeof(rsp.cmd), "%s", cmd.cmd);
- if (match_cmd(command, "POWEROFF", NULL)) { + LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << osmo_trxc_msg_name(&cmd) << "'"; + + if (!strcmp(cmd.cmd, "POWEROFF")) { stop(); - snprintf(response, response_size, "RSP POWEROFF 0"); - } else if (match_cmd(command, "POWERON", NULL)) { - if (!start()) { - snprintf(response, response_size, "RSP POWERON 1"); - } else { - snprintf(response, response_size, "RSP POWERON 0"); + rsp.status = 0; + } else if (!strcmp(cmd.cmd, "POWERON")) { + if (start()) { + rsp.status = 0; for (int i = 0; i < 8; i++) { for (int j = 0; j < 8; j++) mHandover[i][j] = false; } } - } else if (match_cmd(command, "HANDOVER", ¶ms)) { + } else if (!strcmp(cmd.cmd, "HANDOVER")) { unsigned ts = 0, ss = 0; - sscanf(params, "%u %u", &ts, &ss); - if (ts > 7 || ss > 7) { - snprintf(response, response_size, "RSP HANDOVER 1 %u %u", ts, ss); - } else { + if (osmo_trxc_msg_params_scan(&cmd, "%u %u", &ts, &ss) == 2 && ts <= 7 && ss <= 7) { mHandover[ts][ss] = true; - snprintf(response, response_size, "RSP HANDOVER 0 %u %u", ts, ss); + rsp.status = 0; } - } else if (match_cmd(command, "NOHANDOVER", ¶ms)) { + snprintf(rsp.params, sizeof(rsp.params), "%u %u", ts, ss); + } else if (!strcmp(cmd.cmd, "NOHANDOVER")) { unsigned ts = 0, ss = 0; - sscanf(params, "%u %u", &ts, &ss); - if (ts > 7 || ss > 7) { - snprintf(response, response_size, "RSP NOHANDOVER 1 %u %u", ts, ss); - } else { + if (osmo_trxc_msg_params_scan(&cmd, "%u %u", &ts, &ss) == 2 && ts <= 7 && ss <= 7) { mHandover[ts][ss] = false; - snprintf(response, response_size, "RSP NOHANDOVER 0 %u %u", ts, ss); + rsp.status = 0; } - } else if (match_cmd(command, "SETMAXDLY", ¶ms)) { + snprintf(rsp.params, sizeof(rsp.params), "%u %u", ts, ss); + } else if (!strcmp(cmd.cmd, "SETMAXDLY")) { //set expected maximum time-of-arrival for Access Bursts - int maxDelay; - sscanf(params, "%d", &maxDelay); - mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km - snprintf(response, response_size, "RSP SETMAXDLY 0 %d", maxDelay); - } else if (match_cmd(command, "SETMAXDLYNB", ¶ms)) { + int maxDelay = 0; + if (osmo_trxc_msg_params_scan(&cmd, "%d", &maxDelay) == 1) { + mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km + rsp.status = 0; + } + snprintf(rsp.params, sizeof(rsp.params), "%d", maxDelay); + } else if (!strcmp(cmd.cmd, "SETMAXDLYNB")) { //set expected maximum time-of-arrival for Normal Bursts - int maxDelay; - sscanf(params, "%d", &maxDelay); - mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km - snprintf(response, response_size, "RSP SETMAXDLYNB 0 %d", maxDelay); - } else if (match_cmd(command, "SETRXGAIN", ¶ms)) { - int newGain; - sscanf(params, "%d", &newGain); - newGain = mRadioInterface->setRxGain(newGain, chan); - snprintf(response, response_size, "RSP SETRXGAIN 0 %d", newGain); - } else if (match_cmd(command, "NOISELEV", NULL)) { + int maxDelay = 0; + if (osmo_trxc_msg_params_scan(&cmd, "%d", &maxDelay) == 1) { + mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km + rsp.status = 0; + } + snprintf(rsp.params, sizeof(rsp.params), "%d", maxDelay); + } else if (!strcmp(cmd.cmd, "SETRXGAIN")) { + int newGain = 0; + if (osmo_trxc_msg_params_scan(&cmd, "%d", &newGain) == 1) { + newGain = mRadioInterface->setRxGain(newGain, chan); + rsp.status = 0; + } + snprintf(rsp.params, sizeof(rsp.params), "%d", newGain); + } else if (!strcmp(cmd.cmd, "NOISELEV")) { if (mOn) { float lev = mStates[chan].mNoiseLev; - snprintf(response, response_size, "RSP NOISELEV 0 %d", + rsp.status = 0; + snprintf(rsp.params, sizeof(rsp.params), "%d", (int) round(20.0 * log10(rxFullScale / lev))); - } - else { - snprintf(response, response_size, "RSP NOISELEV 1 0"); - } - } else if (match_cmd(command, "SETPOWER", ¶ms)) { - int power; - sscanf(params, "%d", &power); - power = mRadioInterface->setPowerAttenuation(power, chan); - mStates[chan].mPower = power; - snprintf(response, response_size, "RSP SETPOWER 0 %d", power); - } else if (match_cmd(command, "ADJPOWER", ¶ms)) { - int power, step; - sscanf(params, "%d", &step); - power = mStates[chan].mPower + step; - power = mRadioInterface->setPowerAttenuation(power, chan); - mStates[chan].mPower = power; - snprintf(response, response_size, "RSP ADJPOWER 0 %d", power); - } else if (match_cmd(command, "NOMTXPOWER", NULL)) { - int power = mRadioInterface->getNominalTxPower(chan); - snprintf(response, response_size, "RSP NOMTXPOWER 0 %d", power); - } else if (match_cmd(command, "RXTUNE", ¶ms)) { - // tune receiver - int freqKhz; - sscanf(params, "%d", &freqKhz); - mRxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3; - if (!mRadioInterface->tuneRx(mRxFreq, chan)) { - LOGCHAN(chan, DTRXCTRL, FATAL) << "RX failed to tune"; - snprintf(response, response_size, "RSP RXTUNE 1 %d", freqKhz); - } - else - snprintf(response, response_size, "RSP RXTUNE 0 %d", freqKhz); - } else if (match_cmd(command, "TXTUNE", ¶ms)) { - // tune txmtr - int freqKhz; - sscanf(params, "%d", &freqKhz); - mTxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3; - if (!mRadioInterface->tuneTx(mTxFreq, chan)) { - LOGCHAN(chan, DTRXCTRL, FATAL) << "TX failed to tune"; - snprintf(response, response_size, "RSP TXTUNE 1 %d", freqKhz); - } - else - snprintf(response, response_size, "RSP TXTUNE 0 %d", freqKhz); - } else if (match_cmd(command, "SETTSC", ¶ms)) { - // set TSC - unsigned TSC; - sscanf(params, "%u", &TSC); - if (TSC > 7) { - snprintf(response, response_size, "RSP SETTSC 1 %d", TSC); } else { + snprintf(rsp.params, sizeof(rsp.params), "0"); + } + } else if (!strcmp(cmd.cmd, "SETPOWER")) { + int power = 0; + if (osmo_trxc_msg_params_scan(&cmd, "%d", &power) == 1) { + power = mRadioInterface->setPowerAttenuation(power, chan); + mStates[chan].mPower = power; + rsp.status = 0; + } + snprintf(rsp.params, sizeof(rsp.params), "%d", power); + } else if (!strcmp(cmd.cmd, "ADJPOWER")) { + int power = mStates[chan].mPower, step; + if (osmo_trxc_msg_params_scan(&cmd, "%d", &step) == 1) { + power = mStates[chan].mPower + step; + power = mRadioInterface->setPowerAttenuation(power, chan); + mStates[chan].mPower = power; + rsp.status = 0; + } + snprintf(rsp.params, sizeof(rsp.params), "%d", power); + } else if (!strcmp(cmd.cmd, "NOMTXPOWER")) { + int power = mRadioInterface->getNominalTxPower(chan); + rsp.status = 0; + snprintf(rsp.params, sizeof(rsp.params), "%d", power); + } else if (!strcmp(cmd.cmd, "RXTUNE")) { + // tune receiver + int freqKhz = 0; + if (osmo_trxc_msg_params_scan(&cmd, "%d", &freqKhz) == 1) { + mRxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3; + if (!mRadioInterface->tuneRx(mRxFreq, chan)) + LOGCHAN(chan, DTRXCTRL, FATAL) << "RX failed to tune"; + else + rsp.status = 0; + } + snprintf(rsp.params, sizeof(rsp.params), "%d", freqKhz); + } else if (!strcmp(cmd.cmd, "TXTUNE")) { + // tune txmtr + int freqKhz = 0; + if (osmo_trxc_msg_params_scan(&cmd, "%d", &freqKhz) == 1) { + mTxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3; + if (!mRadioInterface->tuneTx(mTxFreq, chan)) + LOGCHAN(chan, DTRXCTRL, FATAL) << "TX failed to tune"; + else + rsp.status = 0; + } + snprintf(rsp.params, sizeof(rsp.params), "%d", freqKhz); + } else if (!strcmp(cmd.cmd, "SETTSC")) { + // set TSC + unsigned TSC = 0; + if (osmo_trxc_msg_params_scan(&cmd, "%u", &TSC) == 1 && TSC <= 7) { LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC; mTSC = TSC; - snprintf(response, response_size, "RSP SETTSC 0 %d", TSC); + rsp.status = 0; } - } else if (match_cmd(command, "SETSLOT", ¶ms)) { + snprintf(rsp.params, sizeof(rsp.params), "%u", TSC); + } else if (!strcmp(cmd.cmd, "SETSLOT")) { // set slot type - int corrCode; - int timeslot; - sscanf(params, "%d %d", ×lot, &corrCode); - if ((timeslot < 0) || (timeslot > 7)) { + struct osmo_trxc_setslot ss; + if (osmo_trxc_setslot_parse(&ss, &cmd) < 0) { LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus message on control interface"; - snprintf(response, response_size, "RSP SETSLOT 1 %d %d", timeslot, corrCode); - return 0; + } else if (ss.vamos) { + LOGCHAN(chan, DTRXCTRL, NOTICE) << "VAMOS channel combinations are not supported"; + } else { + mStates[chan].chanType[ss.tn] = (ChannelCombination) ss.chan_comb; + setModulus(ss.tn, chan); + rsp.status = 0; } - mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode; - setModulus(timeslot, chan); - snprintf(response, response_size, "RSP SETSLOT 0 %d %d", timeslot, corrCode); - } else if (match_cmd(command, "SETFORMAT", ¶ms)) { + snprintf(rsp.params, sizeof(rsp.params), "%s", cmd.params); + } else if (!strcmp(cmd.cmd, "SETFORMAT")) { // set TRXD protocol version - unsigned version_recv; - sscanf(params, "%u", &version_recv); + unsigned version_recv = 0; + osmo_trxc_msg_params_scan(&cmd, "%u", &version_recv); LOGCHAN(chan, DTRXCTRL, INFO) << "BTS requests TRXD version switch: " << version_recv; if (version_recv > TRX_DATA_FORMAT_VER) { LOGCHAN(chan, DTRXCTRL, INFO) << "rejecting TRXD version " << version_recv << " in favor of " << TRX_DATA_FORMAT_VER; - snprintf(response, response_size, "RSP SETFORMAT %u %u", TRX_DATA_FORMAT_VER, version_recv); + rsp.status = TRX_DATA_FORMAT_VER; } else { LOGCHAN(chan, DTRXCTRL, NOTICE) << "switching to TRXD version " << version_recv; mVersionTRXD[chan] = version_recv; - snprintf(response, response_size, "RSP SETFORMAT %u %u", version_recv, version_recv); + rsp.status = version_recv; } - } else if (match_cmd(command, "RFMUTE", ¶ms)) { + snprintf(rsp.params, sizeof(rsp.params), "%u", version_recv); + } else if (!strcmp(cmd.cmd, "RFMUTE")) { // (Un)mute RF TX and RX - unsigned mute; - sscanf(params, "%u", &mute); - mStates[chan].mMuted = mute ? true : false; - snprintf(response, response_size, "RSP RFMUTE 0 %u", mute); - } else if (match_cmd(command, "_SETBURSTTODISKMASK", ¶ms)) { + unsigned mute = 0; + if (osmo_trxc_msg_params_scan(&cmd, "%u", &mute) == 1) { + mStates[chan].mMuted = mute ? true : false; + rsp.status = 0; + } + snprintf(rsp.params, sizeof(rsp.params), "%u", mute); + } else if (!strcmp(cmd.cmd, "_SETBURSTTODISKMASK")) { // debug command! may change or disappear without notice // set a mask which bursts to dump to disk - int mask; - sscanf(params, "%d", &mask); - mWriteBurstToDiskMask = mask; - snprintf(response, response_size, "RSP _SETBURSTTODISKMASK 0 %d", mask); + int mask = 0; + if (osmo_trxc_msg_params_scan(&cmd, "%d", &mask) == 1) { + mWriteBurstToDiskMask = mask; + rsp.status = 0; + } + snprintf(rsp.params, sizeof(rsp.params), "%d", mask); } else { - LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus command " << command << " on control interface."; - snprintf(response, response_size, "RSP ERR 1"); + LOGCHAN(chan, DTRXCTRL, NOTICE) << "bogus command " << cmd.cmd << " on control interface."; + snprintf(rsp.cmd, sizeof(rsp.cmd), "%s", OSMO_TRXC_CMD_ERR); }
- LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << response << "'"; - transceiver->ctrl_sock_send(cmd_to_send, chan); + LOGCHAN(chan, DTRXCTRL, INFO) << "response is '" << osmo_trxc_msg_name(&rsp) << "'"; + + rc = osmo_trxc_msg_build(cmd_to_send.data, sizeof(cmd_to_send.data), &rsp); + if (rc < 0) { + LOGCHAN(chan, DTRXCTRL, ERROR) << "failed to build response (rc=" << rc << ")"; + return -EIO; + } + ctrl_sock_send(cmd_to_send, chan); return 0; }
bool Transceiver::driveTxPriorityQueue(size_t chan) { + char buffer[OSMO_TRXD_MSG_BUF_SIZE]; int msgLen; - int burstLen; - struct trxd_hdr_v01_dl *dl; - char buffer[sizeof(*dl) + EDGE_BURST_NBITS]; - uint32_t fn; - uint8_t tn; + struct osmo_trxd_parse_state st; + struct osmo_trxd_burst_req br; + int rc;
// check data socket msgLen = read(mDataSockets[chan], buffer, sizeof(buffer)); @@ -1100,48 +1095,37 @@ return false; }
- switch (msgLen) { - case sizeof(*dl) + gSlotLen: /* GSM burst */ - burstLen = gSlotLen; - break; - case sizeof(*dl) + EDGE_BURST_NBITS: /* EDGE burst */ - if (cfg->tx_sps != 4) { - LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << cfg->tx_sps; - return false; - } - burstLen = EDGE_BURST_NBITS; - break; - default: - LOGCHAN(chan, DTRXDDL, ERROR) << "badly formatted packet on GSM->TRX interface (len="<< msgLen << ")"; - return false; - } - - dl = (struct trxd_hdr_v01_dl *) buffer; - - /* Convert TDMA FN to the host endianness */ - fn = osmo_load32be(&dl->common.fn); - tn = dl->common.tn; - - /* Make sure we support the received header format */ - switch (dl->common.version) { - case 0: - /* Version 1 has the same format */ - case 1: - break; - default: - LOGCHAN(chan, DTRXDDL, ERROR) << "Rx TRXD message with unknown header version " << unsigned(dl->common.version); + osmo_trxd_parse_state_init(&st); + rc = osmo_trxd_burst_req_parse(&st, &br, (const uint8_t *) buffer, msgLen); + if (rc < 0) { + LOGCHAN(chan, DTRXDDL, ERROR) << "failed to parse BURST.req (rc=" << rc << ")"; return false; }
- LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message (hdr_ver=" << unsigned(dl->common.version) - << "): fn=" << fn << ", tn=" << unsigned(tn) << ", burst_len=" << burstLen; + switch (br.burst_len) { + case gSlotLen: /* GSM burst */ + break; + case EDGE_BURST_NBITS: /* EDGE burst */ + if (cfg->tx_sps != 4) { + LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << cfg->tx_sps; + return false; + } + break; + default: + LOGCHAN(chan, DTRXDDL, ERROR) << "badly formatted packet on GSM->TRX interface (burst_len=" + << br.burst_len << ")"; + return false; + } + + LOGCHAN(chan, DTRXDDL, DEBUG) << "Rx TRXD message: fn=" << br.fn << ", tn=" << unsigned(br.tn) + << ", burst_len=" << br.burst_len;
TransceiverState *state = &mStates[chan]; - GSM::Time currTime = GSM::Time(fn, tn); + GSM::Time currTime = GSM::Time(br.fn, br.tn);
/* Verify proper FN order in DL stream */ - if (state->first_dl_fn_rcv[tn]) { - int32_t delta = GSM::FNDelta(currTime.FN(), state->last_dl_time_rcv[tn].FN()); + if (state->first_dl_fn_rcv[br.tn]) { + int32_t delta = GSM::FNDelta(currTime.FN(), state->last_dl_time_rcv[br.tn].FN()); if (delta == 1) { /* usual expected scenario, continue code flow */ } else if (delta == 0) { @@ -1151,7 +1135,7 @@ return true; } else if (delta < 0) { LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with previous FN " << currTime - << " vs last " << state->last_dl_time_rcv[tn]; + << " vs last " << state->last_dl_time_rcv[br.tn]; state->ctrs.tx_trxd_fn_outoforder++; dispatch_trx_rate_ctr_change(state, chan); /* Allow adding radio vector below, since it gets sorted in the queue */ @@ -1161,25 +1145,25 @@ * setups. Also, osmo-trx supports optionally filling empty bursts on * its own. In that case bts-trx is not obliged to submit all bursts. */ LOGCHAN(chan, DTRXDDL, INFO) << "Rx TRXD msg with future FN " << currTime - << " vs last " << state->last_dl_time_rcv[tn] + << " vs last " << state->last_dl_time_rcv[br.tn] << ", " << delta - 1 << " FN lost"; state->ctrs.tx_trxd_fn_skipped += delta - 1; dispatch_trx_rate_ctr_change(state, chan); } if (delta > 0) - state->last_dl_time_rcv[tn] = currTime; + state->last_dl_time_rcv[br.tn] = currTime; } else { /* Initial check, simply store state */ - state->first_dl_fn_rcv[tn] = true; - state->last_dl_time_rcv[tn] = currTime; + state->first_dl_fn_rcv[br.tn] = true; + state->last_dl_time_rcv[br.tn] = currTime; }
- BitVector newBurst(burstLen); + BitVector newBurst(br.burst_len); BitVector::iterator itr = newBurst.begin(); - uint8_t *bufferItr = dl->soft_bits; + const ubit_t *bufferItr = br.burst; while (itr < newBurst.end()) *itr++ = *bufferItr++;
- addRadioVector(chan, newBurst, dl->tx_att, currTime); + addRadioVector(chan, newBurst, br.att, currTime);
return true; } @@ -1203,32 +1187,36 @@ return true; }
-void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi) +void Transceiver::logRxBurst(size_t chan, const struct osmo_trxd_burst_ind *bi) { std::ostringstream os; - for (size_t i=0; i < bi->nbits; i++) { - if (bi->rx_burst[i] > 0.5) os << "1"; - else if (bi->rx_burst[i] > 0.25) os << "|"; - else if (bi->rx_burst[i] > 0.0) os << "'"; + for (size_t i = 0; i < bi->burst_len; i++) { + /* sbit_t polarity: <0 confident '1', 127 confident '0' (see float_soft_bit_to_sbit()) */ + if (bi->burst[i] < 0) os << "1"; + else if (bi->burst[i] < 64) os << "|"; + else if (bi->burst[i] < 127) os << "'"; else os << "-"; }
double rssi_offset = rssiOffset(chan); + double noise_dbfs = 20.0 * log10(rxFullScale / mStates[chan].mNoiseLev) + rssi_offset; + double rssi_dbfs = -bi->rssi - rssi_offset;
LOGCHAN(chan, DTRXDUL, DEBUG) << std::fixed << std::right << " time: " << unsigned(bi->tn) << ":" << bi->fn - << " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssi_offset) - << "dBFS/" << std::setw(6) << -bi->rssi << "dBm" - << " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssi_offset) - << "dBFS/" << std::setw(6) << -bi->noise << "dBm" - << " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa - << " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB" + << " RSSI: " << std::setw(5) << std::setprecision(1) << rssi_dbfs + << "dBFS/" << std::setw(6) << int(bi->rssi) << "dBm" + << " noise: " << std::setw(5) << std::setprecision(1) << (noise_dbfs - rssi_offset) + << "dBFS/" << std::setw(6) << -noise_dbfs << "dBm" + << " TOA: " << std::setw(5) << std::setprecision(2) << (bi->toa256 / 256.0) + << " C/I: " << std::setw(5) << std::setprecision(2) << (bi->ci_cb / 10.0) << "dB" << " bits: " << os; }
bool Transceiver::driveReceiveFIFO(size_t chan) { - struct trx_ul_burst_ind bi; + struct osmo_trxd_burst_ind bi; + struct msgb *msg; int rc;
if ((rc = pullRadioVector(chan, &bi)) < 0) { @@ -1239,17 +1227,29 @@ return false; /* other errors: we want to stop the process */ }
- if (!bi.idle && log_check_level(DTRXDUL, LOGL_DEBUG)) + if (!(bi.flags & OSMO_TRXD_F_NOPE_IND) && log_check_level(DTRXDUL, LOGL_DEBUG)) logRxBurst(chan, &bi);
- switch (mVersionTRXD[chan]) { - case 0: - return trxd_send_burst_ind_v0(chan, mDataSockets[chan], &bi); - case 1: - return trxd_send_burst_ind_v1(chan, mDataSockets[chan], &bi); - default: - OSMO_ASSERT(false); + msg = msgb_alloc(OSMO_TRXD_MSG_BUF_SIZE, "trxd_burst_ind"); + if (!msg) + return false; + + rc = osmo_trxd_burst_ind_build(msg, mVersionTRXD[chan], &bi); + if (rc < 0) { + LOGCHAN(chan, DTRXDUL, ERROR) << "failed to build BURST.ind (rc=" << rc << ")"; + msgb_free(msg); + return false; } + osmo_trxd_build_fin(msg, mVersionTRXD[chan]); + + rc = write(mDataSockets[chan], msgb_data(msg), msgb_length(msg)); + msgb_free(msg); + if (rc < 0) { + LOGCHAN(chan, DTRXDUL, NOTICE) << "mDataSockets write(" << mDataSockets[chan] << ") failed: " << rc; + return false; + } + + return true; }
void Transceiver::driveTxFIFO() @@ -1310,16 +1310,22 @@
bool Transceiver::writeClockInterface() { - int msgLen; - char command[50]; + char command[64]; + int rc; // FIXME -- This should be adaptive. - sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2)); + uint32_t fn = mTransmitDeadlineClock.FN() + 2; + + rc = osmo_trxc_clock_ind_build(command, sizeof(command), fn); + if (rc < 0) { + LOGC(DTRXCLK, ERROR) << "failed to build IND CLOCK " << fn; + return false; + }
LOGC(DTRXCLK, INFO) << "sending " << command;
- msgLen = write(mClockSocket, command, strlen(command) + 1); - if (msgLen <= 0) { - LOGC(DTRXCLK, ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << msgLen; + rc = write(mClockSocket, command, strlen(command) + 1); + if (rc <= 0) { + LOGC(DTRXCLK, ERROR) << "mClockSocket write(" << mClockSocket << ") failed: " << rc; return false; }
diff --git a/Transceiver52M/Transceiver.h b/Transceiver52M/Transceiver.h index babe420..8d27db9 100644 --- a/Transceiver52M/Transceiver.h +++ b/Transceiver52M/Transceiver.h @@ -34,6 +34,8 @@ extern "C" { #include <osmocom/core/signal.h> #include <osmocom/core/select.h> +#include <osmocom/trx/trxc.h> +#include <osmocom/trx/trxd.h> #include "config_defs.h" }
@@ -41,6 +43,9 @@
extern Transceiver *transceiver;
+/* The latest TRXD header format version advertised/accepted by this TRX implementation */ +#define TRX_DATA_FORMAT_VER 1 + /** Channel descriptor for transceiver object and channel number pair */ struct TrxChanThParams { Transceiver *trx; @@ -150,10 +155,9 @@ private: size_t mChans; struct ctrl_msg { - char data[101]; + char data[OSMO_TRXC_MSG_BUF_SIZE]; ctrl_msg() {}; }; - struct ctrl_sock_state { osmo_fd conn_bfd; std::deque<ctrl_msg> txmsgqueue; @@ -202,7 +206,7 @@ void pushRadioVector(GSM::Time &nowTime);
/** Pull and demodulate a burst from the receive FIFO */ - int pullRadioVector(size_t chan, struct trx_ul_burst_ind *ind); + int pullRadioVector(size_t chan, struct osmo_trxd_burst_ind *ind);
/** Set modulus for specific timeslot */ void setModulus(size_t timeslot, size_t chan); @@ -264,7 +268,7 @@ double rssiOffset(size_t chan); void reset();
- void logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi); + void logRxBurst(size_t chan, const struct osmo_trxd_burst_ind *bi); };
void *RxUpperLoopAdapter(TrxChanThParams *params); diff --git a/Transceiver52M/proto_trxd.c b/Transceiver52M/proto_trxd.c deleted file mode 100644 index e5a889d..0000000 --- a/Transceiver52M/proto_trxd.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * Copyright (C) 2019 sysmocom - s.f.m.c. GmbH - * All Rights Reserved - * - * SPDX-License-Identifier: AGPL-3.0+ - * - * Author: Pau Espin Pedrol pespin@sysmocom.de - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see http://www.gnu.org/licenses/. - * See the COPYING file in the main directory for details. - */ - -#include "proto_trxd.h" - -#include <osmocom/core/bits.h> - -static void trxd_fill_common(struct trxd_hdr_common *common, const struct trx_ul_burst_ind *bi, uint8_t version) -{ - common->version = version & 0b1111; - common->reserved = 0; - common->tn = bi->tn; - osmo_store32be(bi->fn, &common->fn); -} - -static void trxd_fill_v0_specific(struct trxd_hdr_v0_specific *v0, const struct trx_ul_burst_ind *bi) -{ - int toa_int; - - /* in 1/256 symbols, round to closest integer */ - toa_int = (int) (bi->toa * 256.0 + 0.5); - v0->rssi = bi->rssi; - osmo_store16be(toa_int, &v0->toa); -} - -static void trxd_fill_v1_specific(struct trxd_hdr_v1_specific *v1, const struct trx_ul_burst_ind *bi) -{ - int16_t ci_int_cB; - - /* deciBels->centiBels, round to closest integer */ - ci_int_cB = (int16_t)((bi->ci * 10) + 0.5); - - v1->idle = !!bi->idle; - v1->modulation = (bi->modulation == MODULATION_GMSK) ? - TRXD_MODULATION_GMSK(bi->tss) : - TRXD_MODULATION_8PSK(bi->tss); - v1->tsc = bi->tsc; - osmo_store16be(ci_int_cB, &v1->ci); -} - -static void trxd_fill_burst_normalized255(uint8_t* soft_bits, const struct trx_ul_burst_ind *bi) -{ - unsigned i; - for (i = 0; i < bi->nbits; i++) - soft_bits[i] = (uint8_t) round(bi->rx_burst[i] * 255.0); -} - -bool trxd_send_burst_ind_v0(size_t chan, int fd, const struct trx_ul_burst_ind *bi) { - int rc; - - /* v0 doesn't support idle frames, they are simply dropped, not sent */ - if (bi->idle) - return true; - - /* +2: Historically (OpenBTS times), two extra non-used bytes are sent appended to each burst */ - char buf[sizeof(struct trxd_hdr_v0) + bi->nbits + 2]; - struct trxd_hdr_v0* pkt = (struct trxd_hdr_v0*)buf; - - trxd_fill_common(&pkt->common, bi, 0); - trxd_fill_v0_specific(&pkt->v0, bi); - trxd_fill_burst_normalized255(&pkt->soft_bits[0], bi); - - /* +1: Historical reason. There's an uninitizalied byte in there: pkt->soft_bits[bi->nbits] */ - pkt->soft_bits[bi->nbits + 1] = '\0'; - - rc = write(fd, buf, sizeof(struct trxd_hdr_v0) + bi->nbits + 2); - if (rc <= 0) { - CLOGCHAN(chan, DMAIN, LOGL_NOTICE, "mDataSockets write(%d) failed: %d\n", fd, rc); - return false; - } - return true; -} - -bool trxd_send_burst_ind_v1(size_t chan, int fd, const struct trx_ul_burst_ind *bi) { - int rc; - size_t buf_len; - - buf_len = sizeof(struct trxd_hdr_v1); - if (!bi->idle) - buf_len += bi->nbits; - char buf[buf_len]; - - struct trxd_hdr_v1* pkt = (struct trxd_hdr_v1*)buf; - trxd_fill_common(&pkt->common, bi, 1); - trxd_fill_v0_specific(&pkt->v0, bi); - trxd_fill_v1_specific(&pkt->v1, bi); - - if (!bi->idle) - trxd_fill_burst_normalized255(&pkt->soft_bits[0], bi); - - rc = write(fd, buf, buf_len); - if (rc <= 0) { - CLOGCHAN(chan, DMAIN, LOGL_NOTICE, "mDataSockets write(%d) failed: %d\n", fd, rc); - return false; - } - return true; -} diff --git a/Transceiver52M/proto_trxd.h b/Transceiver52M/proto_trxd.h deleted file mode 100644 index c250a74..0000000 --- a/Transceiver52M/proto_trxd.h +++ /dev/null @@ -1,99 +0,0 @@ -#pragma once - -#include <stdint.h> -#include <stdbool.h> -#include <unistd.h> -#include <math.h> - -#include <osmocom/core/endian.h> - -#include "debug.h" - -#define MAX_RX_BURST_BUF_SIZE 444 /* 444 = EDGE_BURST_NBITS */ - -enum Modulation { - MODULATION_GMSK, - MODULATION_8PSK, -/* Not supported yet: - MODULATION_AQPSK, - MODULATION_16QAM, - MODULATION_32QAM -*/ -}; - -struct trx_ul_burst_ind { - float rx_burst[MAX_RX_BURST_BUF_SIZE]; /* soft bits normalized 0..1 */ - unsigned nbits; // number of symbols per slot in rxBurst, not counting guard periods - uint32_t fn; // TDMA frame number - uint8_t tn; // TDMA time-slot number - double rssi; // in dBFS - double toa; // in symbols - double noise; // noise level in dBFS - bool idle; // true if no valid burst is included - enum Modulation modulation; // modulation type - uint8_t tss; // training sequence set - uint8_t tsc; // training sequence code - float ci; // Carrier-to-Interference ratio, in dB -}; - -bool trxd_send_burst_ind_v0(size_t chan, int fd, const struct trx_ul_burst_ind *bi); -bool trxd_send_burst_ind_v1(size_t chan, int fd, const struct trx_ul_burst_ind *bi); - -/* The latest supported TRXD header format version */ -#define TRX_DATA_FORMAT_VER 1 - -struct trxd_hdr_common { -#if OSMO_IS_LITTLE_ENDIAN - uint8_t tn:3, - reserved:1, - version:4; -#elif OSMO_IS_BIG_ENDIAN -/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ - uint8_t version:4, reserved:1, tn:3; -#endif - uint32_t fn; /* big endian */ -} __attribute__ ((packed)); - -struct trxd_hdr_v0_specific { - uint8_t rssi; - uint16_t toa; /* big endian */ -} __attribute__ ((packed)); - -struct trxd_hdr_v0 { - struct trxd_hdr_common common; - struct trxd_hdr_v0_specific v0; - uint8_t soft_bits[0]; -} __attribute__ ((packed)); - -/* Downlink burst (BTS->TRX), v0 anf v1 use same format */ -struct trxd_hdr_v01_dl { - struct trxd_hdr_common common; - uint8_t tx_att; /* Tx Attentuation */ - uint8_t soft_bits[0]; -} __attribute__ ((packed)); - - -#define TRXD_MODULATION_GMSK(ts_set) (0b0000 | (ts_set & 0b0011)) -#define TRXD_MODULATION_8PSK(ts_set) (0b0100 | (ts_set & 0b0001)) -#define TRXD_MODULATION_AQPSK(ts_set) (0b0110 | (ts_set & 0b0001)) -#define TRXD_MODULATION_16QAM(ts_set) (0b1000 | (ts_set & 0b0001)) -#define TRXD_MODULATION_32QAM(ts_set) (0b1010 | (ts_set & 0b0001)) - -struct trxd_hdr_v1_specific { -#if OSMO_IS_LITTLE_ENDIAN - uint8_t tsc:3, - modulation:4, - idle:1; -#elif OSMO_IS_BIG_ENDIAN -/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */ - uint8_t idle:1, modulation:4, tsc:3; -#endif - int16_t ci; /* big endian, in centiBels */ -} __attribute__ ((packed)); - -struct trxd_hdr_v1 { - struct trxd_hdr_common common; - struct trxd_hdr_v0_specific v0; - struct trxd_hdr_v1_specific v1; - uint8_t soft_bits[0]; -} __attribute__ ((packed));