mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-03 13:43:18 +00:00
Compare commits
26 Commits
1.6.0
...
Hoernchen/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea6cec5014 | ||
|
|
98a50b7597 | ||
|
|
238891f161 | ||
|
|
3d8598d460 | ||
|
|
1d7f5a9175 | ||
|
|
511d62733e | ||
|
|
8f19df3bd7 | ||
|
|
b7cde0f6a2 | ||
|
|
25590be470 | ||
|
|
f1ce0e7692 | ||
|
|
99e0746f37 | ||
|
|
1f8eb7c658 | ||
|
|
d8a1dee2c9 | ||
|
|
56c7b777f3 | ||
|
|
989fe75038 | ||
|
|
8aea236c56 | ||
|
|
a2d76f1d2f | ||
|
|
a118d98ec3 | ||
|
|
c7fc94dff6 | ||
|
|
06c0810e98 | ||
|
|
6ee9dccddb | ||
|
|
242ceb25d1 | ||
|
|
b52650f157 | ||
|
|
992a49e586 | ||
|
|
ad9b8b4211 | ||
|
|
0f4381d480 |
@@ -1,4 +1,5 @@
|
||||
--exclude osmocom-bb/.*
|
||||
--exclude .*h
|
||||
--exclude Transceiver52M/grgsm_vitac/.*
|
||||
--exclude utils/va-test/.*
|
||||
--ignore FUNCTION_WITHOUT_ARGS
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -38,6 +38,7 @@ Transceiver52M/device/ipc/ipc-driver-test
|
||||
.deps
|
||||
.libs
|
||||
.dirstamp
|
||||
.version
|
||||
*~
|
||||
Makefile
|
||||
config.log
|
||||
@@ -83,6 +84,7 @@ contrib/osmo-trx.spec
|
||||
!contrib/osmo-trx.spec.in
|
||||
|
||||
utils/osmo-prbs-tool
|
||||
utils/va-test/osmo-burst-gen
|
||||
/.qtc_clangd/*
|
||||
/.cache/*
|
||||
/.vscode/*
|
||||
|
||||
@@ -55,12 +55,15 @@ const BitVector GSM::gEdgeTrainingSequence[] = {
|
||||
};
|
||||
|
||||
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
|
||||
const BitVector GSM::gDummyBurstTSC("01110001011100010111000101");
|
||||
|
||||
/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */
|
||||
const BitVector GSM::gRACHSynchSequenceTS0("01001011011111111001100110101010001111000"); /* GSM, GMSK (default) */
|
||||
const BitVector GSM::gRACHSynchSequenceTS1("01010100111110001000011000101111001001101"); /* EGPRS, 8-PSK */
|
||||
const BitVector GSM::gRACHSynchSequenceTS2("11101111001001110101011000001101101110111"); /* EGPRS, GMSK */
|
||||
|
||||
const BitVector GSM::gSCHSynchSequence("1011100101100010000001000000111100101101010001010111011000011011");
|
||||
|
||||
// |-head-||---------midamble----------------------||--------------data----------------||t|
|
||||
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
|
||||
|
||||
|
||||
@@ -52,11 +52,16 @@ extern const BitVector gEdgeTrainingSequence[];
|
||||
|
||||
/** C0T0 filler burst, GSM 05.02, 5.2.6 */
|
||||
extern const BitVector gDummyBurst;
|
||||
extern const BitVector gDummyBurstTSC;
|
||||
|
||||
/** Random access burst synch. sequence */
|
||||
extern const BitVector gRACHSynchSequenceTS0;
|
||||
extern const BitVector gRACHSynchSequenceTS1;
|
||||
extern const BitVector gRACHSynchSequenceTS2;
|
||||
|
||||
/** Synchronization burst sync sequence */
|
||||
extern const BitVector gSCHSynchSequence;
|
||||
|
||||
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
|
||||
extern const BitVector gRACHBurst;
|
||||
|
||||
|
||||
@@ -43,12 +43,20 @@ SUBDIRS += \
|
||||
doc \
|
||||
$(NULL)
|
||||
|
||||
BUILT_SOURCES = $(top_srcdir)/.version
|
||||
$(top_srcdir)/.version:
|
||||
echo $(VERSION) > $@-t && mv $@-t $@
|
||||
dist-hook:
|
||||
echo $(VERSION) > $(distdir)/.tarball-version
|
||||
|
||||
EXTRA_DIST = \
|
||||
.version \
|
||||
LEGAL \
|
||||
COPYING \
|
||||
README.md \
|
||||
contrib/osmo-trx.spec.in \
|
||||
debian \
|
||||
git-version-gen \
|
||||
$(NULL)
|
||||
|
||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
|
||||
|
||||
@@ -88,6 +88,7 @@ TRXCON_LDADD = \
|
||||
MS_LOWER_SRC = \
|
||||
ms/sch.c \
|
||||
ms/ms.cpp \
|
||||
ms/threadsched.cpp \
|
||||
ms/ms_rx_lower.cpp \
|
||||
grgsm_vitac/grgsm_vitac.cpp \
|
||||
grgsm_vitac/viterbi_detector.cc
|
||||
@@ -96,11 +97,13 @@ 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
|
||||
|
||||
noinst_HEADERS += \
|
||||
ms/ms.h \
|
||||
ms/threadsched.h \
|
||||
ms/bladerf_specific.h \
|
||||
ms/uhd_specific.h \
|
||||
ms/ms_upper.h \
|
||||
@@ -108,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
|
||||
@@ -125,16 +129,16 @@ osmo_trx_uhd_LDADD = \
|
||||
$(UHD_LIBS)
|
||||
osmo_trx_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
|
||||
|
||||
if ENABLE_MS_TRX
|
||||
bin_PROGRAMS += osmo-trx-ms-uhd
|
||||
osmo_trx_ms_uhd_SOURCES = $(MS_LOWER_SRC) $(MS_UPPER_SRC)
|
||||
osmo_trx_ms_uhd_LDADD = \
|
||||
$(builddir)/device/uhd/libdevice.la \
|
||||
$(COMMON_LDADD) \
|
||||
$(UHD_LIBS) \
|
||||
$(TRXCON_LDADD)
|
||||
osmo_trx_ms_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DBUILDUHD
|
||||
endif
|
||||
#if ENABLE_MS_TRX
|
||||
#bin_PROGRAMS += osmo-trx-ms-uhd
|
||||
#osmo_trx_ms_uhd_SOURCES = $(MS_LOWER_SRC) $(MS_UPPER_SRC)
|
||||
#osmo_trx_ms_uhd_LDADD = \
|
||||
# $(builddir)/device/uhd/libdevice.la \
|
||||
# $(COMMON_LDADD) \
|
||||
# $(UHD_LIBS) \
|
||||
# $(TRXCON_LDADD)
|
||||
#osmo_trx_ms_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DBUILDUHD
|
||||
#endif
|
||||
endif
|
||||
|
||||
if DEVICE_USRP1
|
||||
|
||||
@@ -32,7 +32,7 @@ extern "C" {
|
||||
#define M_PI 3.14159265358979323846264338327f
|
||||
#endif
|
||||
|
||||
#define MAX_OUTPUT_LEN 4096
|
||||
#define MAX_OUTPUT_LEN 4096*4
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
@@ -959,19 +959,18 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
|
||||
sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
|
||||
}
|
||||
} else if (match_cmd(command, "SETMAXDLY", ¶ms)) {
|
||||
//set expected maximum time-of-arrival
|
||||
//set expected maximum time-of-arrival for Access Bursts
|
||||
int maxDelay;
|
||||
sscanf(params, "%d", &maxDelay);
|
||||
mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
|
||||
sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
|
||||
} else if (match_cmd(command, "SETMAXDLYNB", ¶ms)) {
|
||||
//set expected maximum time-of-arrival
|
||||
//set expected maximum time-of-arrival for Normal Bursts
|
||||
int maxDelay;
|
||||
sscanf(params, "%d", &maxDelay);
|
||||
mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
|
||||
sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
|
||||
} else if (match_cmd(command, "SETRXGAIN", ¶ms)) {
|
||||
//set expected maximum time-of-arrival
|
||||
int newGain;
|
||||
sscanf(params, "%d", &newGain);
|
||||
newGain = mRadioInterface->setRxGain(newGain, chan);
|
||||
|
||||
@@ -279,6 +279,7 @@ int blade_device::open()
|
||||
|
||||
dev_type = blade_dev_type::BLADE2;
|
||||
tx_window = TX_WINDOW_FIXED;
|
||||
update_band_dev(dev_key(dev_type, tx_sps, rx_sps));
|
||||
|
||||
struct bladerf_devinfo info;
|
||||
bladerf_get_devinfo(dev, &info);
|
||||
|
||||
@@ -34,7 +34,7 @@ class band_manager {
|
||||
using powerkeyt = typename powermapt::key_type;
|
||||
using powermappedt = typename powermapt::mapped_type;
|
||||
using devkeyt = typename devmapt::key_type;
|
||||
const devkeyt &m_dev_type;
|
||||
devkeyt m_dev_type;
|
||||
const powermapt &m_power_map;
|
||||
const devmapt &m_dev_map;
|
||||
powerkeyt m_fallback;
|
||||
@@ -102,6 +102,10 @@ class band_manager {
|
||||
band_ass_curr_sess = false;
|
||||
}
|
||||
|
||||
void update_band_dev(devkeyt dev_type) {
|
||||
m_dev_type = dev_type;
|
||||
}
|
||||
|
||||
void get_dev_band_desc(powermappedt &desc)
|
||||
{
|
||||
if (m_band == 0) {
|
||||
|
||||
@@ -245,6 +245,7 @@ int LMSDevice::open()
|
||||
|
||||
m_dev_type = parse_dev_type(m_lms_dev);
|
||||
dev_desc = dev_param_map.at(m_dev_type);
|
||||
update_band_dev(m_dev_type);
|
||||
|
||||
if ((cfg->clock_ref != REF_EXTERNAL) && (cfg->clock_ref != REF_INTERNAL)) {
|
||||
LOGC(DDEV, ERROR) << "Invalid reference type";
|
||||
|
||||
@@ -530,6 +530,8 @@ int uhd_device::open()
|
||||
if (!parse_dev_type())
|
||||
return -1;
|
||||
|
||||
update_band_dev(dev_key(dev_type, tx_sps, rx_sps));
|
||||
|
||||
if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
|
||||
LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater";
|
||||
return -1;
|
||||
|
||||
@@ -95,7 +95,7 @@ NO_UBSAN static void detect_burst_generic(const gr_complex *input, gr_complex *c
|
||||
viterbi_detector(filtered_burst, burst_size, rhh, start_state, stop_states, 2, output);
|
||||
|
||||
for (unsigned int i = 0; i < burst_size; i++)
|
||||
output_binary[i] = (char)(output[i] * -127); // pre flip bits!
|
||||
output_binary[i] = output[i] > 0 ? -127 : 127; // pre flip bits!
|
||||
}
|
||||
|
||||
NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary,
|
||||
|
||||
@@ -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;
|
||||
@@ -183,7 +186,6 @@ struct blade_otw_buffer_helper {
|
||||
};
|
||||
|
||||
using dev_buf_t = typeof(blade_otw_buffer_helper<BLADE_BUFFER_SIZE, blade_speed_buffer_type::SS>::x);
|
||||
// using buf_in_use = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
|
||||
using bh_fn_t = std::function<int(dev_buf_t *)>;
|
||||
|
||||
template <typename T>
|
||||
@@ -191,12 +193,11 @@ struct blade_hw {
|
||||
struct bladerf *dev;
|
||||
struct bladerf_stream *rx_stream;
|
||||
struct bladerf_stream *tx_stream;
|
||||
// using pkt2buf = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
|
||||
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 +206,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 +228,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), 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,9 +260,11 @@ struct blade_hw {
|
||||
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);
|
||||
|
||||
blade_check(bladerf_open, &dev, "");
|
||||
if (!dev) {
|
||||
std::cerr << "open failed, device missing?" << std::endl;
|
||||
@@ -268,7 +279,8 @@ struct blade_hw {
|
||||
|
||||
bool is_locked;
|
||||
blade_check(bladerf_set_pll_enable, dev, true);
|
||||
blade_check(bladerf_set_pll_refclk, dev, 10000000UL);
|
||||
uint64_t refclock = 10000000UL;
|
||||
blade_check(bladerf_set_pll_refclk, dev, refclock);
|
||||
for (int i = 0; i < 20; i++) {
|
||||
usleep(50 * 1000);
|
||||
bladerf_get_pll_lock_state(dev, &is_locked);
|
||||
@@ -292,9 +304,10 @@ struct blade_hw {
|
||||
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);
|
||||
@@ -348,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);
|
||||
@@ -356,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);
|
||||
@@ -427,10 +440,12 @@ struct blade_hw {
|
||||
|
||||
auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
|
||||
{
|
||||
auto fn = [this] {
|
||||
using thist = decltype(this);
|
||||
auto fn = [](void *args) -> void * {
|
||||
thist t = reinterpret_cast<thist>(args);
|
||||
int status = 0;
|
||||
if (!stop_lower_threads_flag)
|
||||
status = bladerf_stream(rx_stream, BLADERF_RX_X1);
|
||||
status = bladerf_stream(t->rx_stream, BLADERF_RX_X1);
|
||||
if (status < 0)
|
||||
std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
|
||||
|
||||
@@ -440,10 +455,12 @@ struct blade_hw {
|
||||
}
|
||||
auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
|
||||
{
|
||||
auto fn = [this] {
|
||||
using thist = decltype(this);
|
||||
auto fn = [](void *args) -> void * {
|
||||
thist t = reinterpret_cast<thist>(args);
|
||||
int status = 0;
|
||||
if (!stop_lower_threads_flag)
|
||||
status = bladerf_stream(tx_stream, BLADERF_TX_X1);
|
||||
status = bladerf_stream(t->tx_stream, BLADERF_TX_X1);
|
||||
if (status < 0)
|
||||
std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ class spsc_cond_timeout_detail {
|
||||
std::condition_variable cond_r, cond_w;
|
||||
std::mutex lr, lw;
|
||||
std::atomic_int r_flag, w_flag;
|
||||
const int timeout_ms = 200;
|
||||
const int timeout_ms = 600;
|
||||
|
||||
public:
|
||||
explicit spsc_cond_timeout_detail() : r_flag(0), w_flag(0)
|
||||
|
||||
@@ -22,18 +22,14 @@
|
||||
#include "GSMCommon.h"
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <complex>
|
||||
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <thread>
|
||||
#include <fstream>
|
||||
|
||||
#include "ms.h"
|
||||
|
||||
extern "C" {
|
||||
#include "sch.h"
|
||||
}
|
||||
#include "threadsched.h"
|
||||
|
||||
dummylog ms_trx::dummy_log;
|
||||
|
||||
@@ -83,13 +79,11 @@ void ms_trx::start_lower_ms()
|
||||
if (stop_lower_threads_flag)
|
||||
return;
|
||||
auto fn = get_rx_burst_handler_fn(rx_bh());
|
||||
lower_rx_task = std::thread(fn);
|
||||
set_name_aff_sched(lower_rx_task.native_handle(), sched_params::thread_names::RXRUN);
|
||||
lower_rx_task = spawn_worker_thread(sched_params::thread_names::RXRUN, fn, this);
|
||||
|
||||
usleep(1000);
|
||||
auto fn2 = get_tx_burst_handler_fn(tx_bh());
|
||||
lower_tx_task = std::thread(fn2);
|
||||
set_name_aff_sched(lower_tx_task.native_handle(), sched_params::thread_names::TXRUN);
|
||||
lower_tx_task = spawn_worker_thread(sched_params::thread_names::TXRUN, fn2, this);
|
||||
|
||||
actually_enable_streams();
|
||||
}
|
||||
@@ -105,9 +99,9 @@ void ms_trx::stop_threads()
|
||||
stop_lower_threads_flag = true;
|
||||
close_device();
|
||||
std::cerr << "dev closed..." << std::endl;
|
||||
lower_rx_task.join();
|
||||
pthread_join(lower_rx_task, nullptr);
|
||||
std::cerr << "L rx dead..." << std::endl;
|
||||
lower_tx_task.join();
|
||||
pthread_join(lower_tx_task, nullptr);
|
||||
std::cerr << "L tx dead..." << std::endl;
|
||||
}
|
||||
|
||||
@@ -130,7 +124,7 @@ void ms_trx::submit_burst(blade_sample_type *buffer, int len, GSM::Time target)
|
||||
tosend.decTN(-diff_tn);
|
||||
|
||||
// in theory fn equal and tn+3 equal is also a problem...
|
||||
if (diff_fn < 0 || (diff_fn == 0 && (now_time.TN() - target_tn < 1))) {
|
||||
if (diff_fn < 0 || (diff_fn == 0 && (target_tn-now_time.TN() < 3))) {
|
||||
std::cerr << "## TX too late?! fn DIFF:" << diff_fn << " tn LOCAL: " << now_time.TN()
|
||||
<< " tn OTHER: " << target_tn << std::endl;
|
||||
return;
|
||||
|
||||
@@ -26,7 +26,7 @@
|
||||
#include <cstdint>
|
||||
#include <mutex>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
// #include <thread>
|
||||
|
||||
#if defined(BUILDBLADE)
|
||||
#include "bladerf_specific.h"
|
||||
@@ -42,9 +42,9 @@
|
||||
#include "GSMCommon.h"
|
||||
#include "itrq.h"
|
||||
#include "threadpool.h"
|
||||
#include "threadsched.h"
|
||||
|
||||
const unsigned int ONE_TS_BURST_LEN = (3 + 58 + 26 + 58 + 3 + 8.25) * 4 /*sps*/;
|
||||
const unsigned int NUM_RXQ_FRAMES = 1; // rx thread <-> upper rx queue
|
||||
const unsigned int SCH_LEN_SPS = (ONE_TS_BURST_LEN * 8 /*ts*/ * 12 /*frames*/);
|
||||
|
||||
template <typename T>
|
||||
@@ -133,7 +133,7 @@ struct one_burst {
|
||||
};
|
||||
};
|
||||
|
||||
using rx_queue_t = spsc_cond_timeout<8 * NUM_RXQ_FRAMES, one_burst, true, false>;
|
||||
using rx_queue_t = spsc_cond_timeout<4, one_burst, true, false>;
|
||||
|
||||
enum class SCH_STATE { SEARCHING, FOUND };
|
||||
|
||||
@@ -181,7 +181,6 @@ class time_keeper {
|
||||
std::lock_guard<std::mutex> g(m);
|
||||
global_time_keeper.incTN(1);
|
||||
global_ts_keeper = new_ts;
|
||||
// std::cerr << "u " << new_ts << std::endl;
|
||||
}
|
||||
void inc_and_update_safe(int64_t new_ts)
|
||||
{
|
||||
@@ -191,7 +190,6 @@ class time_keeper {
|
||||
assert(diff > 0.5 * ONE_TS_BURST_LEN);
|
||||
global_time_keeper.incTN(1);
|
||||
global_ts_keeper = new_ts;
|
||||
// std::cerr << "s " << new_ts << std::endl;
|
||||
}
|
||||
void dec_by_one()
|
||||
{
|
||||
@@ -217,52 +215,30 @@ class time_keeper {
|
||||
}
|
||||
};
|
||||
|
||||
static struct sched_params {
|
||||
enum thread_names { U_CTL = 0, U_RX, U_TX, SCH_SEARCH, MAIN, LEAKCHECK, RXRUN, TXRUN, _THRD_NAME_COUNT };
|
||||
enum target { ODROID = 0, PI4 };
|
||||
const char *name;
|
||||
int core;
|
||||
int schedtype;
|
||||
int prio;
|
||||
} schdp[][sched_params::_THRD_NAME_COUNT]{
|
||||
// used to globally initialize the sched/hw information
|
||||
struct sched_hw_info {
|
||||
int hw_cpus;
|
||||
sched_params::target hw_target;
|
||||
|
||||
sched_hw_info()
|
||||
{
|
||||
{ "upper_ctrl", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) },
|
||||
{ "upper_rx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
|
||||
{ "upper_tx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 1 },
|
||||
|
||||
{ "sch_search", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
|
||||
{ "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
|
||||
{ "leakcheck", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
|
||||
|
||||
{ "rxrun", 4, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
|
||||
{ "txrun", 5, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
|
||||
},
|
||||
{
|
||||
{ "upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) },
|
||||
{ "upper_rx", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
|
||||
{ "upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
|
||||
|
||||
{ "sch_search", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
|
||||
{ "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
|
||||
{ "leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
|
||||
|
||||
{ "rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
|
||||
{ "txrun", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
|
||||
},
|
||||
hw_cpus = std::thread::hardware_concurrency();
|
||||
hw_target = hw_cpus > 4 ? sched_params::target::ODROID : sched_params::target::PI4;
|
||||
set_sched_target(hw_target);
|
||||
std::cerr << "scheduling for: " << (hw_cpus > 4 ? "odroid" : "pi4") << std::endl;
|
||||
}
|
||||
};
|
||||
|
||||
using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>;
|
||||
|
||||
struct ms_trx : public BASET {
|
||||
struct ms_trx : public BASET, public sched_hw_info {
|
||||
using base = BASET;
|
||||
static dummylog dummy_log;
|
||||
unsigned int mTSC;
|
||||
unsigned int mBSIC;
|
||||
int timing_advance;
|
||||
bool do_auto_gain;
|
||||
bool use_va;
|
||||
|
||||
std::thread lower_rx_task;
|
||||
std::thread lower_tx_task;
|
||||
pthread_t lower_rx_task;
|
||||
pthread_t lower_tx_task;
|
||||
|
||||
// provides bursts to upper rx thread
|
||||
rx_queue_t rxqueue;
|
||||
@@ -278,9 +254,7 @@ struct ms_trx : public BASET {
|
||||
int64_t first_sch_ts_start = -1;
|
||||
|
||||
time_keeper timekeeper;
|
||||
int hw_cpus;
|
||||
sched_params::target hw_target;
|
||||
single_thread_pool worker_thread;
|
||||
single_thread_pool worker_thread; // uses base class sched target hw info
|
||||
|
||||
void start_lower_ms();
|
||||
std::atomic<bool> upper_is_ready;
|
||||
@@ -298,16 +272,12 @@ struct ms_trx : public BASET {
|
||||
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 }, hw_cpus(std::thread::hardware_concurrency()),
|
||||
hw_target(hw_cpus > 4 ? sched_params::target::ODROID : sched_params::target::PI4),
|
||||
upper_is_ready(false)
|
||||
rcv_done{ false }, sch_thread_done{ false }, upper_is_ready(false)
|
||||
{
|
||||
std::cerr << "scheduling for: " << (hw_cpus > 4 ? "odroid" : "pi4") << std::endl;
|
||||
set_name_aff_sched(worker_thread.get_handle(), sched_params::thread_names::SCH_SEARCH);
|
||||
}
|
||||
|
||||
virtual ~ms_trx()
|
||||
@@ -324,73 +294,4 @@ struct ms_trx : public BASET {
|
||||
assert(val > -127 && val < 128);
|
||||
timing_advance = val * 4;
|
||||
}
|
||||
|
||||
void set_name_aff_sched(sched_params::thread_names name)
|
||||
{
|
||||
set_name_aff_sched(pthread_self(), name);
|
||||
}
|
||||
|
||||
void set_name_aff_sched(std::thread::native_handle_type h, sched_params::thread_names name)
|
||||
{
|
||||
auto tgt = schdp[hw_target][name];
|
||||
// std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << std::endl;
|
||||
set_name_aff_sched(h, tgt.name, tgt.core, tgt.schedtype, tgt.prio);
|
||||
}
|
||||
|
||||
using pt_sig = void *(*)(void *);
|
||||
|
||||
pthread_t spawn_worker_thread(sched_params::thread_names name, pt_sig fun, void *arg)
|
||||
{
|
||||
auto tgt = schdp[hw_target][name];
|
||||
// std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << " prio:" << tgt.prio << std::endl;
|
||||
return do_spawn_thr(tgt.name, tgt.core, tgt.schedtype, tgt.prio, fun, arg);
|
||||
}
|
||||
|
||||
private:
|
||||
void set_name_aff_sched(std::thread::native_handle_type h, const char *name, int cpunum, int schedtype,
|
||||
int prio)
|
||||
{
|
||||
pthread_setname_np(h, name);
|
||||
|
||||
cpu_set_t cpuset;
|
||||
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(cpunum, &cpuset);
|
||||
|
||||
if (pthread_setaffinity_np(h, sizeof(cpuset), &cpuset) < 0) {
|
||||
std::cerr << name << " affinity: errreur! " << std::strerror(errno);
|
||||
return exit(0);
|
||||
}
|
||||
|
||||
sched_param sch_params;
|
||||
sch_params.sched_priority = prio;
|
||||
if (pthread_setschedparam(h, schedtype, &sch_params) < 0) {
|
||||
std::cerr << name << " sched: errreur! " << std::strerror(errno);
|
||||
return exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
pthread_t do_spawn_thr(const char *name, int cpunum, int schedtype, int prio, pt_sig fun, void *arg)
|
||||
{
|
||||
pthread_t thread;
|
||||
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
|
||||
sched_param sch_params;
|
||||
sch_params.sched_priority = prio;
|
||||
cpu_set_t cpuset;
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(cpunum, &cpuset);
|
||||
auto a = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
|
||||
a |= pthread_attr_setschedpolicy(&attr, schedtype);
|
||||
a |= pthread_attr_setschedparam(&attr, &sch_params);
|
||||
a |= pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
|
||||
if(a)
|
||||
std::cerr << "thread arg rc:" << a << std::endl;
|
||||
pthread_create(&thread, &attr, fun, arg);
|
||||
pthread_setname_np(thread, name);
|
||||
pthread_attr_destroy(&attr);
|
||||
return thread;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include "sigProcLib.h"
|
||||
#include "signalVector.h"
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <complex>
|
||||
@@ -52,6 +54,8 @@ extern "C" {
|
||||
|
||||
#define PRINT_Q_OVERFLOW
|
||||
|
||||
extern std::atomic<bool> g_exit_flag;
|
||||
|
||||
bool ms_trx::decode_sch(char *bits, bool update_global_clock)
|
||||
{
|
||||
int fn;
|
||||
@@ -85,8 +89,6 @@ bool ms_trx::decode_sch(char *bits, bool update_global_clock)
|
||||
mBSIC = sch.bsic;
|
||||
mTSC = sch.bsic & 0x7;
|
||||
timekeeper.set(fn, 0);
|
||||
// global_time_keeper.FN(fn);
|
||||
// global_time_keeper.TN(0);
|
||||
}
|
||||
|
||||
return true;
|
||||
@@ -106,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);
|
||||
}
|
||||
@@ -139,10 +141,10 @@ bool ms_trx::handle_sch_or_nb()
|
||||
memcpy(brst.sch_bits, sch_demod_bits, sizeof(sch_demod_bits));
|
||||
}
|
||||
|
||||
while (upper_is_ready && !rxqueue.spsc_push(&brst))
|
||||
while (!g_exit_flag && upper_is_ready && !rxqueue.spsc_push(&brst))
|
||||
;
|
||||
|
||||
if (do_auto_gain)
|
||||
if (!use_agc)
|
||||
maybe_update_gain(brst);
|
||||
|
||||
return false;
|
||||
@@ -155,40 +157,144 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
|
||||
auto current_gsm_time = timekeeper.gsmtime();
|
||||
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;
|
||||
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;
|
||||
memset((void *)&sch_acq_buffer[0], 0, sizeof(sch_acq_buffer));
|
||||
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 (sch_decode_success) {
|
||||
const auto ts_offset_symb = 0;
|
||||
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) {
|
||||
// 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;
|
||||
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 // 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;
|
||||
|
||||
// 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);
|
||||
|
||||
int howmuchdelay = ebp.toa * 4;
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
// 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);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << start << " " << current_gsm_time.FN()
|
||||
<< ":" << current_gsm_time.TN() << std::endl;
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
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 false;
|
||||
}
|
||||
@@ -227,12 +333,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)
|
||||
@@ -262,7 +368,6 @@ void ms_trx::grab_bursts(dev_buf_t *rcd)
|
||||
|
||||
if (fracts)
|
||||
timekeeper.inc_both();
|
||||
// timekeeper.inc_and_update(first_sch_ts_start + 1 * ONE_TS_BURST_LEN);
|
||||
|
||||
timekeeper.dec_by_one(); // oops, off by one?
|
||||
|
||||
|
||||
@@ -37,6 +37,6 @@ struct internal_q_tx_buf {
|
||||
memcpy(buf, (void *)br->burst, br->burst_len);
|
||||
}
|
||||
};
|
||||
using tx_queue_t = spsc_cond<8 * 1, internal_q_tx_buf, true, false>;
|
||||
using tx_queue_t = spsc_cond_timeout<8 * 1, internal_q_tx_buf, true, false>;
|
||||
using cmd_queue_t = spsc_cond_timeout<8 * 1, trxcon_phyif_cmd, true, false>;
|
||||
using cmdr_queue_t = spsc_cond<8 * 1, trxcon_phyif_rsp, false, false>;
|
||||
using cmdr_queue_t = spsc_cond_timeout<8 * 1, trxcon_phyif_rsp, false, false>;
|
||||
|
||||
@@ -50,6 +50,7 @@ void __lsan_do_recoverable_leak_check();
|
||||
|
||||
#include "ms_trxcon_if.h"
|
||||
#include "ms_upper.h"
|
||||
#include "threadsched.h"
|
||||
|
||||
extern bool trxc_l1ctl_init(void *tallctx);
|
||||
struct trxcon_inst *g_trxcon;
|
||||
@@ -159,10 +160,11 @@ static void *static_alloc(size_t newSize)
|
||||
|
||||
bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
|
||||
{
|
||||
float pow, avg = 1.0;
|
||||
// float pow, avg = 1.0;
|
||||
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;
|
||||
@@ -170,9 +172,11 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
|
||||
std::fill(workbuf, workbuf + workbuf_size, 0);
|
||||
// assert(sv.begin() == &workbuf[40]);
|
||||
|
||||
while (!rxqueue.spsc_pop(&e)) {
|
||||
while (!rxqueue.spsc_pop(&e) && !g_exit_flag) {
|
||||
rxqueue.spsc_prep_pop();
|
||||
}
|
||||
if (g_exit_flag)
|
||||
return false;
|
||||
|
||||
wTime = e.gsmts;
|
||||
|
||||
@@ -182,8 +186,11 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
|
||||
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
|
||||
@@ -198,40 +205,75 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
|
||||
return true;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
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);
|
||||
|
||||
@@ -258,6 +300,7 @@ void upper_trx::driveReceiveFIFO()
|
||||
trxcon_phyif_handle_burst_ind(g_trxcon, &bi);
|
||||
}
|
||||
|
||||
burstTime.incTN(2);
|
||||
struct trxcon_phyif_rts_ind rts {
|
||||
static_cast<uint32_t>(burstTime.FN()), static_cast<uint8_t>(burstTime.TN())
|
||||
};
|
||||
@@ -380,13 +423,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;
|
||||
@@ -417,6 +460,12 @@ void sighandler(int sigset)
|
||||
}
|
||||
}
|
||||
|
||||
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");
|
||||
@@ -437,6 +486,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;
|
||||
@@ -448,15 +513,14 @@ 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) {
|
||||
std::cerr << "Error initializing hardware, quitting.." << std::endl;
|
||||
return -1;
|
||||
}
|
||||
trx->set_name_aff_sched(sched_params::thread_names::MAIN);
|
||||
set_name_aff_sched(sched_params::thread_names::MAIN);
|
||||
|
||||
if (!trxc_l1ctl_init(tall_trxcon_ctx)) {
|
||||
std::cerr << "Error initializing l1ctl, quitting.." << std::endl;
|
||||
|
||||
@@ -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){};
|
||||
};
|
||||
|
||||
264
Transceiver52M/ms/mssdr_vty.c
Normal file
264
Transceiver52M/ms/mssdr_vty.c
Normal file
@@ -0,0 +1,264 @@
|
||||
/*
|
||||
* (C) 2024 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Eric Wild <ewild@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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#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 <tom@tsou.cc>\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 <info@sysmocom.de>\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;
|
||||
}
|
||||
44
Transceiver52M/ms/mssdr_vty.h
Normal file
44
Transceiver52M/ms/mssdr_vty.h
Normal file
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
/*
|
||||
* (C) 2024 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Eric Wild <ewild@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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
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;
|
||||
@@ -20,13 +20,11 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <functional>
|
||||
#include <thread>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <queue>
|
||||
#include "threadsched.h"
|
||||
|
||||
struct single_thread_pool {
|
||||
std::mutex m;
|
||||
@@ -34,7 +32,7 @@ struct single_thread_pool {
|
||||
std::atomic<bool> stop_flag;
|
||||
std::atomic<bool> is_ready;
|
||||
std::deque<std::function<void()>> wq;
|
||||
std::thread worker_thread;
|
||||
pthread_t worker_thread;
|
||||
|
||||
template <class F>
|
||||
void add_task(F &&f)
|
||||
@@ -45,19 +43,23 @@ struct single_thread_pool {
|
||||
return;
|
||||
}
|
||||
|
||||
single_thread_pool() : stop_flag(false), is_ready(false), worker_thread(std::thread([this] { thread_loop(); }))
|
||||
single_thread_pool() : stop_flag(false), is_ready(false)
|
||||
{
|
||||
worker_thread = spawn_worker_thread(
|
||||
sched_params::thread_names::SCH_SEARCH,
|
||||
[](void *args) -> void * {
|
||||
using thist = decltype(this);
|
||||
thist t = reinterpret_cast<thist>(args);
|
||||
t->thread_loop();
|
||||
return 0;
|
||||
},
|
||||
this);
|
||||
}
|
||||
~single_thread_pool()
|
||||
{
|
||||
stop();
|
||||
}
|
||||
|
||||
std::thread::native_handle_type get_handle()
|
||||
{
|
||||
return worker_thread.native_handle();
|
||||
}
|
||||
|
||||
private:
|
||||
void stop()
|
||||
{
|
||||
@@ -67,7 +69,7 @@ struct single_thread_pool {
|
||||
stop_flag = true;
|
||||
cv.notify_one();
|
||||
}
|
||||
worker_thread.join();
|
||||
pthread_join(worker_thread, nullptr);
|
||||
}
|
||||
|
||||
void thread_loop()
|
||||
|
||||
104
Transceiver52M/ms/threadsched.cpp
Normal file
104
Transceiver52M/ms/threadsched.cpp
Normal file
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Eric Wild <ewild@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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cerrno>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <iostream>
|
||||
#include <thread>
|
||||
|
||||
extern "C" {
|
||||
#include <pthread.h>
|
||||
}
|
||||
|
||||
#include "threadsched.h"
|
||||
|
||||
sched_params::target scheduling_target;
|
||||
|
||||
void set_sched_target(sched_params::target t)
|
||||
{
|
||||
scheduling_target = t;
|
||||
}
|
||||
|
||||
void set_name_aff_sched(std::thread::native_handle_type h, const char *name, int cpunum, int schedtype, int prio)
|
||||
{
|
||||
pthread_setname_np(h, name);
|
||||
|
||||
cpu_set_t cpuset;
|
||||
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(cpunum, &cpuset);
|
||||
|
||||
if (pthread_setaffinity_np(h, sizeof(cpuset), &cpuset) < 0) {
|
||||
std::cerr << name << " affinity: errreur! " << std::strerror(errno);
|
||||
return exit(0);
|
||||
}
|
||||
|
||||
sched_param sch_params;
|
||||
sch_params.sched_priority = prio;
|
||||
if (pthread_setschedparam(h, schedtype, &sch_params) < 0) {
|
||||
std::cerr << name << " sched: errreur! " << std::strerror(errno);
|
||||
return exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
static pthread_t do_spawn_thr(const char *name, int cpunum, int schedtype, int prio, worker_func_sig fun, void *arg)
|
||||
{
|
||||
pthread_t thread;
|
||||
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
|
||||
sched_param sch_params;
|
||||
sch_params.sched_priority = prio;
|
||||
cpu_set_t cpuset;
|
||||
CPU_ZERO(&cpuset);
|
||||
CPU_SET(cpunum, &cpuset);
|
||||
auto a = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
|
||||
a |= pthread_attr_setschedpolicy(&attr, schedtype);
|
||||
a |= pthread_attr_setschedparam(&attr, &sch_params);
|
||||
a |= pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
|
||||
if (a)
|
||||
std::cerr << "thread arg rc:" << a << std::endl;
|
||||
pthread_create(&thread, &attr, fun, arg);
|
||||
pthread_setname_np(thread, name);
|
||||
pthread_attr_destroy(&attr);
|
||||
return thread;
|
||||
}
|
||||
|
||||
void set_name_aff_sched(std::thread::native_handle_type h, sched_params::thread_names name)
|
||||
{
|
||||
auto tgt = schdp[scheduling_target][name];
|
||||
// std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << std::endl;
|
||||
set_name_aff_sched(h, tgt.name, tgt.core, tgt.schedtype, tgt.prio);
|
||||
}
|
||||
|
||||
void set_name_aff_sched(sched_params::thread_names name)
|
||||
{
|
||||
set_name_aff_sched(pthread_self(), name);
|
||||
}
|
||||
|
||||
pthread_t spawn_worker_thread(sched_params::thread_names name, worker_func_sig fun, void *arg)
|
||||
{
|
||||
auto tgt = schdp[scheduling_target][name];
|
||||
// std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << " prio:" << tgt.prio << std::endl;
|
||||
return do_spawn_thr(tgt.name, tgt.core, tgt.schedtype, tgt.prio, fun, arg);
|
||||
}
|
||||
68
Transceiver52M/ms/threadsched.h
Normal file
68
Transceiver52M/ms/threadsched.h
Normal file
@@ -0,0 +1,68 @@
|
||||
#pragma once
|
||||
/*
|
||||
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Eric Wild <ewild@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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
extern "C" {
|
||||
#include <pthread.h>
|
||||
#include <sched.h>
|
||||
}
|
||||
|
||||
static struct sched_params {
|
||||
enum thread_names { U_CTL = 0, U_RX, U_TX, SCH_SEARCH, MAIN, LEAKCHECK, RXRUN, TXRUN, _THRD_NAME_COUNT };
|
||||
enum target { ODROID = 0, PI4 };
|
||||
const char *name;
|
||||
int core;
|
||||
int schedtype;
|
||||
int prio;
|
||||
} schdp[][sched_params::_THRD_NAME_COUNT]{
|
||||
{
|
||||
{ "upper_ctrl", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) },
|
||||
{ "upper_rx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
|
||||
{ "upper_tx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 1 },
|
||||
|
||||
{ "sch_search", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
|
||||
{ "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
|
||||
{ "leakcheck", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
|
||||
|
||||
{ "rxrun", 4, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
|
||||
{ "txrun", 5, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
|
||||
},
|
||||
{
|
||||
{ "upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) },
|
||||
{ "upper_rx", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
|
||||
{ "upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
|
||||
|
||||
{ "sch_search", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
|
||||
{ "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
|
||||
{ "leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
|
||||
|
||||
{ "rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
|
||||
{ "txrun", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
|
||||
},
|
||||
};
|
||||
|
||||
void set_sched_target(sched_params::target t);
|
||||
|
||||
using worker_func_sig = void *(*)(void *);
|
||||
|
||||
void set_name_aff_sched(sched_params::thread_names name);
|
||||
|
||||
pthread_t spawn_worker_thread(sched_params::thread_names name, worker_func_sig fun, void *arg);
|
||||
@@ -231,24 +231,29 @@ struct uhd_hw {
|
||||
|
||||
auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
|
||||
{
|
||||
auto fn = [this, burst_handler] {
|
||||
// C cb -> ghetto closure capture, which is fine, the args never change.
|
||||
static auto rx_burst_cap_this = this;
|
||||
static auto rx_burst_cap_bh = burst_handler;
|
||||
auto fn = [](void *args) -> void * {
|
||||
pthread_setname_np(pthread_self(), "rxrun");
|
||||
|
||||
uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
|
||||
stream_cmd.stream_now = true;
|
||||
stream_cmd.time_spec = uhd::time_spec_t();
|
||||
rx_stream->issue_stream_cmd(stream_cmd);
|
||||
rx_burst_cap_this->rx_stream->issue_stream_cmd(stream_cmd);
|
||||
|
||||
while (!stop_lower_threads_flag) {
|
||||
rx_cb(burst_handler);
|
||||
while (!rx_burst_cap_this->stop_lower_threads_flag) {
|
||||
rx_burst_cap_this->rx_cb(rx_burst_cap_bh);
|
||||
}
|
||||
return 0;
|
||||
};
|
||||
return fn;
|
||||
}
|
||||
auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
|
||||
{
|
||||
auto fn = [] {
|
||||
auto fn = [](void *args) -> void * {
|
||||
// dummy
|
||||
return 0;
|
||||
};
|
||||
return fn;
|
||||
}
|
||||
|
||||
@@ -129,6 +129,8 @@ struct PulseSequence {
|
||||
static CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
|
||||
static CorrelationSequence *gEdgeMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
|
||||
static CorrelationSequence *gRACHSequences[] = {NULL,NULL,NULL};
|
||||
static CorrelationSequence *gSCHSequence = NULL;
|
||||
static CorrelationSequence *gDummySequence = NULL;
|
||||
static PulseSequence *GSMPulse1 = NULL;
|
||||
static PulseSequence *GSMPulse4 = NULL;
|
||||
|
||||
@@ -151,6 +153,12 @@ void sigProcLibDestroy()
|
||||
gRACHSequences[i] = NULL;
|
||||
}
|
||||
|
||||
delete gSCHSequence;
|
||||
gSCHSequence = NULL;
|
||||
|
||||
delete gDummySequence;
|
||||
gDummySequence = NULL;
|
||||
|
||||
delete GMSKRotation1;
|
||||
delete GMSKReverseRotation1;
|
||||
delete GMSKRotation4;
|
||||
@@ -315,6 +323,7 @@ static signalVector *convolve(const signalVector *x, const signalVector *h,
|
||||
append = true;
|
||||
break;
|
||||
case CUSTOM:
|
||||
// FIXME: x->getstart?
|
||||
if (start < h->size() - 1) {
|
||||
head = h->size() - start;
|
||||
append = true;
|
||||
@@ -1289,6 +1298,77 @@ release:
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool generateDummyMidamble(int sps)
|
||||
{
|
||||
bool status = true;
|
||||
float toa;
|
||||
complex *data = NULL;
|
||||
signalVector *autocorr = NULL, *midamble = NULL;
|
||||
signalVector *midMidamble = NULL, *_midMidamble = NULL;
|
||||
|
||||
delete gDummySequence;
|
||||
|
||||
/* Use middle 16 bits of each TSC. Correlation sequence is not pulse shaped */
|
||||
midMidamble = modulateBurst(gDummyBurstTSC.segment(5,16), 0, sps, true);
|
||||
if (!midMidamble)
|
||||
return false;
|
||||
|
||||
/* Simulated receive sequence is pulse shaped */
|
||||
midamble = modulateBurst(gDummyBurstTSC, 0, sps, false);
|
||||
if (!midamble) {
|
||||
status = false;
|
||||
goto release;
|
||||
}
|
||||
|
||||
// NOTE: Because ideal TSC 16-bit midamble is 66 symbols into burst,
|
||||
// the ideal TSC has an + 180 degree phase shift,
|
||||
// due to the pi/2 frequency shift, that
|
||||
// needs to be accounted for.
|
||||
// 26-midamble is 61 symbols into burst, has +90 degree phase shift.
|
||||
scaleVector(*midMidamble, complex(-1.0, 0.0));
|
||||
scaleVector(*midamble, complex(0.0, 1.0));
|
||||
|
||||
conjugateVector(*midMidamble);
|
||||
|
||||
/* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
|
||||
data = (complex *) convolve_h_alloc(midMidamble->size());
|
||||
_midMidamble = new signalVector(data, 0, midMidamble->size(), convolve_h_alloc, free);
|
||||
_midMidamble->setAligned(true);
|
||||
midMidamble->copyTo(*_midMidamble);
|
||||
|
||||
autocorr = convolve(midamble, _midMidamble, NULL, NO_DELAY);
|
||||
if (!autocorr) {
|
||||
status = false;
|
||||
goto release;
|
||||
}
|
||||
|
||||
gDummySequence = new CorrelationSequence;
|
||||
gDummySequence->sequence = _midMidamble;
|
||||
gDummySequence->gain = peakDetect(*autocorr, &toa, NULL);
|
||||
|
||||
/* For 1 sps only
|
||||
* (Half of correlation length - 1) + midpoint of pulse shape + remainder
|
||||
* 13.5 = (16 / 2 - 1) + 1.5 + (26 - 10) / 2
|
||||
*/
|
||||
if (sps == 1)
|
||||
gDummySequence->toa = toa - 13.5;
|
||||
else
|
||||
gDummySequence->toa = 0;
|
||||
|
||||
release:
|
||||
delete autocorr;
|
||||
delete midamble;
|
||||
delete midMidamble;
|
||||
|
||||
if (!status) {
|
||||
delete _midMidamble;
|
||||
free(data);
|
||||
gDummySequence = NULL;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static CorrelationSequence *generateEdgeMidamble(int tsc)
|
||||
{
|
||||
complex *data = NULL;
|
||||
@@ -1384,6 +1464,69 @@ release:
|
||||
return status;
|
||||
}
|
||||
|
||||
bool generateSCHSequence(int sps)
|
||||
{
|
||||
bool status = true;
|
||||
float toa;
|
||||
complex *data = NULL;
|
||||
signalVector *autocorr = NULL;
|
||||
signalVector *seq0 = NULL, *seq1 = NULL, *_seq1 = NULL;
|
||||
|
||||
delete gSCHSequence;
|
||||
|
||||
seq0 = modulateBurst(gSCHSynchSequence, 0, sps, false);
|
||||
if (!seq0)
|
||||
return false;
|
||||
|
||||
seq1 = modulateBurst(gSCHSynchSequence, 0, sps, true);
|
||||
if (!seq1) {
|
||||
status = false;
|
||||
goto release;
|
||||
}
|
||||
|
||||
conjugateVector(*seq1);
|
||||
|
||||
/* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
|
||||
data = (complex *) convolve_h_alloc(seq1->size());
|
||||
_seq1 = new signalVector(data, 0, seq1->size(), convolve_h_alloc, free);
|
||||
_seq1->setAligned(true);
|
||||
seq1->copyTo(*_seq1);
|
||||
|
||||
autocorr = convolve(seq0, _seq1, autocorr, NO_DELAY);
|
||||
if (!autocorr) {
|
||||
status = false;
|
||||
goto release;
|
||||
}
|
||||
|
||||
gSCHSequence = new CorrelationSequence;
|
||||
gSCHSequence->sequence = _seq1;
|
||||
gSCHSequence->buffer = data;
|
||||
gSCHSequence->gain = peakDetect(*autocorr, &toa, NULL);
|
||||
|
||||
/* For 1 sps only
|
||||
* (Half of correlation length - 1) + midpoint of pulse shaping filer
|
||||
* 20.5 = (64 / 2 - 1) + 1.5
|
||||
*/
|
||||
if (sps == 1)
|
||||
gSCHSequence->toa = toa - 32.5;
|
||||
else
|
||||
gSCHSequence->toa = 0.0;
|
||||
|
||||
release:
|
||||
delete autocorr;
|
||||
delete seq0;
|
||||
delete seq1;
|
||||
|
||||
if (!status) {
|
||||
delete _seq1;
|
||||
free(data);
|
||||
gSCHSequence = NULL;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Peak-to-average computation +/- range from peak in symbols
|
||||
*/
|
||||
@@ -1441,14 +1584,15 @@ float energyDetect(const signalVector &rxBurst, unsigned windowLength)
|
||||
return energy/windowLength;
|
||||
}
|
||||
|
||||
static signalVector *downsampleBurst(const signalVector &burst)
|
||||
static signalVector *downsampleBurst(const signalVector &burst, int in_len = DOWNSAMPLE_IN_LEN,
|
||||
int out_len = DOWNSAMPLE_OUT_LEN)
|
||||
{
|
||||
signalVector in(DOWNSAMPLE_IN_LEN, dnsampler->len());
|
||||
signalVector *out = new signalVector(DOWNSAMPLE_OUT_LEN);
|
||||
burst.copyToSegment(in, 0, DOWNSAMPLE_IN_LEN);
|
||||
signalVector in(in_len, dnsampler->len());
|
||||
// gSCHSequence->sequence->size(), ensure next conv has no realloc
|
||||
signalVector *out = new signalVector(out_len, 64);
|
||||
burst.copyToSegment(in, 0, in_len);
|
||||
|
||||
if (dnsampler->rotate((float *) in.begin(), DOWNSAMPLE_IN_LEN,
|
||||
(float *) out->begin(), DOWNSAMPLE_OUT_LEN) < 0) {
|
||||
if (dnsampler->rotate((float *)in.begin(), in_len, (float *)out->begin(), out_len) < 0) {
|
||||
delete out;
|
||||
out = NULL;
|
||||
}
|
||||
@@ -1469,6 +1613,12 @@ static float computeCI(const signalVector *burst, const CorrelationSequence *syn
|
||||
/* Integer position where the sequence starts */
|
||||
const int ps = start + 1 - N + (int)roundf(toa);
|
||||
|
||||
if(ps < 0) // might be -22 for toa 40 with N=64, if off by a lot during sch ms sync
|
||||
return 0;
|
||||
|
||||
if (ps + N > (int)burst->size())
|
||||
return 0;
|
||||
|
||||
/* Estimate Signal power */
|
||||
S = 0.0f;
|
||||
for (int i=0, j=ps; i<(int)N; i++,j++)
|
||||
@@ -1652,6 +1802,80 @@ static int detectRACHBurst(const signalVector &burst, float threshold, int sps,
|
||||
return rc;
|
||||
}
|
||||
|
||||
int detectSCHBurst(signalVector &burst,
|
||||
float thresh,
|
||||
int sps,
|
||||
sch_detect_type state, struct estim_burst_params *ebp)
|
||||
{
|
||||
int rc, start, target, head, tail, len;
|
||||
complex _amp;
|
||||
CorrelationSequence *sync;
|
||||
|
||||
if ((sps != 1) && (sps != 4))
|
||||
return -1;
|
||||
|
||||
target = 3 + 39 + 64;
|
||||
|
||||
switch (state) {
|
||||
case sch_detect_type::SCH_DETECT_NARROW:
|
||||
head = 4;
|
||||
tail = 4;
|
||||
break;
|
||||
case sch_detect_type::SCH_DETECT_BUFFER:
|
||||
target = 1;
|
||||
head = 0;
|
||||
tail = (12 * 8 * 625) / 4; // 12 frames, downsampled /4 to 1 sps
|
||||
break;
|
||||
case sch_detect_type::SCH_DETECT_FULL:
|
||||
default:
|
||||
head = target - 1;
|
||||
tail = 39 + 3 + 9;
|
||||
break;
|
||||
}
|
||||
|
||||
start = (target - head) * 1 - 1;
|
||||
len = (head + tail) * 1;
|
||||
sync = gSCHSequence;
|
||||
signalVector corr(len);
|
||||
|
||||
signalVector *dec = downsampleBurst(burst, len * 4, len);
|
||||
rc = detectBurst(*dec, corr, sync, thresh, 1, start, len, ebp);
|
||||
delete dec;
|
||||
|
||||
if (rc < 0) {
|
||||
return -1;
|
||||
} else if (!rc) {
|
||||
ebp->amp = 0.0f;
|
||||
ebp->toa = 0.0f;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (state == sch_detect_type::SCH_DETECT_BUFFER)
|
||||
ebp->toa = ebp->toa - (3 + 39 + 64);
|
||||
else {
|
||||
/* Subtract forward search bits from delay */
|
||||
ebp->toa = ebp->toa - head;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int detectDummyBurst(const signalVector &burst, float threshold,
|
||||
int sps, unsigned max_toa, struct estim_burst_params *ebp)
|
||||
{
|
||||
int rc, target, head, tail;
|
||||
CorrelationSequence *sync;
|
||||
|
||||
target = 3 + 58 + 16 + 5;
|
||||
head = 10;
|
||||
tail = 6 + max_toa;
|
||||
sync = gDummySequence;
|
||||
|
||||
ebp->tsc = 0;
|
||||
rc = detectGeneralBurst(burst, threshold, sps, target, head, tail, sync, ebp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Normal burst detection
|
||||
*
|
||||
@@ -1670,7 +1894,7 @@ static int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float th
|
||||
return -SIGERR_UNSUPPORTED;
|
||||
|
||||
target = 3 + 58 + 16 + 5;
|
||||
head = 6;
|
||||
head = 10;
|
||||
tail = 6 + max_toa;
|
||||
sync = gMidambles[tsc];
|
||||
|
||||
@@ -1719,6 +1943,9 @@ int detectAnyBurst(const signalVector &burst, unsigned tsc, float threshold,
|
||||
case RACH:
|
||||
rc = detectRACHBurst(burst, threshold, sps, max_toa, type == EXT_RACH, ebp);
|
||||
break;
|
||||
case IDLE:
|
||||
rc = detectDummyBurst(burst, threshold, sps, max_toa, ebp);
|
||||
break;
|
||||
default:
|
||||
LOG(ERR) << "Invalid correlation type";
|
||||
}
|
||||
@@ -1921,6 +2148,9 @@ bool sigProcLibSetup()
|
||||
generateRACHSequence(&gRACHSequences[1], gRACHSynchSequenceTS1, 1);
|
||||
generateRACHSequence(&gRACHSequences[2], gRACHSynchSequenceTS2, 1);
|
||||
|
||||
generateSCHSequence(1);
|
||||
generateDummyMidamble(1);
|
||||
|
||||
for (int tsc = 0; tsc < 8; tsc++) {
|
||||
generateMidamble(1, tsc);
|
||||
gEdgeMidambles[tsc] = generateEdgeMidamble(tsc);
|
||||
|
||||
@@ -31,6 +31,7 @@ enum CorrType{
|
||||
TSC, ///< timeslot should contain a normal burst
|
||||
EXT_RACH, ///< timeslot should contain an extended access burst
|
||||
RACH, ///< timeslot should contain an access burst
|
||||
SCH,
|
||||
EDGE, ///< timeslot should contain an EDGE burst
|
||||
IDLE ///< timeslot is an idle (or dummy) burst
|
||||
};
|
||||
@@ -93,6 +94,8 @@ signalVector *generateDummyBurst(int sps, int tn);
|
||||
void scaleVector(signalVector &x,
|
||||
complex scale);
|
||||
|
||||
signalVector *delayVector(const signalVector *in, signalVector *out, float delay);
|
||||
|
||||
/**
|
||||
Rough energy estimator.
|
||||
@param rxBurst A GSM burst.
|
||||
@@ -133,6 +136,17 @@ int detectAnyBurst(const signalVector &burst,
|
||||
unsigned max_toa,
|
||||
struct estim_burst_params *ebp);
|
||||
|
||||
enum class sch_detect_type {
|
||||
SCH_DETECT_FULL,
|
||||
SCH_DETECT_NARROW,
|
||||
SCH_DETECT_BUFFER,
|
||||
};
|
||||
|
||||
int detectSCHBurst(signalVector &rxBurst,
|
||||
float detectThreshold,
|
||||
int sps,
|
||||
sch_detect_type state, struct estim_burst_params *ebp);
|
||||
|
||||
/** Demodulate burst basde on type and output soft bits */
|
||||
SoftVector *demodAnyBurst(const signalVector &burst, CorrType type,
|
||||
int sps, struct estim_burst_params *ebp);
|
||||
|
||||
@@ -374,6 +374,7 @@ AC_CONFIG_FILES([\
|
||||
tests/CommonLibs/Makefile \
|
||||
tests/Transceiver52M/Makefile \
|
||||
utils/Makefile \
|
||||
utils/va-test/Makefile \
|
||||
doc/Makefile \
|
||||
doc/examples/Makefile \
|
||||
contrib/Makefile \
|
||||
|
||||
@@ -85,7 +85,17 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
||||
export LD_LIBRARY_PATH="$inst/lib"
|
||||
export PATH="$inst/bin:$PATH"
|
||||
|
||||
CONFIG="--enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms --with-ipc --with-mstrx $INSTR"
|
||||
CONFIG="
|
||||
--enable-sanitize
|
||||
--enable-werror
|
||||
--with-bladerf
|
||||
--with-ipc
|
||||
--with-lms
|
||||
--with-mstrx
|
||||
--with-uhd
|
||||
--with-usrp1
|
||||
$INSTR
|
||||
"
|
||||
|
||||
# Additional configure options and depends
|
||||
if [ "$WITH_MANUALS" = "1" ]; then
|
||||
@@ -109,7 +119,7 @@ $MAKE check \
|
||||
|| cat-testlogs.sh
|
||||
|
||||
if arch | grep -v -q arm; then
|
||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck \
|
||||
DISTCHECK_CONFIGURE_FLAGS="$(echo $CONFIG | tr -d '\n')" $MAKE $PARALLEL_MAKE distcheck \
|
||||
|| cat-testlogs.sh
|
||||
fi
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ Type=simple
|
||||
Restart=always
|
||||
StateDirectory=osmocom
|
||||
WorkingDirectory=%S/osmocom
|
||||
Environment=HOME=%h
|
||||
ExecStart=/usr/bin/osmo-trx-uhd -C /etc/osmocom/osmo-trx-uhd.cfg
|
||||
RestartSec=2
|
||||
# CPU scheduling policy:
|
||||
|
||||
20
debian/control
vendored
20
debian/control
vendored
@@ -14,6 +14,7 @@ Build-Depends: debhelper (>= 10),
|
||||
libtalloc-dev,
|
||||
libusrp-dev,
|
||||
liblimesuite-dev,
|
||||
libbladerf-dev,
|
||||
libosmocore-dev (>= 1.9.0),
|
||||
osmo-gsm-manuals-dev (>= 1.5.0)
|
||||
Standards-Version: 3.9.6
|
||||
@@ -110,6 +111,25 @@ Description: SDR transceiver that implements Layer 1 of a GSM BTS (generic IPC)
|
||||
between different telecommunication associations for developing new
|
||||
generations of mobile phone networks. (post-2G/GSM)
|
||||
|
||||
Package: osmo-trx-ms-blade
|
||||
Architecture: any
|
||||
Depends: ${shlibs:Depends}, ${misc:Depends}
|
||||
Description: MS side transceiver (bladeRF)
|
||||
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
|
||||
physical layer of a BTS comprising the following 3GPP specifications:
|
||||
.
|
||||
TS 05.01 "Physical layer on the radio path"
|
||||
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
|
||||
TS 05.04 "Modulation"
|
||||
TS 05.10 "Radio subsystem synchronization"
|
||||
.
|
||||
In this context, BTS is "Base transceiver station". It's the stations that
|
||||
connect mobile phones to the mobile network.
|
||||
.
|
||||
3GPP is the "3rd Generation Partnership Project" which is the collaboration
|
||||
between different telecommunication associations for developing new
|
||||
generations of mobile phone networks. (post-2G/GSM)
|
||||
|
||||
Package: osmo-trx-doc
|
||||
Architecture: all
|
||||
Section: doc
|
||||
|
||||
1
debian/osmo-trx-ms-blade.install
vendored
Normal file
1
debian/osmo-trx-ms-blade.install
vendored
Normal file
@@ -0,0 +1 @@
|
||||
/usr/bin/osmo-trx-ms-blade
|
||||
11
debian/rules
vendored
11
debian/rules
vendored
@@ -9,7 +9,16 @@ override_dh_shlibdeps:
|
||||
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
|
||||
|
||||
override_dh_auto_configure:
|
||||
dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-ipc --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
|
||||
dh_auto_configure -- \
|
||||
--enable-manuals \
|
||||
--with-systemdsystemunitdir=/lib/systemd/system \
|
||||
--with-bladerf \
|
||||
--with-ipc \
|
||||
--with-lms \
|
||||
--with-mstrx \
|
||||
--with-uhd \
|
||||
--with-usrp1 \
|
||||
$(NULL)
|
||||
|
||||
override_dh_strip:
|
||||
dh_strip --dbg-package=osmo-trx-dbg
|
||||
|
||||
@@ -1,3 +1,16 @@
|
||||
# all config examples must be listed here unconditionally, so that
|
||||
# all of them end up in the release tarball (see OS#6349)
|
||||
EXTRA_DIST = \
|
||||
osmo-trx-uhd/osmo-trx-limesdr.cfg \
|
||||
osmo-trx-uhd/osmo-trx-usrp_b200.cfg \
|
||||
osmo-trx-uhd/osmo-trx-uhd.cfg \
|
||||
osmo-trx-uhd/osmo-trx-umtrx.cfg \
|
||||
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 =
|
||||
osmoconfdir = $(sysconfdir)/osmocom
|
||||
|
||||
@@ -19,7 +32,6 @@ OSMOCONF_FILES += osmo-trx-ipc/osmo-trx-ipc.cfg
|
||||
endif
|
||||
|
||||
osmoconf_DATA = $(OSMOCONF_FILES)
|
||||
EXTRA_DIST = $(OSMOCONF_FILES)
|
||||
|
||||
CFG_FILES = find $(srcdir) -type f -name '*.cfg*' | sed -e 's,^$(srcdir),,'
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 1
|
||||
logging print file basename
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
logging level set-all notice
|
||||
!
|
||||
line vty
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 1
|
||||
logging print file basename
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
logging level set-all notice
|
||||
!
|
||||
line vty
|
||||
|
||||
23
doc/examples/osmo-trx-ms-blade/mssdr.cfg
Normal file
23
doc/examples/osmo-trx-ms-blade/mssdr.cfg
Normal file
@@ -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
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 1
|
||||
logging print file basename
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
logging level set-all notice
|
||||
!
|
||||
line vty
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 1
|
||||
logging print file basename
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
logging level set-all notice
|
||||
!
|
||||
line vty
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
log stderr
|
||||
logging filter all 1
|
||||
logging color 1
|
||||
logging print category-hex 0
|
||||
logging print category 1
|
||||
logging timestamp 1
|
||||
logging print file basename
|
||||
logging timestamp 0
|
||||
logging print file basename last
|
||||
logging print level 1
|
||||
logging level set-all notice
|
||||
!
|
||||
line vty
|
||||
|
||||
Submodule osmocom-bb updated: 05ddc05233...f12b17dffb
@@ -1,6 +1,8 @@
|
||||
AM_CPPFLAGS = $(LIBOSMOCODING_CFLAGS)
|
||||
AM_CFLAGS = -Wall
|
||||
|
||||
DIST_SUBDIRS = va-test
|
||||
|
||||
EXTRA_DIST = clockdump.sh matlab
|
||||
|
||||
noinst_PROGRAMS = osmo-prbs-tool
|
||||
|
||||
17
utils/va-test/Makefile.am
Normal file
17
utils/va-test/Makefile.am
Normal file
@@ -0,0 +1,17 @@
|
||||
include $(top_srcdir)/Makefile.common
|
||||
|
||||
noinst_PROGRAMS = osmo-burst-gen
|
||||
|
||||
osmo_burst_gen_SOURCES = burst-gen.cpp \
|
||||
${top_srcdir}/Transceiver52M/grgsm_vitac/grgsm_vitac.cpp \
|
||||
${top_srcdir}/Transceiver52M/grgsm_vitac/viterbi_detector.cc
|
||||
osmo_burst_gen_LDADD = \
|
||||
${top_srcdir}/Transceiver52M/libtransceiver_common.la \
|
||||
$(ARCH_LA) \
|
||||
$(GSM_LA) \
|
||||
$(COMMON_LA)
|
||||
osmo_burst_gen_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) \
|
||||
-I${top_srcdir}/Transceiver52M/arch/common \
|
||||
-I${top_srcdir}/Transceiver52M/device/common \
|
||||
-I${top_srcdir}/Transceiver52M
|
||||
|
||||
531
utils/va-test/burst-gen.cpp
Normal file
531
utils/va-test/burst-gen.cpp
Normal file
@@ -0,0 +1,531 @@
|
||||
/*
|
||||
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||
* All Rights Reserved
|
||||
*
|
||||
* Author: Eric Wild <ewild@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/>.
|
||||
*
|
||||
*/
|
||||
|
||||
// this allows messing with the demod to check the detecton offset impact,
|
||||
// not intended for actual automated tests.
|
||||
|
||||
#include "sigProcLib.h"
|
||||
|
||||
extern "C" {
|
||||
#include "convert.h"
|
||||
#include <convolve.h>
|
||||
}
|
||||
|
||||
#define _CRT_SECURE_NO_WARNINGS
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
#include <iomanip>
|
||||
#include <numeric>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <grgsm_vitac/grgsm_vitac.h>
|
||||
|
||||
#define DO_RACH
|
||||
|
||||
const int SAMPLE_SCALE_FACTOR = 1;
|
||||
|
||||
template <typename DST_T, typename SRC_T, typename ST>
|
||||
void convert_and_scale(void *dst, void *src, unsigned int src_len, ST scale)
|
||||
{
|
||||
for (unsigned int i = 0; i < src_len; i++)
|
||||
reinterpret_cast<DST_T *>(dst)[i] = static_cast<DST_T>((reinterpret_cast<SRC_T *>(src)[i]) * scale);
|
||||
}
|
||||
template <typename DST_T, typename SRC_T>
|
||||
void convert_and_scale_default(void *dst, void *src, unsigned int src_len)
|
||||
{
|
||||
return convert_and_scale<DST_T, SRC_T>(dst, src, src_len, SAMPLE_SCALE_FACTOR);
|
||||
}
|
||||
|
||||
static const unsigned int txFullScale = (float)(1 << 14) - 1;
|
||||
// static const unsigned int rxFullScale = (float)(1 << 14) - 1;
|
||||
|
||||
static const BitVector
|
||||
gRACHBurstx("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
|
||||
|
||||
static const BitVector gTrainingSequencex[] = {
|
||||
BitVector("00100101110000100010010111"), BitVector("00101101110111100010110111"),
|
||||
BitVector("01000011101110100100001110"), BitVector("01000111101101000100011110"),
|
||||
BitVector("00011010111001000001101011"), BitVector("01001110101100000100111010"),
|
||||
BitVector("10100111110110001010011111"), BitVector("11101111000100101110111100"),
|
||||
};
|
||||
|
||||
struct mrv {
|
||||
std::vector<char> bits;
|
||||
signalVector *rvbuf;
|
||||
std::unique_ptr<std::vector<std::complex<float>>> convolved;
|
||||
// mrv(): bits(), demod_bits() {}
|
||||
CorrType ct;
|
||||
};
|
||||
|
||||
static mrv genRandNormalBurstx(int tsc, int sps, int tn)
|
||||
{
|
||||
mrv retstruct;
|
||||
int i = 0;
|
||||
BitVector bits(148);
|
||||
|
||||
/* Tail bits */
|
||||
for (; i < 3; i++)
|
||||
bits[i] = 0;
|
||||
|
||||
/* Random bits */
|
||||
for (int j = 0; i < 60; i++, j++)
|
||||
bits[i] = rand() % 2;
|
||||
|
||||
/* Stealing bit */
|
||||
bits[i++] = 0;
|
||||
|
||||
/* Training sequence */
|
||||
for (int n = 0; i < 87; i++, n++)
|
||||
bits[i] = gTrainingSequencex[tsc][n];
|
||||
|
||||
/* Stealing bit */
|
||||
bits[i++] = 0;
|
||||
|
||||
/* Random bits */
|
||||
for (; i < 145; i++)
|
||||
bits[i] = rand() % 2;
|
||||
|
||||
/* Tail bits */
|
||||
for (; i < 148; i++)
|
||||
bits[i] = 0;
|
||||
|
||||
int guard = 8 + !(tn % 4);
|
||||
auto r = modulateBurst(bits, guard, sps);
|
||||
|
||||
retstruct.rvbuf = r;
|
||||
for (size_t i = 0; i < bits.size(); i++)
|
||||
retstruct.bits.push_back(bits.bit(i) ? 1 : 0);
|
||||
return retstruct;
|
||||
}
|
||||
|
||||
static mrv genRandAccessBurstx(int delay, int sps, int tn)
|
||||
{
|
||||
mrv retstruct;
|
||||
int i = 0;
|
||||
BitVector bits(88 + delay);
|
||||
|
||||
/* delay */
|
||||
for (; i < delay; i++)
|
||||
bits[i] = 0;
|
||||
|
||||
/* head and synch bits */
|
||||
for (int n = 0; i < 49 + delay; i++, n++)
|
||||
bits[i] = gRACHBurstx[n];
|
||||
|
||||
/* Random bits */
|
||||
for (int j = 0; i < 85 + delay; i++, j++)
|
||||
bits[i] = rand() % 2;
|
||||
|
||||
for (; i < 88 + delay; i++)
|
||||
bits[i] = 0;
|
||||
|
||||
int guard = 68 - delay + !(tn % 4);
|
||||
auto r = modulateBurst(bits, guard, sps);
|
||||
|
||||
retstruct.rvbuf = r;
|
||||
for (size_t i = 0; i < bits.size(); i++)
|
||||
retstruct.bits.push_back(bits.bit(i) ? 1 : 0);
|
||||
return retstruct;
|
||||
}
|
||||
|
||||
extern gr_complex d_acc_training_seq[N_ACCESS_BITS]; ///<encoded training sequence of a SCH burst
|
||||
extern gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a SCH burst
|
||||
extern gr_complex d_norm_training_seq[TRAIN_SEQ_NUM]
|
||||
[N_TRAIN_BITS]; ///<encoded training sequences of a normal and dummy burst
|
||||
|
||||
void sv_write_helper(signalVector *burst, std::string fname)
|
||||
{
|
||||
auto start = burst->begin();
|
||||
auto n = burst->bytes();
|
||||
char *data = reinterpret_cast<char *>(start);
|
||||
|
||||
const int len_in_real = burst->size() * 2;
|
||||
auto cvrtbuf_tx_a = new int16_t[len_in_real];
|
||||
convert_float_short(cvrtbuf_tx_a, (float *)burst->begin(), float(txFullScale), len_in_real);
|
||||
|
||||
std::ofstream fout;
|
||||
fout.open(fname + ".cfile", std::ios::binary | std::ios::out);
|
||||
fout.write(data, n);
|
||||
fout.close();
|
||||
|
||||
fout.open(fname + ".cs16", std::ios::binary | std::ios::out);
|
||||
fout.write((char *)cvrtbuf_tx_a, len_in_real * sizeof(uint16_t));
|
||||
fout.close();
|
||||
delete[] cvrtbuf_tx_a;
|
||||
}
|
||||
|
||||
// borrowed from a real world burst..
|
||||
static std::vector<std::complex<float>> chan_im_resp = {
|
||||
{ 4.1588e-05 + -0.000361925 }, { 0.000112728 + -0.000289796 }, { 0.000162952 + -0.000169028 },
|
||||
{ 0.000174185 + -2.54575e-05 }, { 0.000142947 + 0.000105992 }, { 8.65919e-05 + 0.000187041 },
|
||||
{ 4.15799e-05 + 0.000184346 }, { 5.30207e-05 + 7.84921e-05 }, { 0.000158877 + -0.000128058 },
|
||||
{ 0.000373956 + -0.000407954 }, { 0.000680606 + -0.000712065 }, { 0.00102929 + -0.000979604 },
|
||||
{ 0.00135049 + -0.00115333 }, { 0.00157434 + -0.0011948 }, { 0.00165098 + -0.00109534 },
|
||||
{ 0.00156519 + -0.000878794 }, { 0.0013399 + -0.000594285 }, { 0.00102788 + -0.00030189 },
|
||||
{ 0.000694684 + -5.58912e-05 }, { 0.000399328 + 0.000109463 }
|
||||
};
|
||||
|
||||
// as above, downsampled to 1sps + just magnitude
|
||||
static std::vector<float> chan_im_resp_trunc = { 1., 0.20513351, 0.10020305, 0.11490235 };
|
||||
|
||||
template <typename A, typename B>
|
||||
auto conv(const std::vector<A> &a, const std::vector<B> &b) -> std::unique_ptr<std::vector<A>>
|
||||
{
|
||||
int data_len = a.size();
|
||||
int conv_len = b.size();
|
||||
int conv_size = conv_len + data_len - 1;
|
||||
auto retv = std::make_unique<std::vector<A>>(conv_size);
|
||||
|
||||
for (int i = 0; i < data_len; ++i) {
|
||||
for (int j = 0; j < conv_len; ++j) {
|
||||
(*retv)[i + j] += a[i] * b[j];
|
||||
}
|
||||
}
|
||||
return retv;
|
||||
}
|
||||
|
||||
template <typename A>
|
||||
static auto conv(const A *a, int len, std::vector<float> &b)
|
||||
{
|
||||
std::vector<A> aa(len);
|
||||
std::copy_n(a, len, aa.begin());
|
||||
std::reverse(b.begin(), b.end());
|
||||
return conv(aa, b);
|
||||
}
|
||||
template <typename A>
|
||||
static auto conv(const A *a, int len, std::vector<A> &b)
|
||||
{
|
||||
std::vector<A> aa(len);
|
||||
std::copy_n(a, len, aa.begin());
|
||||
std::reverse(b.begin(), b.end());
|
||||
return conv(aa, b);
|
||||
}
|
||||
|
||||
// signalvector is owning despite claiming not to, but we can pretend, too..
|
||||
static void dummy_free(void *wData){};
|
||||
static void *dummy_alloc(size_t newSize)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
size_t read_from_file(std::string path, std::vector<T> &outvec)
|
||||
{
|
||||
std::ifstream infile;
|
||||
infile.open(path, std::ios::in | std::ios::binary);
|
||||
if (infile.fail()) {
|
||||
std::cout << " not found: " << path << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
infile.seekg(0, std::ios_base::end);
|
||||
size_t fsize = infile.tellg();
|
||||
auto fsize_in_T = fsize / sizeof(T);
|
||||
infile.seekg(0, std::ios_base::beg);
|
||||
|
||||
outvec.resize(fsize_in_T);
|
||||
infile.read(reinterpret_cast<char *>(&outvec[0]), fsize);
|
||||
infile.close();
|
||||
std::cout << "Read " << fsize << " from " << path << std::endl;
|
||||
return fsize;
|
||||
}
|
||||
void demod_real_burst(int num = 0)
|
||||
{
|
||||
auto path = "./nb_chunk_tsc7.cfile";
|
||||
auto bitfile = "./demodbits_tsc7.s8";
|
||||
|
||||
std::vector<std::complex<float>> burstdata;
|
||||
std::vector<char> bitdata;
|
||||
read_from_file(path, burstdata);
|
||||
read_from_file(bitfile, bitdata);
|
||||
|
||||
// print "known good" burst bits
|
||||
std::cerr << "known bits:" << std::endl;
|
||||
std::cerr << std::setw(5) << 0 << " - ";
|
||||
for (auto i : bitdata)
|
||||
std::cout << (i > 0 ? "1" : "0");
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "demod tests sigproclib:" << std::endl;
|
||||
|
||||
auto ct = CorrType::TSC;
|
||||
auto delay = 0;
|
||||
auto tsc = 7;
|
||||
int offset = 0;
|
||||
auto cplx = reinterpret_cast<complex *>(&burstdata[offset]);
|
||||
auto stdcplx = reinterpret_cast<std::complex<float> *>(&burstdata[offset]);
|
||||
signalVector sv(&cplx[0], 0, burstdata.size() - offset, dummy_alloc, dummy_free);
|
||||
|
||||
struct estim_burst_params ebp;
|
||||
auto rc = detectAnyBurst(sv, tsc, BURST_THRESH, 4, ct, 40, &ebp);
|
||||
|
||||
auto rxBurst = std::unique_ptr<SoftVector>(demodAnyBurst(sv, (CorrType)rc, 4, &ebp));
|
||||
// print osmotrx sigproclib demod result
|
||||
std::cerr << std::setw(5) << int(ebp.toa) << " o ";
|
||||
for (ssize_t i = 0 + delay; i < 148 + delay; i++)
|
||||
std::cout << (rxBurst->bit(i) ? "1" : "0");
|
||||
std::cerr << std::endl;
|
||||
|
||||
std::cerr << "demod test va:" << std::endl;
|
||||
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
|
||||
float ncmax;
|
||||
char demodded_softbits[444];
|
||||
|
||||
// demod at known offset
|
||||
{
|
||||
auto inp = &stdcplx[29]; // known offset
|
||||
auto normal_burst_startX = get_norm_chan_imp_resp(inp, &chan_imp_resp[0], &ncmax, tsc);
|
||||
detect_burst_nb(inp, &chan_imp_resp[0], normal_burst_startX, demodded_softbits);
|
||||
|
||||
std::cerr << std::setw(5) << normal_burst_startX << " v ";
|
||||
for (size_t i = 0; i < 148; i++)
|
||||
std::cerr << (demodded_softbits[i] < 0 ? "1" : "0");
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
{
|
||||
std::cerr << "-- va start offset loop --" << std::endl;
|
||||
std::cerr << "offset/det offset/#errors/known^demod bits" << std::endl;
|
||||
for (int i = 0; i < 34; i++) {
|
||||
auto inp = &stdcplx[i];
|
||||
auto conved_beg = inp;
|
||||
|
||||
auto me = get_norm_chan_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, tsc);
|
||||
detect_burst_nb(conved_beg, &chan_imp_resp[0], me, demodded_softbits);
|
||||
auto bitdiffarr = std::make_unique<char[]>(148);
|
||||
for (size_t i = 0; i < 148; i++)
|
||||
bitdiffarr.get()[i] = (demodded_softbits[i] < 0 ? 1 : 0) ^ (bitdata[i] > 0 ? 1 : 0);
|
||||
auto ber = std::accumulate(bitdiffarr.get(), bitdiffarr.get() + 148, 0);
|
||||
|
||||
std::cerr << std::setw(3) << i << ": " << std::setw(3) << me << " v " << std::setw(3) << ber
|
||||
<< " ";
|
||||
for (size_t i = 0; i < 148; i++)
|
||||
std::cerr << (bitdiffarr[i] ? "1" : "0");
|
||||
std::cerr << std::endl;
|
||||
// std::cerr << std::setw(4) << i << " (" << std::setw(4) << 29 - i << "):" << std::setw(4) << org
|
||||
// << " " << std::setw(4) << me << " y " << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto gen_burst(CorrType t, int delay, int tsc)
|
||||
{
|
||||
mrv rs;
|
||||
if (t == CorrType::RACH) {
|
||||
rs = genRandAccessBurstx(delay, 4, tsc);
|
||||
|
||||
} else if (t == CorrType::TSC) {
|
||||
rs = genRandNormalBurstx(tsc, 4, 0);
|
||||
} else {
|
||||
std::cerr << "wtf?" << std::endl;
|
||||
exit(0);
|
||||
}
|
||||
rs.ct = t;
|
||||
|
||||
signalVector *burst = rs.rvbuf;
|
||||
// sv_write_helper(burst, std::to_string(num));
|
||||
// scaleVector(*burst, {1, 0});
|
||||
const int len_in_real = burst->size() * 2;
|
||||
auto cvrtbuf_tx_a = std::make_unique<short[]>(len_in_real);
|
||||
auto cvrtbuf_rx_a = std::make_unique<float[]>(len_in_real);
|
||||
auto rx_cfloat = reinterpret_cast<std::complex<float> *>(&cvrtbuf_rx_a[0]);
|
||||
|
||||
convert_float_short(cvrtbuf_tx_a.get(), (float *)burst->begin(), float(txFullScale), len_in_real);
|
||||
convert_short_float(cvrtbuf_rx_a.get(), cvrtbuf_tx_a.get(), len_in_real);
|
||||
for (int i = 0; i < len_in_real; i++) // scale properly!
|
||||
cvrtbuf_rx_a[i] *= 1. / txFullScale;
|
||||
auto conved = conv(rx_cfloat, burst->size(), chan_im_resp);
|
||||
|
||||
std::cerr << "-- generated " << (t == CorrType::RACH ? "RACH" : "TSC") << " burst --" << std::endl;
|
||||
for (size_t i = 0; i < rs.bits.size(); i++)
|
||||
std::cerr << (rs.bits[i] ? "1" : "0");
|
||||
std::cerr << std::endl;
|
||||
delete burst;
|
||||
rs.convolved = std::move(conved);
|
||||
return rs;
|
||||
}
|
||||
|
||||
void demod_generated_burst(CorrType t)
|
||||
{
|
||||
int tsc = 0;
|
||||
int delay = 0;
|
||||
auto rs = gen_burst(t, delay, tsc);
|
||||
auto conved_beg = &(*rs.convolved)[0];
|
||||
|
||||
if (rs.ct == CorrType::RACH) {
|
||||
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
|
||||
float ncmax;
|
||||
char demodded_softbits[444];
|
||||
int normal_burst_start = 0;
|
||||
normal_burst_start = get_access_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, 0);
|
||||
normal_burst_start = std::max(normal_burst_start, 0);
|
||||
for (int j = 0; j < 4; j++) {
|
||||
for (int start_val = 0; start_val < 16; start_val++) {
|
||||
auto bitdiffarr = std::make_unique<char[]>(rs.bits.size());
|
||||
detect_burst_ab(conved_beg, &chan_imp_resp[0], normal_burst_start + j,
|
||||
demodded_softbits, start_val);
|
||||
|
||||
for (size_t i = 0; i < rs.bits.size(); i++)
|
||||
bitdiffarr.get()[i] = (demodded_softbits[i] < 0 ? 1 : 0) ^ rs.bits[i];
|
||||
auto ber = std::accumulate(bitdiffarr.get(), bitdiffarr.get() + rs.bits.size(), 0);
|
||||
|
||||
std::cerr << "ber " << std::setw(4) << ber << " bo:" << std::setw(4) << j
|
||||
<< " vas:" << std::setw(4) << start_val << " ";
|
||||
// for (size_t i = 0; i < rs.num_bits; i++)
|
||||
// std::cerr << (demodded_softbits[i] < 0 ? "1" : "0");
|
||||
// std::cerr << std::endl;
|
||||
// std::cerr << "d " << std::setw(4) << ber << " ";
|
||||
for (size_t i = 0; i < rs.bits.size(); i++)
|
||||
std::cerr << (bitdiffarr.get()[i] ? "1" : "0");
|
||||
std::cerr << std::endl;
|
||||
|
||||
// std::cerr << "v " << std::setw(4) << j << std::setw(4) << start_val << " ";
|
||||
// for (size_t i = 0; i < rs.num_bits; i++)
|
||||
// std::cerr << (demodded_softbits[i] < 0 ? "1" : "0");
|
||||
// std::cerr << std::endl;
|
||||
// std::cerr << "d " << std::setw(4) << ber << " ";
|
||||
// for (size_t i = 0; i < rs.num_bits; i++)
|
||||
// std::cerr << (ptr.get()[i] ? "1" : "0");
|
||||
// std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
} else {
|
||||
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
|
||||
float ncmax;
|
||||
char demodded_softbits[444];
|
||||
|
||||
auto normal_burst_start = get_norm_chan_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, tsc);
|
||||
detect_burst_nb(conved_beg, &chan_imp_resp[0], normal_burst_start + 0, demodded_softbits);
|
||||
std::cerr << "toa " << std::setprecision(2) << normal_burst_start << std::endl;
|
||||
|
||||
std::cerr << "vita ";
|
||||
for (size_t i = 0; i < rs.bits.size(); i++)
|
||||
std::cerr << (demodded_softbits[i] < 0 ? "1" : "0");
|
||||
std::cerr << std::endl;
|
||||
std::cerr << "diff ";
|
||||
for (size_t i = 0; i < rs.bits.size(); i++)
|
||||
std::cerr << ((demodded_softbits[i] < 0 ? 1 : 0) ^ rs.bits[i] ? "1" : "0");
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
struct estim_burst_params ebp;
|
||||
char demodded_softbits[444];
|
||||
complex *rx_sigproc_cfloat = reinterpret_cast<complex *>(conved_beg);
|
||||
signalVector sv(rx_sigproc_cfloat, 0, rs.convolved->size(), dummy_alloc, dummy_free);
|
||||
|
||||
auto rc = detectAnyBurst(sv, tsc, BURST_THRESH, 4, rs.ct, 40, &ebp);
|
||||
auto rxBurst = std::unique_ptr<SoftVector>(demodAnyBurst(sv, (CorrType)rc, 4, &ebp));
|
||||
|
||||
std::cerr << "toa " << std::setprecision(2) << ebp.toa << std::endl;
|
||||
|
||||
for (ssize_t i = 0; i < delay; i++) // maybe pad rach op?
|
||||
demodded_softbits[i] = 0;
|
||||
for (size_t i = 0 + delay; i < rs.bits.size() + delay; i++)
|
||||
demodded_softbits[i] = (rxBurst->bit(i) ? 1 : 0);
|
||||
|
||||
std::cerr << "sigp ";
|
||||
for (size_t i = 0; i < rs.bits.size(); i++)
|
||||
std::cerr << (demodded_softbits[i] ? "1" : "0");
|
||||
std::cerr << std::endl;
|
||||
|
||||
std::cerr << "diff ";
|
||||
for (size_t i = 0; i < rs.bits.size(); i++)
|
||||
std::cerr << (demodded_softbits[i] ^ rs.bits[i] ? "1" : "0");
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
|
||||
void demod_test_offsets()
|
||||
{
|
||||
const int tsc = 0;
|
||||
const int delaybuffer_realoffset = 100;
|
||||
|
||||
{
|
||||
auto rs = gen_burst(CorrType::RACH, 0, tsc);
|
||||
typeof(*rs.convolved) delay_buffer(rs.convolved->size() * 2); // plenty of space..
|
||||
|
||||
for (int delay = -10; delay < 60; delay++) {
|
||||
std::fill(delay_buffer.begin(), delay_buffer.end(), 0);
|
||||
std::copy(rs.convolved->begin(), rs.convolved->end(),
|
||||
delay_buffer.begin() + delaybuffer_realoffset + delay);
|
||||
|
||||
auto conved_beg = &delay_buffer[delaybuffer_realoffset];
|
||||
|
||||
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
|
||||
float ncmax;
|
||||
auto va_burst_start = get_access_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, 60);
|
||||
|
||||
complex *rx_sigproc_cfloat = reinterpret_cast<complex *>(conved_beg);
|
||||
struct estim_burst_params ebp;
|
||||
signalVector sv(rx_sigproc_cfloat, 0, rs.convolved->size(), dummy_alloc, dummy_free);
|
||||
detectAnyBurst(sv, tsc, BURST_THRESH, 4, rs.ct, 60, &ebp);
|
||||
std::cerr << "delay:" << std::setw(3) << std::setprecision(2) << delay;
|
||||
std::cerr << " va: " << std::setw(3) << std::setprecision(2) << va_burst_start;
|
||||
std::cerr << " sg: " << std::setw(3) << std::setprecision(2) << ebp.toa;
|
||||
std::cerr << " d: " << std::setw(3) << std::setprecision(2) << (ebp.toa * 4) - va_burst_start;
|
||||
std::cerr << " ! " << float(va_burst_start + 13) / 4 << std::endl;
|
||||
}
|
||||
}
|
||||
{
|
||||
auto rs = gen_burst(CorrType::TSC, 0, tsc);
|
||||
typeof(*rs.convolved) delay_buffer(rs.convolved->size() * 2); // plenty of space..
|
||||
|
||||
for (int delay = -10; delay < 10; delay++) {
|
||||
std::fill(delay_buffer.begin(), delay_buffer.end(), 0);
|
||||
std::copy(rs.convolved->begin(), rs.convolved->end(),
|
||||
delay_buffer.begin() + delaybuffer_realoffset + delay);
|
||||
|
||||
auto conved_beg = &delay_buffer[delaybuffer_realoffset];
|
||||
|
||||
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
|
||||
float ncmax;
|
||||
auto va_burst_start = get_norm_chan_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, tsc);
|
||||
|
||||
complex *rx_sigproc_cfloat = reinterpret_cast<complex *>(conved_beg);
|
||||
struct estim_burst_params ebp;
|
||||
signalVector sv(rx_sigproc_cfloat, 0, rs.convolved->size(), dummy_alloc, dummy_free);
|
||||
detectAnyBurst(sv, tsc, BURST_THRESH, 4, rs.ct, 60, &ebp);
|
||||
std::cerr << "delay:" << std::setw(3) << std::setprecision(2) << delay;
|
||||
std::cerr << " va: " << std::setw(3) << std::setprecision(2) << va_burst_start;
|
||||
std::cerr << " sg: " << std::setw(3) << std::setprecision(2) << ebp.toa;
|
||||
std::cerr << " d: " << std::setw(3) << std::setprecision(2) << (ebp.toa * 4) - va_burst_start;
|
||||
std::cerr << " ! " << float(va_burst_start + 19) / 4 << std::endl;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
convolve_init();
|
||||
convert_init();
|
||||
sigProcLibSetup();
|
||||
initvita();
|
||||
|
||||
for (int i = 0; i < 1; i++) {
|
||||
demod_real_burst(i);
|
||||
demod_generated_burst(CorrType::RACH);
|
||||
demod_generated_burst(CorrType::TSC);
|
||||
demod_test_offsets();
|
||||
}
|
||||
}
|
||||
BIN
utils/va-test/demodbits_tsc7.s8
Normal file
BIN
utils/va-test/demodbits_tsc7.s8
Normal file
Binary file not shown.
BIN
utils/va-test/nb_chunk_tsc7.cfile
Normal file
BIN
utils/va-test/nb_chunk_tsc7.cfile
Normal file
Binary file not shown.
Reference in New Issue
Block a user