Compare commits

..

19 Commits

Author SHA1 Message Date
Eric Wild
3fc39b53cc ms: fix up template deduction failure
clang and gcc disagree, so just use a real var with the right type.

Change-Id: I590060d201445ad25cf5eb73c4b1360eaf6a6cea
2024-03-18 16:16:17 +01:00
Oliver Smith
25590be470 debian: add osmo-trx-ms-blade
Related: OS#6409
Change-Id: I85d24355975f292610bcde61a8917aec852043ca
2024-03-18 13:46:44 +01:00
Oliver Smith
f1ce0e7692 debian/rules: make configure args diff friendly
Have one arg per line, and order it mostly alphabetically while at it
(backends are still together, as recommended in review).

Change-Id: I354affacb38958efe70baedc6175aeab525190a6
2024-03-18 13:44:33 +01:00
Oliver Smith
99e0746f37 gitignore: add .version
Change-Id: Ib75781b5d473921b42936e4d3c441e6a520b9108
2024-03-18 12:34:53 +01:00
Eric
1f8eb7c658 ms: add demod test tool and data
It just uses the viterbi equalizer and the sigproclib to generate and
demodulate bursts and prints the bits, only useful for
development.

Change-Id: I852e34d9667d1f12f235f8b3da1fcc0d738b2db9
2024-02-22 13:41:26 +01:00
Eric Wild
d8a1dee2c9 ms: add sigproclib demod
This is basically a fixed version of ttsous ancient branch that can be
used instead of the VA. Required config option part of a future
patchset.

Change-Id: I6558992bd69f18526be5ebe7d424ca00ceb67772
2024-02-22 12:39:33 +01:00
Eric Wild
56c7b777f3 ms: hard preswapped VA gsm bits
small * 127 can still be small enough to end up 0 after casting which
breaks everything.

Change-Id: I44b95dced64208eebfb5214da034a5d49abdd0df
2024-02-21 19:38:36 +01:00
Eric
989fe75038 ms: get rid of std::thread
2fc2b594da6e329577b195cb2543a8dd9e1b9ed0 changed std::thread to pthread
for proper affinity to circumvent startup issues, so just stick to
pthread instead of mixing std::thread and pthread, which made tracking
thread creation difficult due to different functions.

Change-Id: I0ba2fd958530394b9d99ed82111064d428c5870f
2024-02-21 19:38:30 +01:00
Eric Wild
8aea236c56 ms: do not set the blade tuning mode
Sophisticated users can export BLADERF_DEFAULT_TUNING_MODE=fpga which
reduces the startup time to 1 second, or (default)
BLADERF_DEFAULT_TUNING_MODE=host which always works.

Defaulting to fpga mode has the unfortunate side effect that the blade
can get stuck in a weird invalid mode when supplying wrong parameters
that breaks sample streaming until it is power cycled or "reset" by
using host tuning once. So, let's do the safe thing, and not default to
fpga mode.

Change-Id: I109f925f07a198d1fb33fe793e91e455fea05a96
2024-02-01 12:22:37 +00:00
Vadim Yanitskiy
a2d76f1d2f doc/examples: fix missing config files in release tarballs
All config file examples must be listed in EXTRA_DIST unconditionally.
Adding them conditionally results in incomplete release tarballs,
containing only some '*.cfg' files and failing to build.

Change-Id: Iffb6d7577de175fc5d14642f0af6852508d74e69
Related: OS#6349
2024-01-28 23:46:26 +07:00
Vadim Yanitskiy
a118d98ec3 build: include version files into the release tarball
Change-Id: I818b645737bed35495cb4bad35667b369fd5ea6c
2024-01-26 23:45:28 +07:00
Vadim Yanitskiy
c7fc94dff6 Transceiver::ctrl_sock_handle_rx(): fix copy-pasted comments
Change-Id: If31c0de0b3562c06220dcb9ad6f0c13d8621f6e3
2024-01-23 04:12:20 +07:00
Andreas Eversberg
06c0810e98 Use uniform log format for default config files
Related: OS#6272
Change-Id: If9466e925a405d35deed2e81f25a0a1677de0f3c
2023-12-01 11:54:01 +01:00
Harald Welte
6ee9dccddb osmo-trx-uhd: Make sure HOME environment variable is set
It turns out that uhd versions >= 4.0.0.0 *require* that either the
HOME or the XDG_CONFIG_HOME variables are set, and otherwise will
terminate the program.

