Hoernchen has uploaded this change for review. ( 
https://gerrit.osmocom.org/c/osmo-trx/+/36373?usp=email )


Change subject: ms: add config file
......................................................................

ms: add config file

This adds proper config options.
The ul/dl freq lines can be copied from the osmo-trx (network side) cfg.

Change-Id: Ibd432f7abbd00065a59104d2c006b676d5db7b47
---
M Transceiver52M/Makefile.am
M Transceiver52M/ms/bladerf_specific.h
M Transceiver52M/ms/ms.h
M Transceiver52M/ms/ms_rx_lower.cpp
M Transceiver52M/ms/ms_upper.cpp
M Transceiver52M/ms/ms_upper.h
A Transceiver52M/ms/mssdr_vty.c
A Transceiver52M/ms/mssdr_vty.h
M doc/examples/Makefile.am
A doc/examples/osmo-trx-ms-blade/mssdr.cfg
10 files changed, 580 insertions(+), 196 deletions(-)



  git pull ssh://gerrit.osmocom.org:29418/osmo-trx refs/changes/73/36373/1

diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am
index dadfde9..b007a3d 100644
--- a/Transceiver52M/Makefile.am
+++ b/Transceiver52M/Makefile.am
@@ -97,6 +97,7 @@
        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 @@
        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 9db8bf0..d31ea29 100644
--- a/Transceiver52M/ms/bladerf_specific.h
+++ b/Transceiver52M/ms/bladerf_specific.h
@@ -32,6 +32,9 @@
 #include <libbladeRF.h>
 #include <Timeval.h>
 #include <unistd.h>
+extern "C" {
+#include "mssdr_vty.h"
+}

 const size_t BLADE_BUFFER_SIZE = 1024 * 1;
 const size_t BLADE_NUM_BUFFERS = 32 * 1;
@@ -195,8 +198,8 @@
        using tx_buf_q_type = spsc_cond_timeout<BLADE_NUM_BUFFERS, dev_buf_t *, 
true, false>;
        const unsigned int rxFullScale, txFullScale;
        const int rxtxdelay;
+       bool use_agc;

-       float rxgain, txgain;
        static std::atomic<bool> stop_lower_threads_flag;
        double rxfreq_cache, txfreq_cache;

@@ -205,9 +208,13 @@
                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 +230,14 @@
        {
                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), 
use_agc(cfgdata->use_agc), 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 +262,7 @@
        int init_device(bh_fn_t rxh, bh_fn_t txh)
        {
                struct bladerf_rational_rate rate = { 0, 
static_cast<uint64_t>((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);
@@ -294,9 +306,10 @@
                blade_check(bladerf_set_bandwidth, dev, BLADERF_CHANNEL_TX(0), 
(bladerf_bandwidth)cfg.bandwidth,
                            (bladerf_bandwidth *)NULL);

-               blade_check(bladerf_set_gain_mode, dev, BLADERF_CHANNEL_RX(0), 
BLADERF_GAIN_MGC);
-               setRxGain(rxgain, 0);
-               setTxGain(txgain, 0);
+               blade_check(bladerf_set_gain_mode, dev, BLADERF_CHANNEL_RX(0),
+                           use_agc ? BLADERF_GAIN_AUTOMATIC : 
BLADERF_GAIN_MGC);
+               setRxGain(cfg.rxgain, 0);
+               setTxGain(cfg.txgain, 0);
                usleep(1000);
 
                bladerf_set_stream_timeout(dev, BLADERF_TX, 10);
@@ -350,7 +363,7 @@

        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 +371,7 @@
        };
        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 2ad78de..e088f73 100644
--- a/Transceiver52M/ms/ms.h
+++ b/Transceiver52M/ms/ms.h
@@ -239,7 +239,7 @@
        unsigned int mTSC;
        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 +276,8 @@
        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), 
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 d894e96..b169dd8 100644
--- a/Transceiver52M/ms/ms_rx_lower.cpp
+++ b/Transceiver52M/ms/ms_rx_lower.cpp
@@ -108,12 +108,12 @@
        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);
                }
@@ -144,7 +144,7 @@
        while (upper_is_ready && !rxqueue.spsc_push(&brst))
                ;

-       if (do_auto_gain)
+       if (!use_agc)
                maybe_update_gain(brst);

        return false;
@@ -158,24 +158,24 @@
        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<std::complex<float> 
*>(which_out_buffer);
-       std::complex<float> 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<std::complex<float> 
*>(which_out_buffer);
+               std::complex<float> 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 @@
        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<float>) * 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<float>) * 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 @@
                        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 2e8bc11..af5bf82 100644
--- a/Transceiver52M/ms/ms_upper.cpp
+++ b/Transceiver52M/ms/ms_upper.cpp
@@ -164,6 +164,7 @@
        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;
@@ -183,8 +184,11 @@
        trxcon_phyif_rtr_ind i = { static_cast<uint32_t>(wTime.FN()), 
static_cast<uint8_t>(wTime.TN()) };
        trxcon_phyif_rtr_rsp r = {};
        trxcon_phyif_handle_rtr_ind(g_trxcon, &i, &r);
