From 115b0099dd3fd3720051d9f3c93f036c0ffdf375 Mon Sep 17 00:00:00 2001 From: Eric Wild Date: Wed, 20 Mar 2024 19:39:05 +0100 Subject: [PATCH] ms: make app configurable This adds proper config options. The ul/dl freq lines can be copied from the osmo-trx (network side) cfg. Change-Id: Ibd432f7abbd00065a59104d2c006b676d5db7b47 --- Transceiver52M/Makefile.am | 2 + Transceiver52M/ms/bladerf_specific.h | 27 ++- Transceiver52M/ms/ms.h | 5 +- Transceiver52M/ms/ms_rx_lower.cpp | 227 ++++++++++----------- Transceiver52M/ms/ms_upper.cpp | 122 +++++++----- Transceiver52M/ms/ms_upper.h | 3 +- Transceiver52M/ms/mssdr_vty.c | 239 +++++++++++++++++++++++ Transceiver52M/ms/mssdr_vty.h | 43 ++++ doc/examples/Makefile.am | 1 + doc/examples/osmo-trx-ms-blade/mssdr.cfg | 22 +++ 10 files changed, 516 insertions(+), 175 deletions(-) create mode 100644 Transceiver52M/ms/mssdr_vty.c create mode 100644 Transceiver52M/ms/mssdr_vty.h create mode 100644 doc/examples/osmo-trx-ms-blade/mssdr.cfg diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am index 0b63b16e..c54a6913 100644 --- a/Transceiver52M/Makefile.am +++ b/Transceiver52M/Makefile.am @@ -97,6 +97,7 @@ MS_UPPER_SRC = \ ms/ms_upper.cpp \ ms/l1ctl_server.c \ ms/logging.c \ + ms/mssdr_vty.c \ ms/l1ctl_server_cb.cpp \ ms/ms_trxcon_if.cpp @@ -110,6 +111,7 @@ noinst_HEADERS += \ ms/itrq.h \ ms/sch.h \ ms/threadpool.h \ + ms/mssdr_vty.h \ grgsm_vitac/viterbi_detector.h \ grgsm_vitac/constants.h \ grgsm_vitac/grgsm_vitac.h diff --git a/Transceiver52M/ms/bladerf_specific.h b/Transceiver52M/ms/bladerf_specific.h index 9db8bf0a..2ed3766f 100644 --- a/Transceiver52M/ms/bladerf_specific.h +++ b/Transceiver52M/ms/bladerf_specific.h @@ -32,6 +32,9 @@ #include #include #include +extern "C" { +#include "mssdr_vty.h" +} const size_t BLADE_BUFFER_SIZE = 1024 * 1; const size_t BLADE_NUM_BUFFERS = 32 * 1; @@ -196,7 +199,6 @@ struct blade_hw { const unsigned int rxFullScale, txFullScale; const int rxtxdelay; - float rxgain, txgain; static std::atomic stop_lower_threads_flag; double rxfreq_cache, txfreq_cache; @@ -205,9 +207,13 @@ struct blade_hw { int rx_freq; int sample_rate; int bandwidth; + float rxgain; + float txgain; public: - ms_trx_config() : tx_freq(881e6), rx_freq(926e6), sample_rate(((1625e3 / 6) * 4)), bandwidth(1e6) + ms_trx_config() + : tx_freq(881e6), rx_freq(926e6), sample_rate(((1625e3 / 6) * 4)), bandwidth(1e6), rxgain(30), + txgain(30) { } } cfg; @@ -223,10 +229,14 @@ struct blade_hw { { close_device(); } - blade_hw() - : rxFullScale(2047), txFullScale(2047), rxtxdelay(-60), rxgain(30), txgain(30), rxfreq_cache(0), + blade_hw(struct mssdr_cfg *cfgdata) + : rxFullScale(2047), txFullScale(2047), rxtxdelay(-60), rxfreq_cache(0), txfreq_cache(0) { + cfg.tx_freq = cfgdata->overrides.ul_freq; + cfg.rx_freq = cfgdata->overrides.dl_freq; + cfg.rxgain = cfgdata->overrides.dl_gain; + cfg.txgain = cfgdata->overrides.ul_gain; } void close_device() @@ -251,6 +261,7 @@ struct blade_hw { int init_device(bh_fn_t rxh, bh_fn_t txh) { struct bladerf_rational_rate rate = { 0, static_cast((1625e3 * 4)) * 64, 6 * 64 }, actual; + std::cerr << "cfg: ul " << cfg.tx_freq << " dl " << cfg.rx_freq << std::endl; bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_DEBUG); bladerf_set_usb_reset_on_open(true); @@ -295,8 +306,8 @@ struct blade_hw { (bladerf_bandwidth *)NULL); blade_check(bladerf_set_gain_mode, dev, BLADERF_CHANNEL_RX(0), BLADERF_GAIN_MGC); - setRxGain(rxgain, 0); - setTxGain(txgain, 0); + setRxGain(cfg.rxgain, 0); + setTxGain(cfg.txgain, 0); usleep(1000); bladerf_set_stream_timeout(dev, BLADERF_TX, 10); @@ -350,7 +361,7 @@ struct blade_hw { double setRxGain(double dB, size_t chan = 0) { - rxgain = dB; + cfg.rxgain = dB; msleep(15); blade_check(bladerf_set_gain, dev, BLADERF_CHANNEL_RX(0), (bladerf_gain)dB); msleep(15); @@ -358,7 +369,7 @@ struct blade_hw { }; double setTxGain(double dB, size_t chan = 0) { - txgain = dB; + cfg.txgain = dB; msleep(15); blade_check(bladerf_set_gain, dev, BLADERF_CHANNEL_TX(0), (bladerf_gain)dB); msleep(15); diff --git a/Transceiver52M/ms/ms.h b/Transceiver52M/ms/ms.h index 2ad78de5..c972f0a8 100644 --- a/Transceiver52M/ms/ms.h +++ b/Transceiver52M/ms/ms.h @@ -240,6 +240,7 @@ struct ms_trx : public BASET, public sched_hw_info { unsigned int mBSIC; int timing_advance; bool do_auto_gain; + bool use_va; pthread_t lower_rx_task; pthread_t lower_tx_task; @@ -276,8 +277,8 @@ struct ms_trx : public BASET, public sched_hw_info { void *tx_cb(); void maybe_update_gain(one_burst &brst); - ms_trx() - : mTSC(0), mBSIC(0), timing_advance(0), do_auto_gain(false), rxqueue(), + ms_trx(struct mssdr_cfg *cfgdata) + : BASET(cfgdata), mTSC(0), mBSIC(0), timing_advance(0), do_auto_gain(false), use_va(cfgdata->use_va), rxqueue(), first_sch_buf(new blade_sample_type[SCH_LEN_SPS]), burst_copy_buffer(new blade_sample_type[ONE_TS_BURST_LEN]), first_sch_buf_rcv_ts(0), rcv_done{ false }, sch_thread_done{ false }, upper_is_ready(false) diff --git a/Transceiver52M/ms/ms_rx_lower.cpp b/Transceiver52M/ms/ms_rx_lower.cpp index d894e968..f7788827 100644 --- a/Transceiver52M/ms/ms_rx_lower.cpp +++ b/Transceiver52M/ms/ms_rx_lower.cpp @@ -108,12 +108,12 @@ void ms_trx::maybe_update_gain(one_burst &brst) runmean = gain_check ? (runmean * (gain_check + 2) - 1 + sum) / (gain_check + 2) : sum; if (gain_check == avgburst_num - 1) { - DBGLG2() << "\x1B[32m #RXG \033[0m" << rxgain << " " << runmean << " " << sum << std::endl; + DBGLG2() << "\x1B[32m #RXG \033[0m" << cfg.rxgain << " " << runmean << " " << sum << std::endl; auto gainoffset = runmean < (rxFullScale / 4 ? 4 : 2); gainoffset = runmean < (rxFullScale / 2 ? 2 : 1); - float newgain = runmean < rx_max_cutoff ? rxgain + gainoffset : rxgain - gainoffset; + float newgain = runmean < rx_max_cutoff ? cfg.rxgain + gainoffset : cfg.rxgain - gainoffset; // FIXME: gian cutoff - if (newgain != rxgain && newgain <= 60) { + if (newgain != cfg.rxgain && newgain <= 60) { auto gain_fun = [this, newgain] { setRxGain(newgain); }; worker_thread.add_task(gain_fun); } @@ -158,24 +158,24 @@ bool ms_trx::handle_sch(bool is_first_sch_acq) const auto buf_len = is_first_sch_acq ? SCH_LEN_SPS : ONE_TS_BURST_LEN; const auto which_in_buffer = is_first_sch_acq ? first_sch_buf : burst_copy_buffer; memset((void *)&sch_acq_buffer[0], 0, sizeof(sch_acq_buffer)); -#if 1 - const auto which_out_buffer = is_first_sch_acq ? sch_acq_buffer : &sch_acq_buffer[40 * 2]; - const auto ss = reinterpret_cast *>(which_out_buffer); - std::complex channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR]; - int start; - convert_and_scale(which_out_buffer, which_in_buffer, buf_len * 2, 1.f / float(rxFullScale)); - if (is_first_sch_acq) { - float max_corr = 0; - start = get_sch_buffer_chan_imp_resp(ss, &channel_imp_resp[0], buf_len, &max_corr); - } else { - start = get_sch_chan_imp_resp(ss, &channel_imp_resp[0]); - start = start < 39 ? start : 39; - start = start > -39 ? start : -39; - } - detect_burst_nb(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits); + if (use_va) { + const auto which_out_buffer = is_first_sch_acq ? sch_acq_buffer : &sch_acq_buffer[40 * 2]; + const auto ss = reinterpret_cast *>(which_out_buffer); + std::complex channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR]; + int start; + convert_and_scale(which_out_buffer, which_in_buffer, buf_len * 2, 1.f / float(rxFullScale)); + if (is_first_sch_acq) { + float max_corr = 0; + start = get_sch_buffer_chan_imp_resp(ss, &channel_imp_resp[0], buf_len, &max_corr); + } else { + start = get_sch_chan_imp_resp(ss, &channel_imp_resp[0]); + start = start < 39 ? start : 39; + start = start > -39 ? start : -39; + } + detect_burst_nb(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits); - auto sch_decode_success = decode_sch(sch_demod_bits, is_first_sch_acq); -#if 0 + auto sch_decode_success = decode_sch(sch_demod_bits, is_first_sch_acq); +#if 0 // useful to debug offset shifts auto burst = new signalVector(buf_len, 50); const auto corr_type = is_first_sch_acq ? sch_detect_type::SCH_DETECT_BUFFER : sch_detect_type::SCH_DETECT_FULL; struct estim_burst_params ebp; @@ -189,113 +189,114 @@ bool ms_trx::handle_sch(bool is_first_sch_acq) std::cerr << "ooffs: " << howmuchdelay << " " << std::endl; std::cerr << "voffs: " << start << " " << sch_decode_success << std::endl; #endif - if (sch_decode_success) { - const auto ts_offset_symb = 4; - if (is_first_sch_acq) { - // update ts to first sample in sch buffer, to allow delay calc for current ts - first_sch_ts_start = first_sch_buf_rcv_ts + start - (ts_offset_symb * 4) - 1; - } else if (abs(start) > 1) { - // continuous sch tracking, only update if off too much - temp_ts_corr_offset += -start; - std::cerr << "offs: " << start << " " << temp_ts_corr_offset << std::endl; + if (sch_decode_success) { + const auto ts_offset_symb = 4; + if (is_first_sch_acq) { + // update ts to first sample in sch buffer, to allow delay calc for current ts + first_sch_ts_start = first_sch_buf_rcv_ts + start - (ts_offset_symb * 4) - 1; + } else if (abs(start) > 1) { + // continuous sch tracking, only update if off too much + temp_ts_corr_offset += -start; + std::cerr << "offs: " << start << " " << temp_ts_corr_offset << std::endl; + } + + return true; + } else { + DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << start << " " + << current_gsm_time.FN() << ":" << current_gsm_time.TN() << std::endl; } - - return true; } else { - DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << start << " " << current_gsm_time.FN() - << ":" << current_gsm_time.TN() << std::endl; - } -#else - const auto ts_offset_symb = 4; - auto burst = new signalVector(buf_len, 50); - const auto corr_type = is_first_sch_acq ? sch_detect_type::SCH_DETECT_BUFFER : sch_detect_type::SCH_DETECT_FULL; - struct estim_burst_params ebp; + const auto ts_offset_symb = 4; + auto burst = new signalVector(buf_len, 50); + const auto corr_type = + is_first_sch_acq ? sch_detect_type::SCH_DETECT_BUFFER : sch_detect_type::SCH_DETECT_FULL; + struct estim_burst_params ebp; - // scale like uhd, +-2k -> +-32k - convert_and_scale(burst->begin(), which_in_buffer, buf_len * 2, SAMPLE_SCALE_FACTOR); + // scale like uhd, +-2k -> +-32k + convert_and_scale(burst->begin(), which_in_buffer, buf_len * 2, SAMPLE_SCALE_FACTOR); - auto rv = detectSCHBurst(*burst, 4, 4, corr_type, &ebp); + auto rv = detectSCHBurst(*burst, 4, 4, corr_type, &ebp); - int howmuchdelay = ebp.toa * 4; + int howmuchdelay = ebp.toa * 4; - if (!rv) { - delete burst; - DBGLG() << "SCH : \x1B[31m detect fail \033[0m NOOOOOOOOOOOOOOOOOO toa:" << ebp.toa << " " - << current_gsm_time.FN() << ":" << current_gsm_time.TN() << std::endl; - return false; - } - - SoftVector *bits; - if (is_first_sch_acq) { - // can't be legit with a buf size spanning _at least_ one SCH but delay that implies partial sch burst - if (howmuchdelay < 0 || (buf_len - howmuchdelay) < ONE_TS_BURST_LEN) { + if (!rv) { delete burst; + DBGLG() << "SCH : \x1B[31m detect fail \033[0m NOOOOOOOOOOOOOOOOOO toa:" << ebp.toa << " " + << current_gsm_time.FN() << ":" << current_gsm_time.TN() << std::endl; return false; } - struct estim_burst_params ebp2; - // auto sch_chunk = new signalVector(ONE_TS_BURST_LEN, 50); - // auto sch_chunk_start = sch_chunk->begin(); - // memcpy(sch_chunk_start, sch_buf_f.data() + howmuchdelay, sizeof(std::complex) * ONE_TS_BURST_LEN); - - auto delay = delayVector(burst, NULL, -howmuchdelay); - - scaleVector(*delay, (complex)1.0 / ebp.amp); - - auto rv2 = detectSCHBurst(*delay, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp2); - DBGLG() << "FIRST SCH : " << (rv2 ? "yes " : " ") << "Timing offset " << ebp2.toa << " symbols" - << std::endl; - - bits = demodAnyBurst(*delay, SCH, 4, &ebp2); - delete delay; - } else { - bits = demodAnyBurst(*burst, SCH, 4, &ebp); - } - - delete burst; - - // clamp to +-1.5 because +-127 softbits scaled by 64 after -0.5 can be at most +-1.5 - clamp_array(bits->begin(), 148, 1.5f); - - float_to_sbit(&bits->begin()[0], (signed char *)&sch_demod_bits[0], 62, 148); - // float_to_sbit(&bits->begin()[106], &data[39], 62, 39); - - if (decode_sch((char *)sch_demod_bits, is_first_sch_acq)) { - auto current_gsm_time_updated = timekeeper.gsmtime(); + SoftVector *bits; if (is_first_sch_acq) { - // update ts to first sample in sch buffer, to allow delay calc for current ts - first_sch_ts_start = first_sch_buf_rcv_ts + howmuchdelay - (ts_offset_symb * 4); - } else { - // continuous sch tracking, only update if off too much - auto diff = [](float x, float y) { return x > y ? x - y : y - x; }; - - auto d = diff(ebp.toa, ts_offset_symb); - if (abs(d) > 0.3) { - if (ebp.toa < ts_offset_symb) - ebp.toa = d; - else - ebp.toa = -d; - temp_ts_corr_offset += ebp.toa * 4; - - DBGLG() << "offs: " << ebp.toa << " " << temp_ts_corr_offset << std::endl; + // can't be legit with a buf size spanning _at least_ one SCH but delay that implies partial sch burst + if (howmuchdelay < 0 || (buf_len - howmuchdelay) < ONE_TS_BURST_LEN) { + delete burst; + return false; } + + struct estim_burst_params ebp2; + // auto sch_chunk = new signalVector(ONE_TS_BURST_LEN, 50); + // auto sch_chunk_start = sch_chunk->begin(); + // memcpy(sch_chunk_start, sch_buf_f.data() + howmuchdelay, sizeof(std::complex) * ONE_TS_BURST_LEN); + + auto delay = delayVector(burst, NULL, -howmuchdelay); + + scaleVector(*delay, (complex)1.0 / ebp.amp); + + auto rv2 = detectSCHBurst(*delay, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp2); + DBGLG() << "FIRST SCH : " << (rv2 ? "yes " : " ") << "Timing offset " << ebp2.toa + << " symbols" << std::endl; + + bits = demodAnyBurst(*delay, SCH, 4, &ebp2); + delete delay; + } else { + bits = demodAnyBurst(*burst, SCH, 4, &ebp); } - auto a = gsm_sch_check_fn(current_gsm_time_updated.FN() - 1); - auto b = gsm_sch_check_fn(current_gsm_time_updated.FN()); - auto c = gsm_sch_check_fn(current_gsm_time_updated.FN() + 1); - DBGLG() << "L SCH : Timing offset " << rv << " " << ebp.toa << " " << a << b << c << "fn " - << current_gsm_time_updated.FN() << ":" << current_gsm_time_updated.TN() << std::endl; + delete burst; + + // clamp to +-1.5 because +-127 softbits scaled by 64 after -0.5 can be at most +-1.5 + clamp_array(bits->begin(), 148, 1.5f); + + float_to_sbit(&bits->begin()[0], (signed char *)&sch_demod_bits[0], 62, 148); + // float_to_sbit(&bits->begin()[106], &data[39], 62, 39); + + if (decode_sch((char *)sch_demod_bits, is_first_sch_acq)) { + auto current_gsm_time_updated = timekeeper.gsmtime(); + if (is_first_sch_acq) { + // update ts to first sample in sch buffer, to allow delay calc for current ts + first_sch_ts_start = first_sch_buf_rcv_ts + howmuchdelay - (ts_offset_symb * 4); + } else { + // continuous sch tracking, only update if off too much + auto diff = [](float x, float y) { return x > y ? x - y : y - x; }; + + auto d = diff(ebp.toa, ts_offset_symb); + if (abs(d) > 0.3) { + if (ebp.toa < ts_offset_symb) + ebp.toa = d; + else + ebp.toa = -d; + temp_ts_corr_offset += ebp.toa * 4; + + DBGLG() << "offs: " << ebp.toa << " " << temp_ts_corr_offset << std::endl; + } + } + + auto a = gsm_sch_check_fn(current_gsm_time_updated.FN() - 1); + auto b = gsm_sch_check_fn(current_gsm_time_updated.FN()); + auto c = gsm_sch_check_fn(current_gsm_time_updated.FN() + 1); + DBGLG() << "L SCH : Timing offset " << rv << " " << ebp.toa << " " << a << b << c << "fn " + << current_gsm_time_updated.FN() << ":" << current_gsm_time_updated.TN() << std::endl; + + delete bits; + return true; + } else { + DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << ebp.toa << " " + << current_gsm_time.FN() << ":" << current_gsm_time.TN() << std::endl; + } delete bits; - return true; - } else { - DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << ebp.toa << " " << current_gsm_time.FN() - << ":" << current_gsm_time.TN() << std::endl; } - - delete bits; -#endif return false; } @@ -333,12 +334,12 @@ SCH_STATE ms_trx::search_for_sch(dev_buf_t *rcd) float sum = normed_abs_sum(first_sch_buf, SCH_LEN_SPS); //FIXME: arbitrary value, gain cutoff - if (sum > target_val || rxgain >= 60) // enough ? + if (sum > target_val || cfg.rxgain >= 60) // enough ? sch_thread_done = this->handle_sch(true); else { - std::cerr << "\x1B[32m #RXG \033[0m gain " << rxgain << " -> " << rxgain + 4 + std::cerr << "\x1B[32m #RXG \033[0m gain " << cfg.rxgain << " -> " << cfg.rxgain + 4 << " sample avg:" << sum << " target: >=" << target_val << std::endl; - setRxGain(rxgain + 4); + setRxGain(cfg.rxgain + 4); } if (!sch_thread_done) diff --git a/Transceiver52M/ms/ms_upper.cpp b/Transceiver52M/ms/ms_upper.cpp index 2e8bc11f..e6bc0515 100644 --- a/Transceiver52M/ms/ms_upper.cpp +++ b/Transceiver52M/ms/ms_upper.cpp @@ -164,6 +164,7 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset) const auto zero_pad_len = 40; // give the VA some runway for misaligned bursts const auto workbuf_size = zero_pad_len + ONE_TS_BURST_LEN + zero_pad_len; static complex workbuf[workbuf_size]; + static int32_t meas_p, meas_rssi; static signalVector sv(workbuf, zero_pad_len, ONE_TS_BURST_LEN, static_alloc, static_free); one_burst e; @@ -199,8 +200,8 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset) return true; } -#if 1 - convert_and_scale(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale)); + if (use_va) { + convert_and_scale(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale)); pow = energyDetect(sv, 20 * 4 /*sps*/); if (pow < -1) { @@ -209,34 +210,34 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset) } avg = sqrt(pow); - { - float ncmax; - std::complex chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR]; - auto normal_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp[0], &ncmax, mTSC); + { + float ncmax; + std::complex chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR]; + auto normal_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp[0], &ncmax, mTSC); #ifdef DBGXX - float dcmax; - std::complex chan_imp_resp2[CHAN_IMP_RESP_LENGTH * d_OSR]; - auto dummy_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp2[0], &dcmax, TS_DUMMY); - auto is_nb = ncmax > dcmax; - // DBGLG() << " U " << (is_nb ? "NB" : "DB") << "@ o nb: " << normal_burst_start - // << " o db: " << dummy_burst_start << std::endl; + float dcmax; + std::complex chan_imp_resp2[CHAN_IMP_RESP_LENGTH * d_OSR]; + auto dummy_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp2[0], &dcmax, TS_DUMMY); + auto is_nb = ncmax > dcmax; + // DBGLG() << " U " << (is_nb ? "NB" : "DB") << "@ o nb: " << normal_burst_start + // << " o db: " << dummy_burst_start << std::endl; #endif - normal_burst_start = normal_burst_start < 39 ? normal_burst_start : 39; - normal_burst_start = normal_burst_start > -39 ? normal_burst_start : -39; + normal_burst_start = normal_burst_start < 39 ? normal_burst_start : 39; + normal_burst_start = normal_burst_start > -39 ? normal_burst_start : -39; #ifdef DBGXX - // fprintf(stderr, "%s %d\n", (is_nb ? "N":"D"), burst_time.FN()); - // if (is_nb) + // fprintf(stderr, "%s %d\n", (is_nb ? "N":"D"), burst_time.FN()); + // if (is_nb) #endif - detect_burst_nb(ss, &chan_imp_resp[0], normal_burst_start, demodded_softbits); + detect_burst_nb(ss, &chan_imp_resp[0], normal_burst_start, demodded_softbits); #ifdef DBGXX - // else - // detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin); + // else + // detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin); #endif - } -#else + } + } else { + // lower layer sch detection offset, easy to verify by just printing the detected value using both the va+sigproc code. + convert_and_scale(ss + 16, e.burst, ONE_TS_BURST_LEN * 2, 15); - // lower layer sch detection offset, easy to verify by just printing the detected value using both the va+sigproc code. - convert_and_scale(ss + 16, e.burst, ONE_TS_BURST_LEN * 2, 15); pow = energyDetect(sv, 20 * 4 /*sps*/); if (pow < -1) { @@ -245,30 +246,28 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset) } avg = sqrt(pow); + /* Detect normal or RACH bursts */ + CorrType type = CorrType::TSC; + struct estim_burst_params ebp; + auto rc = detectAnyBurst(sv, mTSC, 3, 4, type, 48, &ebp); + if (rc > 0) { + type = (CorrType)rc; + } - /* Detect normal or RACH bursts */ - CorrType type = CorrType::TSC; - struct estim_burst_params ebp; - auto rc = detectAnyBurst(sv, mTSC, 3, 4, type, 48, &ebp); - if (rc > 0) { - type = (CorrType)rc; + if (rc < 0) { + std::cerr << "UR : \x1B[31m rx fail \033[0m @ toa:" << ebp.toa << " " << e.gsmts.FN() << ":" + << e.gsmts.TN() << std::endl; + return false; + } + SoftVector *bits = demodAnyBurst(sv, type, 4, &ebp); + + SoftVector::const_iterator burstItr = bits->begin(); + // invert and fix to +-127 sbits + for (int ii = 0; ii < 148; ii++) { + demodded_softbits[ii] = *burstItr++ > 0.0f ? -127 : 127; + } + delete bits; } - - if (rc < 0) { - std::cerr << "UR : \x1B[31m rx fail \033[0m @ toa:" << ebp.toa << " " << e.gsmts.FN() << ":" - << e.gsmts.TN() << std::endl; - return false; - } - SoftVector *bits = demodAnyBurst(sv, type, 4, &ebp); - - SoftVector::const_iterator burstItr = bits->begin(); - // invert and fix to +-127 sbits - for (int ii = 0; ii < 148; ii++) { - demodded_softbits[ii] = *burstItr++ > 0.0f ? -127 : 127; - } - delete bits; - -#endif RSSI = (int)floor(20.0 * log10(rxFullScale / avg)); // FIXME: properly handle offset, sch/nb alignment diff? handled by lower anyway... timingOffset = (int)round(0); @@ -419,13 +418,13 @@ bool upper_trx::driveControl() r.param.measure.band_arfcn = cmd.param.measure.band_arfcn; // FIXME: do we want to measure anything, considering the transceiver just syncs by.. syncing? r.param.measure.dbm = -80; - tuneRx(gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 0) * 1000 * 100); - tuneTx(gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 1) * 1000 * 100); + // tuneRx(gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 0) * 1000 * 100); + // tuneTx(gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 1) * 1000 * 100); cmdq_from_phy.spsc_push(&r); break; case TRXCON_PHYIF_CMDT_SETFREQ_H0: - tuneRx(gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 0) * 1000 * 100); - tuneTx(gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 1) * 1000 * 100); + // tuneRx(gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 0) * 1000 * 100); + // tuneTx(gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 1) * 1000 * 100); break; case TRXCON_PHYIF_CMDT_SETFREQ_H1: break; @@ -456,6 +455,12 @@ void sighandler(int sigset) } } +extern "C" { +#include +#include +#include "mssdr_vty.h" +} + int main(int argc, char *argv[]) { auto tall_trxcon_ctx = talloc_init("trxcon context"); @@ -476,6 +481,22 @@ int main(int argc, char *argv[]) osmo_fsm_log_timeouts(true); + auto g_mssdr_ctx = vty_mssdr_ctx_alloc(tall_trxcon_ctx); + vty_init(&g_mssdr_vty_info); + logging_vty_add_cmds(); + mssdr_vty_init(g_mssdr_ctx); + + const char *home_dir = getenv("HOME"); + if (!home_dir) + home_dir = "~"; + auto config_file = talloc_asprintf(tall_trxcon_ctx, "%s/%s", home_dir, ".osmocom/bb/mssdr.cfg"); + + int rc = vty_read_config_file(config_file, NULL); + if (rc < 0) { + fprintf(stderr, "Failed to parse config file: '%s'\n", config_file); + exit(2); + } + g_trxcon = trxcon_inst_alloc(tall_trxcon_ctx, 0); g_trxcon->gsmtap = nullptr; g_trxcon->phyif = nullptr; @@ -487,8 +508,7 @@ int main(int argc, char *argv[]) initvita(); int status = 0; - auto trx = new upper_trx(); - trx->do_auto_gain = true; + auto trx = new upper_trx(&g_mssdr_ctx->cfg); status = trx->init_dev_and_streams(); if (status < 0) { diff --git a/Transceiver52M/ms/ms_upper.h b/Transceiver52M/ms/ms_upper.h index 06f7c543..d19a7d82 100644 --- a/Transceiver52M/ms/ms_upper.h +++ b/Transceiver52M/ms/ms_upper.h @@ -44,5 +44,6 @@ class upper_trx : public ms_trx { void driveReceiveFIFO(); void driveTx(); - upper_trx() : mOn(false){}; + upper_trx() = delete; + explicit upper_trx(struct mssdr_cfg *cfgdata) : ms_trx(cfgdata), mOn(false){}; }; diff --git a/Transceiver52M/ms/mssdr_vty.c b/Transceiver52M/ms/mssdr_vty.c new file mode 100644 index 00000000..eba34649 --- /dev/null +++ b/Transceiver52M/ms/mssdr_vty.c @@ -0,0 +1,239 @@ +/* + * (C) 2024 by sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Eric Wild + * + * 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 . + * + */ + +#include +#include +#include "../config.h" +#include "mssdr_vty.h" + +static struct mssdr_ctx *g_mssdr_ctx; + +enum mssdr_vty_node { + MSSDR_NODE = _LAST_OSMOVTY_NODE + 1, +}; + +static const char mssdr_copyright[] = + "Copyright (C) 2007-2014 Free Software Foundation, Inc.\r\n" + "Copyright (C) 2013 Thomas Tsou \r\n" + "Copyright (C) 2013-2019 Fairwaves, Inc.\r\n" + "Copyright (C) 2015 Ettus Research LLC\r\n" + "Copyright (C) 2017-2024 by sysmocom s.f.m.c. GmbH \r\n" + "License AGPLv3+: GNU AGPL version 3 or later \r\n" + "This is free software: you are free to change and redistribute it.\r\n" + "There is NO WARRANTY, to the extent permitted by law.\r\n"; + +static int mssdr_vty_go_parent(struct vty *vty) +{ + switch (vty->node) { + case MSSDR_NODE: + vty->node = CONFIG_NODE; + vty->index = NULL; + vty->index_sub = NULL; + break; + default: + vty->node = CONFIG_NODE; + vty->index = NULL; + vty->index_sub = NULL; + } + + return vty->node; +} + +struct mssdr_ctx *mssdr_from_vty(struct vty *v) +{ + OSMO_ASSERT(g_mssdr_ctx); + return g_mssdr_ctx; +} + +struct vty_app_info g_mssdr_vty_info = { + .name = "OsmoMSSDR", + .version = PACKAGE_VERSION, + .copyright = mssdr_copyright, + .go_parent_cb = mssdr_vty_go_parent, +}; + +struct mssdr_ctx *vty_mssdr_ctx_alloc(void *talloc_ctx) +{ + struct mssdr_ctx *trx = talloc_zero(talloc_ctx, struct mssdr_ctx); + trx->cfg.use_va = true; + return trx; +} + +static void mssdr_dump_vty(struct vty *vty, struct mssdr_ctx *trx) +{ + // vty_out(vty, "TRX Config:%s", VTY_NEWLINE); + // vty_out(vty, " Local IP: %s%s", trx->cfg.bind_addr, VTY_NEWLINE); + // vty_out(vty, " Remote IP: %s%s", trx->cfg.remote_addr, VTY_NEWLINE); + // vty_out(vty, " TRX Base Port: %u%s", trx->cfg.base_port, VTY_NEWLINE); + // vty_out(vty, " Device args: %s%s", trx->cfg.dev_args, VTY_NEWLINE); + // vty_out(vty, " Tx Samples-per-Symbol: %u%s", trx->cfg.tx_sps, VTY_NEWLINE); + // vty_out(vty, " Rx Samples-per-Symbol: %u%s", trx->cfg.rx_sps, VTY_NEWLINE); + // vty_out(vty, " Filler Burst Type: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE); + vty_out(vty, "trx%s", VTY_NEWLINE); + if (trx->cfg.overrides.dl_freq_override) + vty_out(vty, " dl-freq-override %f%s", trx->cfg.overrides.dl_freq, VTY_NEWLINE); + if (trx->cfg.overrides.ul_freq_override) + vty_out(vty, " ul-freq-override %f%s", trx->cfg.overrides.ul_freq, VTY_NEWLINE); + if (trx->cfg.overrides.dl_gain_override) + 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); +} + +static int config_write_mssdr(struct vty *vty) +{ + struct mssdr_ctx *trx = mssdr_from_vty(vty); + + vty_out(vty, "trx%s", VTY_NEWLINE); + if (trx->cfg.overrides.dl_freq_override) + vty_out(vty, " dl-freq-override %f%s", trx->cfg.overrides.dl_freq, VTY_NEWLINE); + if (trx->cfg.overrides.ul_freq_override) + vty_out(vty, " ul-freq-override %f%s", trx->cfg.overrides.ul_freq, VTY_NEWLINE); + if (trx->cfg.overrides.dl_gain_override) + 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); + return CMD_SUCCESS; +} + +DEFUN(show_mssdr, show_mssdr_cmd, + "show mssdr", + SHOW_STR "Display information on the TRX\n") +{ + struct mssdr_ctx *trx = mssdr_from_vty(vty); + + mssdr_dump_vty(vty, trx); + + return CMD_SUCCESS; +} + +DEFUN(cfg_mssdr, cfg_mssdr_cmd, + "mssdr", + "Configure the mssdr\n") +{ + struct mssdr_ctx *trx = mssdr_from_vty(vty); + + if (!trx) + return CMD_WARNING; + + vty->node = MSSDR_NODE; + + return CMD_SUCCESS; +} + +DEFUN_ATTR(cfg_ul_freq_override, cfg_ul_freq_override_cmd, + "ul-freq-override FLOAT", + "Overrides Tx carrier frequency\n" + "Frequency in Hz (e.g. 145300000)\n", + CMD_ATTR_HIDDEN) +{ + struct mssdr_ctx *trx = mssdr_from_vty(vty); + + trx->cfg.overrides.ul_freq_override = true; + trx->cfg.overrides.ul_freq = atof(argv[0]); + + return CMD_SUCCESS; +} +DEFUN_ATTR(cfg_dl_freq_override, cfg_dl_freq_override_cmd, + "dl-freq-override FLOAT", + "Overrides Rx carrier frequency\n" + "Frequency in Hz (e.g. 145300000)\n", + CMD_ATTR_HIDDEN) +{ + struct mssdr_ctx *trx = mssdr_from_vty(vty); + + trx->cfg.overrides.dl_freq_override = true; + trx->cfg.overrides.dl_freq = atof(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN_ATTR(cfg_ul_gain_override, cfg_ul_gain_override_cmd, + "ul-gain-override FLOAT", + "Overrides Tx gain\n" + "gain in dB\n", + CMD_ATTR_HIDDEN) +{ + struct mssdr_ctx *trx = mssdr_from_vty(vty); + + trx->cfg.overrides.ul_gain_override = true; + trx->cfg.overrides.ul_gain = atof(argv[0]); + + return CMD_SUCCESS; +} +DEFUN_ATTR(cfg_dl_gain_override, cfg_dl_gain_override_cmd, + "dl-gain-override FLOAT", + "Overrides Rx gain\n" + "gain in dB\n", + CMD_ATTR_HIDDEN) +{ + struct mssdr_ctx *trx = mssdr_from_vty(vty); + + trx->cfg.overrides.dl_gain_override = true; + trx->cfg.overrides.dl_gain = atof(argv[0]); + + return CMD_SUCCESS; +} + +DEFUN_ATTR(cfg_use_viterbi, cfg_use_viterbi_cmd, + "viterbi-eq (disable|enable)", + "Use viterbi equalizer for gmsk (default=enable)\n" + "Disable VA\n" + "Enable VA\n", + CMD_ATTR_HIDDEN) +{ + struct mssdr_ctx *trx = mssdr_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; +} + +static struct cmd_node mssdr_node = { + MSSDR_NODE, + "%s(config-mssdr)# ", + 1, +}; + +int mssdr_vty_init(struct mssdr_ctx *trx) +{ + g_mssdr_ctx = trx; + install_element_ve(&show_mssdr_cmd); + install_element(CONFIG_NODE, &cfg_mssdr_cmd); + + install_node(&mssdr_node, config_write_mssdr); + install_element(MSSDR_NODE, &cfg_ul_freq_override_cmd); + install_element(MSSDR_NODE, &cfg_dl_freq_override_cmd); + install_element(MSSDR_NODE, &cfg_ul_gain_override_cmd); + install_element(MSSDR_NODE, &cfg_dl_gain_override_cmd); + install_element(MSSDR_NODE, &cfg_use_viterbi_cmd); + + return 0; +} diff --git a/Transceiver52M/ms/mssdr_vty.h b/Transceiver52M/ms/mssdr_vty.h new file mode 100644 index 00000000..4d8f42dd --- /dev/null +++ b/Transceiver52M/ms/mssdr_vty.h @@ -0,0 +1,43 @@ +#pragma once +/* + * (C) 2024 by sysmocom s.f.m.c. GmbH + * All Rights Reserved + * + * Author: Eric Wild + * + * 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 . + * + */ + +struct mssdr_cfg { + struct { + bool ul_freq_override; + bool dl_freq_override; + bool ul_gain_override; + bool dl_gain_override; + double ul_freq; + double dl_freq; + double ul_gain; + double dl_gain; + } overrides; + bool use_va; +}; + +struct mssdr_ctx { + struct mssdr_cfg cfg; +}; + +struct mssdr_ctx *vty_mssdr_ctx_alloc(void *talloc_ctx); +int mssdr_vty_init(struct mssdr_ctx *trx); +extern struct vty_app_info g_mssdr_vty_info; diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am index ac87457d..fcdc7d19 100644 --- a/doc/examples/Makefile.am +++ b/doc/examples/Makefile.am @@ -8,6 +8,7 @@ EXTRA_DIST = \ osmo-trx-lms/osmo-trx-limesdr.cfg \ osmo-trx-lms/osmo-trx-lms.cfg \ osmo-trx-ipc/osmo-trx-ipc.cfg \ + osmo-trx-ms-blade/mssdr.cfg \ $(NULL) OSMOCONF_FILES = diff --git a/doc/examples/osmo-trx-ms-blade/mssdr.cfg b/doc/examples/osmo-trx-ms-blade/mssdr.cfg new file mode 100644 index 00000000..38eee306 --- /dev/null +++ b/doc/examples/osmo-trx-ms-blade/mssdr.cfg @@ -0,0 +1,22 @@ +log stderr + logging filter all 1 + logging color 1 + logging print category-hex 0 + logging print category 1 + logging timestamp 0 + logging print file basename last + logging print level 1 + logging level set-all notice +! +line vty + no login +! +# cpu-sched +# policy rr 18 +mssdr + ul-freq-override 881e6 + dl-freq-override 926e6 + ul-gain-override 30 + dl-gain-override 30 + viterbi-eq enable +