Change-Id: I1816013c507da28719590f063da0a397da656a10
Closes: OS#6269
2023-11-24 10:38:34 +01:00
Eric
242ceb25d1 devices: fix wrong gain to power mapping
The dev type was set too early, but the actual dev is only being
discovered during open, so update it. This broke the gain to power
mapping by defaulting to a wrong device.

Change-Id: I1dda6023ca6f15bc063c3dfbc704db2410ff7c98
2023-11-09 09:51:07 +00:00
Eric
b52650f157 ms: init blade with fpga control
Blade 1 defaults to fpga tuning, but the blade 2 code defaults to host,
which does 8000 register reads and writes. The only way to speed this up
is to set the env var, which reduces opening the blade device from 10 to
1 seconds.

Change-Id: I32fe31f1e11f4ceb3c864ec8739d177e780d0a7e
2023-11-03 20:37:51 +01:00
Eric
992a49e586 ms: reduce rx burst queue size
This should be fine, because we can at most receive 1.5 bursts of data
at once and produce 2 bursts with previous data, so if this is insufficent the usb buffers are late or the upper layer is stuck and we're in trouble anyway.

Change-Id: Ifb8bf2894c87e4234e3d3f65d66c1e98c8f63c53
2023-10-04 12:53:18 +00:00
Vadim Yanitskiy
ad9b8b4211 osmo-trx-ms: bump osmocom-bb submodule commit
The new revision contains an important fix [1] for GPRS scheduling.

Change-Id: Ibb57b29bb0424a40836819c15d25d1133f554d32
Related: [1] osmocom-bb.git I439615639b8e840b9fd4f3af6934d9f298f32216
Related: OS#5500
2023-10-03 23:57:24 +07:00
Eric
0f4381d480 ms: adjust ts advance
..and fix the delay warning.

I'd rather have a proper fn advance of 1, but that breaks gprs, but just
slightly increasing the ts number is sufficient to fix issues with late
tx bursts that then get silently dropped by the sdr.

The mobile app does not care, and will happily work even with fn+3.

Change-Id: I46b3ea6b0094026bd50709739df464438f9e54c4
2023-09-20 15:19:13 +02:00
38 changed files with 1276 additions and 180 deletions

View File

@@ -1,4 +1,5 @@
--exclude osmocom-bb/.*
--exclude .*h
--exclude Transceiver52M/grgsm_vitac/.*
--exclude utils/va-test/.*
--ignore FUNCTION_WITHOUT_ARGS

2
.gitignore vendored
View File

