Compare commits

...

32 Commits

Author SHA1 Message Date
Oliver Smith 553d6b0b51 contrib/systemd: run as osmocom user
I have verified that with AmbientCapabilities=CAP_SYS_NICE, setting
scheduling policy as described in the manual still works as expected.

Related: OS#4107
Change-Id: I3e0e5978992dac0a0c76c6f0e859576aa3eb72eb
2024-05-15 11:24:58 +02:00
Oliver Smith a7f5826351 contrib: remove rpm spec file
Related: https://osmocom.org/news/255
Related: OS#6446
Change-Id: I0cc8d753b26648efa7c9fb5798f7c4227c328547
2024-05-08 14:41:10 +02:00
Eric Wild 75cf9254a3 transceiver: use log level cache
This uses the newly added log level caching in libosmocore, which
prevents lock contention during the massive amount of usually disabled
but concurrent (threads) log statements.

Depends: libosmocore.git I539872fc9e3c50b407e6bc388f1e091fa2c826c3
Change-Id: Ic9f0e81c89827f2696ce046a3396f041703d6ae5
2024-05-07 08:04:25 +00:00
Pau Espin c5f623f966 doc: Introduce documentation for osmo-trx-ipc and its IPC interface
Related: SYS#6861
Change-Id: Id6863731f9398720030b16efaaf559e05f2444ed
2024-03-27 13:35:39 +01:00
Pau Espin 8fd51cb7c9 code-architecture.adoc: Fix missing alignment in digraph
Change-Id: I9077590f6786833c9a86c3e844fc2dd07b404d58
2024-03-26 18:14:55 +01:00
Harald Welte e6dc78e5c5 README.md: Remove stray apostrophe
Change-Id: I236a3aa66824759bf23b4331a22fa1d9ec25c5bf
2024-03-23 12:31:16 +01:00
Harald Welte 9982b33597 Add funding link to github mirror
see https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/displaying-a-sponsor-button-in-your-repository

Change-Id: If81f350017e51733cf13934e8481095eafe44a4f
2024-03-23 12:30:10 +01:00
Harald Welte 52ef221657 README.md: Add Forum and Issue Tracker links
Change-Id: Ib85f4e82d314cbab1c844497fb82dbda230589a5
2024-03-23 12:29:40 +01:00
Harald Welte ec13193cae README.md: Improve markdown formatting
Change-Id: Ic5764aab323db87015da8c461dec136e57dd9c8f
2024-03-23 12:29:25 +01:00
Eric Wild 3d8598d460 ms: disabe uhd ms build
This won't build, and has been in a untested non working state for a
year, but might still be useful after fixing it up in the future in case
someone needs it.

Change-Id: I9db1740b5c399a02a41b1d07792c645cf7d1bd1b
2024-03-21 09:55:16 +01:00
Eric Wild 1d7f5a9175 ms: update osmocom-bb submodule
Change-Id: I0140f0a5333469f356c85a95660e10bbdd67cd25
2024-03-18 18:11:44 +01:00
Oliver Smith 511d62733e contrib/jenkins: add --with-bladerf
Related: OS#6409
Depends: docker-playground I3c3fe95d8ebb21f7691a491144885c7deb8049a5
Change-Id: I1300aa107fd1031cf921fafc3566f0f64f9cdd72
2024-03-18 16:18:54 +01:00
Eric Wild 8f19df3bd7 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:18:54 +01:00
Oliver Smith b7cde0f6a2 contrib/jenkins: make configure args diff friendly
Removing new lines in DISTCHECK_CONFIGURE_FLAGS again is needed, as it
otherwise fails with:
  enable-sanitize
  /bin/bash: line 1: enable-sanitize: command not found

Change-Id: I049af384eccdb6f8e5b305ca35de106eeaca3fa8
2024-03-18 16:14:38 +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 Wild 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 Wild 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 Wild 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 Wild 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 Wild 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 Wild 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
58 changed files with 1900 additions and 450 deletions

View File

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

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
open_collective: osmocom

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