-       if (!(r.flags & TRXCON_PHYIF_RTR_F_ACTIVE))
+       if (!(r.flags & TRXCON_PHYIF_RTR_F_ACTIVE)) {
+               bladerf_get_rfic_rssi(dev, 0, &meas_p, &meas_rssi);
+               // std::cerr << "G : \x1B[31m rx fail \033[0m @:" << meas_rssi 
<< std::endl;
                return false;
+       }

        if (is_fcch) {
                // return trash
@@ -199,77 +203,75 @@
                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) {
-               LOG(ALERT) << "Received empty burst";
-               return false;
-       }
+               pow = energyDetect(sv, 20 * 4 /*sps*/);
+               if (pow < -1) {
+                       LOG(ALERT) << "Received empty burst";
+                       return false;
+               }

-       avg = sqrt(pow);
-       {
-               float ncmax;
-               std::complex<float> 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);
+               avg = sqrt(pow);
+               {
+                       float ncmax;
+                       std::complex<float> 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<float> 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<float> 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 {
+               // 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) {
+                       LOG(ALERT) << "Received empty burst";
+                       return false;
+               }
+
+               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;
+               }
+
+               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;
        }
-#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);
-
-       pow = energyDetect(sv, 20 * 4 /*sps*/);
-       if (pow < -1) {
-               LOG(ALERT) << "Received empty burst";
-               return false;
-       }
-
-       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;
-       }
-
-       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));
+       RSSI = meas_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 +421,13 @@
                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 +458,12 @@
        }
 }

+extern "C" {
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/logging.h>
+#include "mssdr_vty.h"
+}
+
 int main(int argc, char *argv[])
 {
        auto tall_trxcon_ctx = talloc_init("trxcon context");
@@ -476,6 +484,22 @@

        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 +511,7 @@
        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 06f7c54..d19a7d8 100644
--- a/Transceiver52M/ms/ms_upper.h
+++ b/Transceiver52M/ms/ms_upper.h
@@ -44,5 +44,6 @@
        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 0000000..3e310f3
--- /dev/null
+++ b/Transceiver52M/ms/mssdr_vty.c
@@ -0,0 +1,264 @@
+/*
+ * (C) 2024 by sysmocom s.f.m.c. GmbH <[email protected]>
+ * All Rights Reserved
+ *
+ * Author: Eric Wild <[email protected]>
+ *
+ * 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/>.
+ *
+ */
+
+#include <osmocom/vty/command.h>
+#include <osmocom/vty/logging.h>
+#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 <[email protected]>\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 
<[email protected]>\r\n"
+       "License AGPLv3+: GNU AGPL version 3 or later 
<http://gnu.org/licenses/agpl-3.0.html>\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;
+       trx->cfg.use_agc = 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);
+       if (trx->cfg.use_agc)
+               vty_out(vty, " rx-agc %s%s", trx->cfg.use_agc ? "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);
+       if (trx->cfg.use_agc)
+               vty_out(vty, " rx-agc %s%s", trx->cfg.use_agc ? "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;
+}
+
+DEFUN_ATTR(cfg_use_agc, cfg_use_agc_cmd,
+       "rx-agc (disable|enable)",
+       "Use the transceiver rx agc (default=enable)\n"
+       "Disable agc\n"
+       "Enable agc\n",
+       CMD_ATTR_HIDDEN)
+{
+       struct mssdr_ctx *trx = mssdr_from_vty(vty);
+
+       if (strcmp("disable", argv[0]) == 0)
+               trx->cfg.use_agc = false;
+       else if (strcmp("enable", argv[0]) == 0)
+               trx->cfg.use_agc = 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);
+       install_element(MSSDR_NODE, &cfg_use_agc_cmd);
+
+       return 0;
+}
\ No newline at end of file
diff --git a/Transceiver52M/ms/mssdr_vty.h b/Transceiver52M/ms/mssdr_vty.h
new file mode 100644
index 0000000..9eafa43
--- /dev/null
+++ b/Transceiver52M/ms/mssdr_vty.h
@@ -0,0 +1,44 @@
+#pragma once
+/*
+ * (C) 2024 by sysmocom s.f.m.c. GmbH <[email protected]>
+ * All Rights Reserved
+ *
+ * Author: Eric Wild <[email protected]>
+ *
+ * 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/>.
+ *
+ */
+
+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;
+       bool use_agc;
+};
+
+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;
\ No newline at end of file
diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am
index ac87457..fcdc7d1 100644
--- a/doc/examples/Makefile.am
+++ b/doc/examples/Makefile.am
@@ -8,6 +8,7 @@
        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 0000000..01d716c
--- /dev/null
+++ b/doc/examples/osmo-trx-ms-blade/mssdr.cfg
@@ -0,0 +1,23 @@
+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
+ rx-agc enable
+

--
To view, visit https://gerrit.osmocom.org/c/osmo-trx/+/36373?usp=email
To unsubscribe, or for help writing mail filters, visit 
https://gerrit.osmocom.org/settings

Gerrit-Project: osmo-trx
Gerrit-Branch: master
Gerrit-Change-Id: Ibd432f7abbd00065a59104d2c006b676d5db7b47
Gerrit-Change-Number: 36373
Gerrit-PatchSet: 1
Gerrit-Owner: Hoernchen <[email protected]>
Gerrit-MessageType: newchange

Reply via email to