@@ -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/*

View File

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

View File

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

View File

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

View File

@@ -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
@@ -101,6 +102,7 @@ MS_UPPER_SRC = \
noinst_HEADERS += \
ms/ms.h \
ms/threadsched.h \
ms/bladerf_specific.h \
ms/uhd_specific.h \
ms/ms_upper.h \

View File

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

View File

@@ -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", &params)) {
//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", &params)) {
//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", &params)) {
//set expected maximum time-of-arrival
int newGain;
sscanf(params, "%d", &newGain);
newGain = mRadioInterface->setRxGain(newGain, chan);

View File

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

View File

@@ -254,6 +254,7 @@ struct blade_hw {
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 +269,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);
@@ -427,10 +429,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 +444,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;

View File

@@ -35,6 +35,8 @@ extern "C" {
#include "sch.h"
}
#include "threadsched.h"
dummylog ms_trx::dummy_log;
#ifdef DBGXX
@@ -83,13 +85,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 +105,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 +130,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;

View File

@@ -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 };
@@ -217,43 +217,23 @@ 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]{
{
{ "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 },
},
};
using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>;
struct ms_trx : public BASET {
// used to globally initialize the sched/hw information
struct sched_hw_info {
int hw_cpus;
sched_params::target hw_target;
sched_hw_info()
{
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;
}
};
struct ms_trx : public BASET, public sched_hw_info {
using base = BASET;
static dummylog dummy_log;
unsigned int mTSC;
@@ -261,8 +241,8 @@ struct ms_trx : public BASET {
int timing_advance;
bool do_auto_gain;
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 +258,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;
@@ -302,12 +280,8 @@ struct ms_trx : public BASET {
: mTSC(0), mBSIC(0), timing_advance(0), do_auto_gain(false), 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 +298,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;
}
};

View File

@@ -19,6 +19,8 @@
*
*/
#include "sigProcLib.h"
#include "signalVector.h"
#include <atomic>
#include <cassert>
#include <complex>
@@ -155,12 +157,12 @@ 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;
memset((void *)&sch_acq_buffer[0], 0, sizeof(sch_acq_buffer));
#if 1
const auto which_out_buffer = is_first_sch_acq ? sch_acq_buffer : &sch_acq_buffer[40 * 2];
const auto ss = reinterpret_cast<std::complex<float> *>(which_out_buffer);
std::complex<float> channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
int start;
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;
@@ -173,9 +175,22 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
detect_burst_nb(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
auto sch_decode_success = decode_sch(sch_demod_bits, is_first_sch_acq);
#if 0
auto 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 = 0;
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;
@@ -190,6 +205,97 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
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;
}
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);
// float_to_sbit(&bits->begin()[106], &data[39], 62, 39);
if (decode_sch((char *)sch_demod_bits, is_first_sch_acq)) {
auto current_gsm_time_updated = timekeeper.gsmtime();
if (is_first_sch_acq) {
// update ts to first sample in sch buffer, to allow delay calc for current ts
first_sch_ts_start = first_sch_buf_rcv_ts + howmuchdelay - (ts_offset_symb * 4);
} else {
// continuous sch tracking, only update if off too much
auto diff = [](float x, float y) { return x > y ? x - y : y - x; };
auto d = diff(ebp.toa, ts_offset_symb);
if (abs(d) > 0.3) {
if (ebp.toa < ts_offset_symb)
ebp.toa = d;
else
ebp.toa = -d;
temp_ts_corr_offset += ebp.toa * 4;
DBGLG() << "offs: " << ebp.toa << " " << temp_ts_corr_offset << std::endl;
}
}
auto a = gsm_sch_check_fn(current_gsm_time_updated.FN() - 1);
auto b = gsm_sch_check_fn(current_gsm_time_updated.FN());
auto c = gsm_sch_check_fn(current_gsm_time_updated.FN() + 1);
DBGLG() << "L SCH : Timing offset " << rv << " " << ebp.toa << " " << a << b << c << "fn "
<< current_gsm_time_updated.FN() << ":" << current_gsm_time_updated.TN() << std::endl;
delete bits;
return true;
} else {
DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << ebp.toa << " " << current_gsm_time.FN()
<< ":" << current_gsm_time.TN() << std::endl;
}
delete bits;
#endif
return false;
}

View File

@@ -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;
@@ -198,6 +199,7 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
return true;
}
#if 1
convert_and_scale(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
pow = energyDetect(sv, 20 * 4 /*sps*/);
@@ -231,6 +233,42 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
// 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;
#endif
RSSI = (int)floor(20.0 * log10(rxFullScale / avg));
// FIXME: properly handle offset, sch/nb alignment diff? handled by lower anyway...
timingOffset = (int)round(0);
@@ -258,6 +296,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())
};
@@ -456,7 +495,7 @@ int main(int argc, char *argv[])
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;

View File

@@ -20,13 +20,12 @@
*
*/
#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 +33,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 +44,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 +70,7 @@ struct single_thread_pool {
stop_flag = true;
cv.notify_one();
}
worker_thread.join();
pthread_join(worker_thread, nullptr);
}
void thread_loop()

View 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);
}

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

View File

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

View File

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

View File

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

View File

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

View File

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

7
debian/changelog vendored
View File

@@ -1,10 +1,3 @@
osmo-trx (1.6.1) unstable; urgency=medium
[ Eric Wild ]
* devices: fix wrong gain to power mapping
-- Eric Wild <ewild@sysmocom.de> Thu, 09 Nov 2023 14:16:21 +0200
osmo-trx (1.6.0) unstable; urgency=medium
[ Vadim Yanitskiy ]

20
debian/control vendored
View File

@@ -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
View File

@@ -0,0 +1 @@
/usr/bin/osmo-trx-ms-blade

11
debian/rules vendored
View File

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

View File

@@ -1,3 +1,15 @@
# 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 \
$(NULL)
OSMOCONF_FILES =
osmoconfdir = $(sysconfdir)/osmocom
@@ -19,7 +31,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),,'

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

Binary file not shown.

Binary file not shown.