@ -4,17 +4,17 @@ About OsmoTRX
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"
* 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*
OsmoTRX is originally based on the transceiver code from the
[OpenBTS](https://osmocom.org/projects/osmobts/wiki/OpenBTS) project, but setup
to operate independently with the purpose of using with non-OpenBTS software and
projects, specifically within the Osmocom stack. Used together with
[OsmoBTS](https://osmocom.org/projects/osmobts/wiki) you can get a pretty
standard GSM BTS with Abis interface as per the relevant 3GPP specifications.
standard GSM/GPRS/EGPRS BTS with Abis interface as per the relevant 3GPP specifications.
Homepage
--------
@ -27,7 +27,7 @@ GIT Repository
You can clone from the official osmo-trx.git repository using
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-trx`
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-trx>
@ -39,6 +39,13 @@ also available online for each of the sub-libraries at User Manual for OsmoTRX
can be generated during the build process, and is also available online at
<https://ftp.osmocom.org/docs/latest/osmotrx-usermanual.pdf>.
Forum
-----
We welcome any osmo-trx related discussions in the
[Cellular Network Infrastructure -> 2 RAN (GERAN)](https://discourse.osmocom.org/c/cni/geran)
section of the osmocom discourse (web based Forum).
Mailing List
------------
@ -50,6 +57,13 @@ Please observe the [Osmocom Mailing List
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
when posting.
Issue Tracker
-------------
We use the [issue tracker of the osmo-trx project on osmocom.org](https://osmocom.org/projects/osmotrx/issues) for
tracking the state of bug reports and feature requests. Feel free to submit any issues you may find, or help
us out by resolving existing issues.
Contributing
------------

View File

@ -0,0 +1 @@
libosmocore > 1.9.2 requires log_cache_enable call

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 \
@ -125,16 +127,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

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

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

View File

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

View File

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

View File

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

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

@ -645,6 +645,7 @@ int main(int argc, char *argv[])
osmo_init_logging2(tall_trx_ctx, &log_info);
log_enable_multithread();
log_cache_enable();
osmo_stats_init(tall_trx_ctx);
vty_init(&g_vty_info);
logging_vty_add_cmds();

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

@ -240,7 +240,6 @@ AS_IF([test "x$with_sse" != "xno"], [
AM_CONDITIONAL(HAVE_SSE4_1, false)
])
dnl Check if the compiler supports specified GCC's built-in function
AC_DEFUN([CHECK_BUILTIN_SUPPORT], [
AC_CACHE_CHECK(
[whether ${CC} has $1 built-in],
@ -374,11 +373,11 @@ AC_CONFIG_FILES([\
tests/CommonLibs/Makefile \
tests/Transceiver52M/Makefile \
utils/Makefile \
utils/va-test/Makefile \
doc/Makefile \
doc/examples/Makefile \
contrib/Makefile \
contrib/systemd/Makefile \
doc/manuals/Makefile \
contrib/osmo-trx.spec \
])
AC_OUTPUT

View File

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

View File

@ -1,254 +0,0 @@
#
# spec file for package osmo-trx
#
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
#
# All modifications and additions to the file contributed by third parties
# remain the property of their copyright owners, unless otherwise agreed
# upon. The license for this file, and modifications and additions to the
# file, is the same license as for the pristine package itself (unless the
# license for the pristine package is not an Open Source License, in which
# case the license is the MIT License). An "Open Source License" is a
# license that conforms to the Open Source Definition (Version 1.9)
# published by the Open Source Initiative.
Name: osmo-trx
Version: @VERSION@
Release: 0
Summary: SDR transceiver that implements Layer 1 of a GSM BTS
License: AGPL-3.0-or-later
Group: Productivity/Telephony/Servers
URL: https://osmocom.org/projects/osmotrx
Source: %{name}-%{version}.tar.xz
BuildRequires: autoconf
BuildRequires: automake
BuildRequires: fdupes
BuildRequires: gcc-c++
BuildRequires: libtool
BuildRequires: pkgconfig >= 0.20
%if 0%{?suse_version}
BuildRequires: systemd-rpm-macros
%endif
%if ! 0%{?centos_ver}
BuildRequires: pkgconfig(LimeSuite)
BuildRequires: pkgconfig(usrp) >= 3.3
%endif
BuildRequires: pkgconfig(fftw3f)
BuildRequires: pkgconfig(libosmocoding) >= 1.9.0
BuildRequires: pkgconfig(libosmocore) >= 1.9.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.9.0
BuildRequires: pkgconfig(libosmovty) >= 1.9.0
BuildRequires: pkgconfig(libusb-1.0)
BuildRequires: pkgconfig(uhd)
%{?systemd_requires}
%if 0%{?suse_version} > 1325
BuildRequires: libboost_program_options-devel
BuildRequires: libboost_system-devel
BuildRequires: libboost_test-devel
BuildRequires: libboost_thread-devel
%else
BuildRequires: boost-devel
%endif
%description
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 uhd
Summary: SDR transceiver that implements Layer 1 of a GSM BTS (UHD)
Group: Productivity/Telephony/Servers
Requires: uhd-firmware
%description uhd
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)
%if ! 0%{?centos_ver}
%package usrp1
Summary: SDR transceiver that implements Layer 1 of a GSM BTS (USRP1)
Group: Productivity/Telephony/Servers
%description usrp1
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 lms
Summary: SDR transceiver that implements Layer 1 of a GSM BTS (LimeSuite)
Group: Productivity/Telephony/Servers
%description lms
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)
%endif
%package ipc
Summary: SDR transceiver that implements Layer 1 of a GSM BTS (IPC)
Group: Productivity/Telephony/Servers
%description ipc
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 ipc-test
Summary: SDR transceiver that implements Layer 1 of a GSM BTS (IPC) driver test utility
Group: Productivity/Telephony/Servers
%description ipc-test
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications:
This package include the test tools for osmo-trx-ipc
%prep
%setup -q
%build
echo "%{version}" >.tarball-version
autoreconf -fi
%if 0%{?centos_ver}
%configure \
--docdir=%{_docdir}/%{name} \
--with-systemdsystemunitdir=%{_unitdir} \
--without-lms \
--with-uhd \
--without-usrp1 \
--with-ipc
%else
%configure \
--docdir=%{_docdir}/%{name} \
--with-systemdsystemunitdir=%{_unitdir} \
--with-lms \
--with-uhd \
--with-usrp1 \
--with-ipc
%endif
make %{?_smp_mflags} V=1
%check
make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%install
%make_install
%fdupes -s %{buildroot}/%{_datadir}
%if 0%{?suse_version}
%pre lms %service_add_pre osmo-trx-lms.service
%post lms %service_add_post osmo-trx-lms.service
%preun lms %service_del_preun osmo-trx-lms.service
%postun lms %service_del_postun osmo-trx-lms.service
%pre uhd %service_add_pre osmo-trx-uhd.service
%post uhd %service_add_post osmo-trx-uhd.service
%preun uhd %service_del_preun osmo-trx-uhd.service
%postun uhd %service_del_postun osmo-trx-uhd.service
%pre usrp1 %service_add_pre osmo-trx-usrp1.service
%post usrp1 %service_add_post osmo-trx-usrp1.service
%preun usrp1 %service_del_preun osmo-trx-usrp1.service
%postun usrp1 %service_del_postun osmo-trx-usrp1.service
%pre ipc %service_add_pre osmo-trx-ipc.service
%post ipc %service_add_post osmo-trx-ipc.service
%preun ipc %service_del_preun osmo-trx-ipc.service
%postun ipc %service_del_postun osmo-trx-ipc.service
%endif
%files
%license COPYING
%doc README.md
%doc %{_docdir}/%{name}/examples
%if ! 0%{?centos_ver}
%files lms
%{_bindir}/osmo-trx-lms
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-trx-lms.cfg
%{_unitdir}/osmo-trx-lms.service
%endif
%files uhd
%{_bindir}/osmo-trx-uhd
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-trx-uhd.cfg
%{_unitdir}/osmo-trx-uhd.service
%if ! 0%{?centos_ver}
%files usrp1
%{_bindir}/osmo-trx-usrp1
%dir %{_datadir}/usrp
%dir %{_datadir}/usrp/rev2
%dir %{_datadir}/usrp/rev4
%{_datadir}/usrp/rev2/std_inband.rbf
%{_datadir}/usrp/rev4/std_inband.rbf
%{_unitdir}/osmo-trx-usrp1.service
%endif
%files ipc
%{_bindir}/osmo-trx-ipc
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-trx-ipc.cfg
%{_unitdir}/osmo-trx-ipc.service
%files ipc-test
%{_bindir}/ipc-driver-test
%changelog

View File

@ -8,8 +8,11 @@ Type=simple
Restart=always
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
User=osmocom
Group=osmocom
ExecStart=/usr/bin/osmo-trx-ipc -C /etc/osmocom/osmo-trx-ipc.cfg
RestartSec=2
AmbientCapabilities=CAP_SYS_NICE
# CPU scheduling policy:
CPUSchedulingPolicy=rr
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):

View File

@ -8,8 +8,11 @@ Type=simple
Restart=always
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
User=osmocom
Group=osmocom
ExecStart=/usr/bin/osmo-trx-lms -C /etc/osmocom/osmo-trx-lms.cfg
RestartSec=2
AmbientCapabilities=CAP_SYS_NICE
# CPU scheduling policy:
CPUSchedulingPolicy=rr
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):

View File

@ -8,8 +8,12 @@ Type=simple
Restart=always
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
Environment=HOME=%h
User=osmocom
Group=osmocom
ExecStart=/usr/bin/osmo-trx-uhd -C /etc/osmocom/osmo-trx-uhd.cfg
RestartSec=2
AmbientCapabilities=CAP_SYS_NICE
# CPU scheduling policy:
CPUSchedulingPolicy=rr
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):

View File

@ -8,8 +8,11 @@ Type=simple
Restart=always
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
User=osmocom
Group=osmocom
ExecStart=/usr/bin/osmo-trx-usrp1 -C /etc/osmocom/osmo-trx-usrp1.cfg
RestartSec=2
AmbientCapabilities=CAP_SYS_NICE
# CPU scheduling policy:
CPUSchedulingPolicy=rr
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):

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

38
debian/osmo-trx-ipc.postinst vendored Executable file
View File

@ -0,0 +1,38 @@
#!/bin/sh -e
case "$1" in
configure)
# Create the osmocom group and user (if it doesn't exist yet)
if ! getent group osmocom >/dev/null; then
groupadd --system osmocom
fi
if ! getent passwd osmocom >/dev/null; then
useradd \
--system \
--gid osmocom \
--home-dir /var/lib/osmocom \
--shell /sbin/nologin \
--comment "Open Source Mobile Communications" \
osmocom
fi
# Fix permissions of previous (root-owned) install (OS#4107)
if dpkg --compare-versions "$2" le "1.13.0"; then
if [ -e /etc/osmocom/osmo-trx-ipc.cfg ]; then
chown -v osmocom:osmocom /etc/osmocom/osmo-trx-ipc.cfg
chmod -v 0660 /etc/osmocom/osmo-trx-ipc.cfg
fi
if [ -d /etc/osmocom ]; then
chown -v root:osmocom /etc/osmocom
chmod -v 2775 /etc/osmocom
fi
mkdir -p /var/lib/osmocom
chown -R -v osmocom:osmocom /var/lib/osmocom
fi
;;
esac
# dh_installdeb(1) will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#

38
debian/osmo-trx-lms.postinst vendored Executable file
View File

@ -0,0 +1,38 @@
#!/bin/sh -e
case "$1" in
configure)
# Create the osmocom group and user (if it doesn't exist yet)
if ! getent group osmocom >/dev/null; then
groupadd --system osmocom
fi
if ! getent passwd osmocom >/dev/null; then
useradd \
--system \
--gid osmocom \
--home-dir /var/lib/osmocom \
--shell /sbin/nologin \
--comment "Open Source Mobile Communications" \
osmocom
fi
# Fix permissions of previous (root-owned) install (OS#4107)
if dpkg --compare-versions "$2" le "1.13.0"; then
if [ -e /etc/osmocom/osmo-trx-lms.cfg ]; then
chown -v osmocom:osmocom /etc/osmocom/osmo-trx-lms.cfg
chmod -v 0660 /etc/osmocom/osmo-trx-lms.cfg
fi
if [ -d /etc/osmocom ]; then
chown -v root:osmocom /etc/osmocom
chmod -v 2775 /etc/osmocom
fi
mkdir -p /var/lib/osmocom
chown -R -v osmocom:osmocom /var/lib/osmocom
fi
;;
esac
# dh_installdeb(1) will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#

1
debian/osmo-trx-ms-blade.install vendored Normal file
View File

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

38
debian/osmo-trx-uhd.postinst vendored Executable file
View File

@ -0,0 +1,38 @@
#!/bin/sh -e
case "$1" in
configure)
# Create the osmocom group and user (if it doesn't exist yet)
if ! getent group osmocom >/dev/null; then
groupadd --system osmocom
fi
if ! getent passwd osmocom >/dev/null; then
useradd \
--system \
--gid osmocom \
--home-dir /var/lib/osmocom \
--shell /sbin/nologin \
--comment "Open Source Mobile Communications" \
osmocom
fi
# Fix permissions of previous (root-owned) install (OS#4107)
if dpkg --compare-versions "$2" le "1.13.0"; then
if [ -e /etc/osmocom/osmo-trx-uhd.cfg ]; then
chown -v osmocom:osmocom /etc/osmocom/osmo-trx-uhd.cfg
chmod -v 0660 /etc/osmocom/osmo-trx-uhd.cfg
fi
if [ -d /etc/osmocom ]; then
chown -v root:osmocom /etc/osmocom
chmod -v 2775 /etc/osmocom
fi
mkdir -p /var/lib/osmocom
chown -R -v osmocom:osmocom /var/lib/osmocom
fi
;;
esac
# dh_installdeb(1) will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#

38
debian/osmo-trx-usrp1.postinst vendored Executable file
View File

@ -0,0 +1,38 @@
#!/bin/sh -e
case "$1" in
configure)
# Create the osmocom group and user (if it doesn't exist yet)
if ! getent group osmocom >/dev/null; then
groupadd --system osmocom
fi
if ! getent passwd osmocom >/dev/null; then
useradd \
--system \
--gid osmocom \
--home-dir /var/lib/osmocom \
--shell /sbin/nologin \
--comment "Open Source Mobile Communications" \
osmocom
fi
# Fix permissions of previous (root-owned) install (OS#4107)
if dpkg --compare-versions "$2" le "1.13.0"; then
if [ -e /etc/osmocom/osmo-trx-usrp1.cfg ]; then
chown -v osmocom:osmocom /etc/osmocom/osmo-trx-usrp1.cfg
chmod -v 0660 /etc/osmocom/osmo-trx-usrp1.cfg
fi
if [ -d /etc/osmocom ]; then
chown -v root:osmocom /etc/osmocom
chmod -v 2775 /etc/osmocom
fi
mkdir -p /var/lib/osmocom
chown -R -v osmocom:osmocom /var/lib/osmocom
fi
;;
esac
# dh_installdeb(1) will replace this with shell code automatically
# generated by other debhelper scripts.
#DEBHELPER#

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

@ -9,7 +9,7 @@ digraph hierarchy {
node[shape=record,style=filled,fillcolor=gray95]
edge[dir=back, arrowtail=empty]
2[label = "{Transceiver|+ constructor()\l+ destructor()\l+ init()\l+ numChans()\l+ receiveFIFO()\l+ setSignalHandler()}"]
2[label = "{Transceiver|+ constructor()\l+ destructor()\l+ init()\l+ numChans()\l+ receiveFIFO()\l+ setSignalHandler()\l}"]
3[label = "{RadioInterface|...}"]
4[label = "{RadioInterfaceResamp|...}"]
5[label = "{RadioInterfaceMulti|...}"]
@ -17,6 +17,7 @@ edge[dir=back, arrowtail=empty]
7[label = "{UHDDevice|...}"]
8[label = "{LMSDevice|...}"]
9[label = "{USRPDevice|...}"]
10[label = "{IPCDevice|...}"]
2->3[arrowtail=odiamond]
3->4[constraint=false]
@ -25,6 +26,7 @@ edge[dir=back, arrowtail=empty]
6->7
6->8
6->9
6->10
}
----

View File

@ -0,0 +1,301 @@
[[ipc_if]]
== osmo-trx-ipc IPC Interface
This interface is the one used by _osmo_trx_ipc_ backend to communicate to a
third party process in charge of driving the lowest layer device-specific bits
(from now on the Driver).
It consists of a set of Unix Domain (UD) sockets for the control plane, plus a
shared memory region for the data plane.
Related code can be found in the
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc[Transceiver52M/device/ipc/]
directory in _osmo-trx.git_.
If you are a potential driver implementator, the
various primitives and data structures are publicly available in header file
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/shm.h[Transceiver52M/device/ipc/shm.h].
=== Control plane
Control plane protocol is transmitted over Unix Domain (UD) sockets using
message based primitives. Each primitive has a type identified by an integer,
and each type of primitive has a number of extra attributes attached to it. The
IPC interface consists of 2 types of UD sockets:
* _Master_ UD socket: One per osmo-trx-ipc process.
* _Channel_ UD socket: One for each channel managed by osmo-trx-ipc process.
The _Driver_ is in all cases expected to take the server role when creating UD
sockets, while _osmo-trx-ipc_ takes the client role and connects to sockets
provided by the driver.
=== Master UD socket
During startup, _osmo-trx-ipc_ will try connecting to the _Driver_ Master UD
socket located in the path provided by its own (VTY) configuration. As a result,
it means the _Driver_ process must be running and listening on the Master UD
socket before _osmo-trx-ipc_ is started, otherwise _osmo-trx-ipc_ will fail and
exit.
Once connected, _osmo-trx-ipc_ will submit a `GREETING_REQ` message primitive
announcing the maximum supported protocol version (first version ever is `1`,
increasing over time).
The _Driver_ shall then answer in `GREETING_CNF` message primitive with its own
maximum supported version (`<=` version received), providing 0 if none is
supported.
If _osmo-trx-ipc_ receives back the requested version, then both sides agreed
on the protocol version to use.
If _osmo-trx-ipc_ receives back a lower version, it shall decide to continue
with version negotiation using a lower version, until a supported version or 0
is received. If finally 0 is received, _osmo-trx-ipc_ will disconnect and exit
with failure.
Once the version is negotiated (`v1` as of current date), _osmo-trx-ipc_ will
ask for device information and available characeristics to the _Driver_ using
the `INFO_REQ` message primitive.
The _Driver_ shall then answer with a `INFO_CNF` message
containing information, such as:
* String containing device description
* Available reference clocks,
* {rx,tx} I/Q scaling factors
* Maximum number of channels supported
* for each channel:
** List of available {rx,tx} paths/antennas.
** {min,max}{rx,tx} gains
** Nominal transmit power
All the information received from the _Driver_ during `INFO_CNF` will be used by
_osmo-trx-ipc_ to decide whether it can fullfil the requested configuration from
the user, and proceed to open the device, or exit with a failure (for instance
number of channels, referece clock or tx/rx antenna selected by the user cannot
be fullfilled).
_osmo-trx-ipc_ will then proceed to open the device and do an initial
configuration using an `OPEN_REQ` message, where it will provide the _Driver_
with the desired selected configuration (such as number of channels, rx/tx
paths, clock reference, bandwidth filters, etc.).
The _Driver_ shall then configure the device and send back a `OPEN_CNF` with:
* `return_code` integer attribute set to `0` on success or `!0` on error.
* Name of the Posix Shared Memory region where data plane is going to be
transmitted.
* One path for each channel, containing the just-created UD socket to manage
that channel (for instance by taking Master UD socket path and appending
`_$chan_idx`).
* Path Delay: this is the loopback path delay in samples (= used as a timestamp
offset internally by _osmo-trx-ipc_), this value contains the analog delay as
well as the delay introduced by the digital filters in the fpga in the sdr
devices, and is therefore device type and bandwidth/sample rate dependant. This
can not be omitted, wrong values will lead to a _osmo-trx-ipc_ that just doesn't
detect any bursts.
Finally, _osmo-trx-ipc_ will connect to each channel's UD socket (see next
section).
Upon _osmo-trx-ipc_ closing the UD master socket connection, the _Driver_ shall
go into _closed_ state: stop all processing and instruct the device to power
off.
TIP: See
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/shm.h[Transceiver52M/device/ipc/shm.h]
for the detailed definition of all the related message primitives and data
types for this socket.
=== Channel UD Socket
This socket can be used by _osmo-trx-ipc_ to start/stop data plane processing or
change channel's parameters such as Rx/Tx Frequency, Rx/Tx gains, etc.
A channel can be either in _started_ or _stopped_ state. When a channel is
created (during `OPEN_REQ` in the Master UD Socket), it's by default in
_stopped_ state. `START_REQ` and `STOP_REQ` messages control this state, and
eventual failures can be reported through `START_CNF` and `STOP_CNF` by the
_Driver_.
The message `START_REQ` instructs the _Driver_ to start processing data in the
data plane. Similary, `STOP_REQ` instructs the _Driver_ to stop processing data
in the data plane.
Some parameters are usually changed only when the channel is in stopped mode,
for instance Rx/Tx Frequency.
TIP: See
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/shm.h[Transceiver52M/device/ipc/shm.h]
for the detailed definition of all the related message primitives and data
types for this socket.
=== Data Plane
Data plane protocol is implemented by means of a ring buffer structure on top of
Posix Shared Memory (see `man 7 shm_overview`) between _osmo-trx-ipc_ process
and the _Driver_.
The Posix Shared Memory region is created and its memory structure prepared by
the _Driver_ and its name shared with _osmo-trx-ipc_ during _OPEN_CNF_ message
in the Master UD Socket from the Control Plane. Resource allocation for the
shared memory area and cleanup is up to the ipc server, as is mutex
initialization for the buffers.
==== Posix Shared Memory structure
[[fig-shm-structure]]
.General overview of Posix Shared Memory structure
[graphviz]
----
digraph hierarchy {
node[shape=record,style=filled,fillcolor=gray95]
edge[dir=back, arrowtail=empty]
SHM[label = "{Posix Shared Memory region|+ num_chans\l+ Channels[]\l}"]
CHAN0[label = "{Channel 0|...}"]
CHAN1[label = "{Channel 1|...}"]
CHANN[label = "{Channel ...|}"]
STREAM0_UL[label = "{UL Stream|+ semaphore\l+ read_next\l+ write_next\l+ buffer_size /* In samples */\l+ num_buffers\l+ sample_buffers[]\l}"]
STREAM0_DL[label = "{DL Stream|+ semaphore\l+ read_next\l+ write_next\l+ buffer_size /* In samples */\l+ num_buffers\l+ sample_buffers[]\l}"]
STREAM1_UL[label = "{UL Stream|...}"]
STREAM1_DL[label = "{DL Stream|...}"]
STREAMN_UL[label = "{UL Stream|...}"]
STREAMN_DL[label = "{DL Stream|...}"]
BUF_0DL0[label = "{DL Sample Buffer 0|+ timestamp\l+ buffer_size /* In samples */\l+ samples[] = [16bit I + 16bit Q,...]\l}"]
BUF_0DLN[label = "{DL Sample Buffer ....|...}"]
BUF_0UL0[label = "{UL Sample Buffer 0|+ timestamp\l+ buffer_size /* In samples */\l+ samples[] = [16bit I + 16bit Q,...]\l}"]
BUF_0ULN[label = "{UL Sample Buffer ...|...}"]
SHM->CHAN0
SHM->CHAN1
SHM->CHANN
CHAN0->STREAM0_DL
CHAN0->STREAM0_UL
STREAM0_DL->BUF_0DL0
STREAM0_DL->BUF_0DLN
STREAM0_UL->BUF_0UL0
STREAM0_UL->BUF_0ULN
CHAN1->STREAM1_UL
CHAN1->STREAM1_DL
CHANN->STREAMN_UL
CHANN->STREAMN_DL
}
----
The Posix Shared Memory region contains an array of _Channels_.
Each _Channel_ contains 2 Streams:
* Downlink _Stream_
* Uplink _Stream_
Each _Stream_ handles a ring buffer, which is implemented as:
* An array of pointers to _Sample Buffer_ structures.
* Variables containing the number of buffers in the array, as well as the
maximum size in samples for each Sample Buffer.
* Variables containing `next_read` and `next_write` _Sample Buffer_ (its index
in the array of pointers).
* Unnamed Posix semaphores to do the required locking while using the ring
buffer.
Each _Sample Buffer_ contains:
* A `timestamp` variable, containing the position in the stream of the first
sample in the buffer
* A `data_len` variable, containing the amount of samples available to process
in the buffer
* An array of samples of size specified by the stream struct it is part of.
==== Posix Shared Memory format
The Posix Shared memory region shall be formatted applying the following
considerations:
* All pointers in the memory region are encoded as offsets from the start
address of the region itself, to allow different processes with different
address spaces to decode them.
* All structs must be force-aligned to 8 bytes
* Number of buffers must be power of 2 (2,4,8,16,...) - 4 appears to be plenty
* IQ samples format: One (complex) sample consists of 16bit i + 16bit q, so the
buffer size is number of IQ pairs.
* A reasonable per-buffer size (in samples) is 2500, since this happens to be
the ususal TX (downlink) buffer size used by _osmo-trx-ipc_ with the b210 (rx
over-the-wire packet size for the b210 is 2040 samples, so the larger value of
both is convenient).
TIP: See
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/shm.h[Transceiver52M/device/ipc/shm.h]
for the detailed definition of all the objects being part of the Posix Shared
memory region structure
==== Posix Shared Memory procedures
The queue in the shared memory area is not supposed to be used for actual
buffering of data, only for exchange, so the general expectation is that it is
mostly empty. The only exception to that might be minor processing delays, and
during startup.
Care must be taken to ensure that only timed waits for the mutex protecting it
and the condition variables are used, in order to ensure that no deadlock occurs
should the other side die/quit.
Thread cancellation should be disabled during reads/writes from/to the queue. In
general a timeout can be considered a non recoverable error during regular
processing after startup, at least with the current timeout value of one second.
Should over- or underflows occur a corresponding message should be sent towards
_osmo-trx-ipc_.
Upon **read** of `N` samples, the reader does something like:
. Acquire the semaphore in the channel's stream object.
. Read `stream->next_read`, if `next_read==next_write`, become blocked in
another sempahore (unlocking the previous one) until writer signals us, then
`buff = stream->buffers[next_read]`
. Read `buff->data_len` samples, reset the buffer data (`data_len=0`),
increment `next_read` and if read samples is `<N`, continue with next buffer
until `next_read==next_write`, then block again or if timeout elapsed, then we
reach conditon buffer underflow and `return len < N`.
. Release the semaphore
Upon **write** of `N` samples, the writer does something like:
. Acquire the semapore in the channel's stream object.
. Write samples to `buff = stream->buffers[next_write]`. If `data_len!=0`,
signal `buffer_overflow` (increase field in stream object) and probably
increase next_read`.
. Increase `next_write`.
. If `next_write` was `== next_read`, signal the reader through the other
semaphore that it can continue reading.

View File

@ -71,3 +71,103 @@ with a memory buffer. In this mode, data written to the USRP is actually stored
in a buffer, and read commands to the USRP simply pull data from this buffer.
This was very useful in early testing, and still may be useful in testing basic
Transceiver and radioInterface functionality.
[[backend_ipc]]
=== `osmo-trx-ipc` Inter Process Communication backend
This OsmoTRX model provides its own Inter Process Communication (IPC) interface
to drive the radio device driver (from now on the Driver), allowing for third
party processes to implement the lowest layer device-specific bits without being
affected by copyleft licenses of OsmoTRX.
For more information on such interface, see section <<ipc_if>>.
[[fig-backend-ipc]]
.Architecture with _osmo-trx-ipc_ and its IPC _Driver_
[graphviz]
----
digraph G {
rankdir=LR;
MS0 [label="MS"];
MS1 [label="MS"];
OsmoTRX [label="osmo-trx-ipc", color=red];
BTS;
subgraph cluster_ipc_driver {
label = "IPC Driver";
color=red;
RE [label = "Radio Equipment"];
REC [label="Radio Equipment Controller"];
RE->REC;
}
REC->OsmoTRX [label="IPC Interface", color=red];
MS0->RE [label="Um"];
MS1->RE [label="Um"];
OsmoTRX->BTS [label="bursts over UDP"];
}
----
A sample config file for this OsmoTRX model can be found in _osmo-trx.git_ https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/doc/examples/osmo-trx-ipc/osmo-trx-ipc.cfg[doc/examples/osmo-trx-ipc/osmo-trx-ipc.cfg]
In the config file, the following VTY command can be used to set up the IPC UD Master Socket _osmo-trx-ipc_ will connect to at startup:
.Example: _osmo-trx-ipc_ will connect to UD Master Socket /tmp/ipc_sock0 upon startup
----
dev-args ipc_msock=/tmp/ipc_sock0
----
==== ipc-device-test
When built with `--with-ipc --with-uhd` configure options, _osmo-trx.git_ will
build the test program called _ipc-driver-test_. This program implements the
_Driver_ side of the osmo-trx-ipc interface (see <<ipc_if>> for more
information) on one side, and also interacts internally with UHD (eg B210 as
when using osmo-trx-uhd).
You can use this small program as a reference to:
* Test and experiment with _osmo-trx-ipc_.
* Write your own IPC _Driver_ connecting to osmo-trx-ipc.
[[fig-backend-ipc-device-test]]
.Architecture with _osmo-trx-ipc_ and ipc-device-test as IPC _Driver_
[graphviz]
----
digraph G {
rankdir=LR;
MS0 [label="MS"];
MS1 [label="MS"];
SDR;
ipc_device_test[label = "ipc-device-test", color=red];
OsmoTRX [label="osmo-trx-ipc", color=red];
BTS;
MS0->SDR [label="Um"];
MS1->SDR [label="Um"];
SDR->ipc_device_test [label="UHD"];
ipc_device_test->OsmoTRX [label="IPC Interface", color=red];
OsmoTRX->BTS [label="bursts over UDP"];
}
----
The code for this app is found here:
* https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/ipc-driver-test.h[Transceiver52M/device/ipc/ipc-driver-test.h]
* https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/ipc-driver-test.c[Transceiver52M/device/ipc/ipc-driver-test.c]
Those files use the server-side (_Driver_ side) code to operate the Posix Shared
Memory region implemented in files `shm.c`, `shm.h`, `ipc_shm.c` and `ipc_shm.h`
in the same directory.
Most of the code in that same directory is deliverately released under a BSD
license (unlike most of _osmo-trx.git_), allowing third parties to reuse/recycle
the code on their implemented _Driver_ program no matter it being proprietary or
under an open license. However, care must be taken with external dependencies,
as for instance shm.c uses the talloc memory allocator, which is GPL licensed
and hence cannot be used in a proprietary driver.

View File

@ -35,6 +35,8 @@ include::./common/chapters/vty_cpu_sched.adoc[]
include::./common/chapters/trx_if.adoc[]
include::{srcdir}/chapters/ipc_if.adoc[]
include::./common/chapters/port_numbers.adoc[]
include::./common/chapters/bibliography.adoc[]

@ -1 +1 @@
Subproject commit 05ddc05233100236d93c1268a1d851b14efc6fca
Subproject commit f12b17dffb782c7428a563620aa83ec047fd99c4

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.