Compare commits

...

56 Commits

Author SHA1 Message Date
Eric Wild
9269aaa9cb Bump version: 1.6.0 → 1.6.1
Change-Id: I345ba6569ae0765b3d33e4024d07fc5d925bdf80
2023-11-09 15:25:47 +01:00
Eric
f97c53bc9c 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 15:12:20 +01:00
Pau Espin Pedrol
2ada887367 Bump version: 1.5.0.50-5042-dirty → 1.6.0
Change-Id: I718c6223d0b1a2ffb68887c159546b78e141dffa
2023-09-12 15:56:58 +02:00
Pau Espin Pedrol
5042156437 ms: update osmocom-bb submodule
Change-Id: I5e1a5f385e7d5e041142e665181d68ad5bb22230
2023-09-06 13:45:28 +02:00
Vadim Yanitskiy
70ed3d586e ms: logging: print category, level, and extended timestamp
Change-Id: I14da3c9e77681268e4f575a6ca7220eedbf2a7a5
Related: osmocom-bb.git Ie3d259f3255d8af80e6780f850b808fa243f97b4
2023-09-06 02:28:41 +07:00
Eric
3dfbb6d9f6 ms: bump osmocom-bb submodule to current head
Change-Id: I9a16de0dce2e89fb522271ba63d908e33a1d7cde
2023-08-31 17:23:12 +02:00
Eric
58294fde4b ms: fix a few coverity complaints related to initialization
Change-Id: I203f4da7a4418fc5053d26901bec649d04fad096
2023-08-30 13:54:22 +02:00
Eric
d372eb2f0b ms: fix thread prio startup issue
This fixes the 20 second startup delay caused by tx/control threads
getting temporarily stuck while trying to set their own priority.

Apparently the only sane way for core affinity+priority is to set both
as attributes during thread creation using pthreads.

This switches the cmd queue to the timeout version, too, to ensure the
thread doesn't get stuck waiting for messages, and allows cleaner exits.

Change-Id: I7e2f83a9b9df024acaf9076c58189cb6b7bcc34b
2023-08-30 13:54:18 +02:00
Eric
287ae681b7 ms/va: make ancient gcc < 8 happy
Change-Id: Ib94b9b073a0d2af84ce5791b5f40c63cd7ef9329
2023-08-30 11:54:26 +02:00
Eric
cca5d93f66 transceiver: add experimental viterbi equalizer support
The VA is already being used by the ms side and is part of the original
gsm design. It only works for gmsk, 4sps, and needs a bit of rx burst
scaling and burst shifting.

Change-Id: I9d7a4ff72e323832a94d885d5714fcde01ceeb3d
2023-08-25 13:18:04 +00:00
Eric
a5a2275a08 ms: restructure the va code to add rach support
This commit adds support for rach bursts to the viterbi equalizer, which
is currently only being used by the ms side, so the equalizer can be used
by the osmo-trx network side, too. The difference is that rach bursts are
shorter than any other burst type (due to unknown TA) and start with
diffrent tail bits.

This drops the multiversioning which was only working for x86 anyway because
it can't be combined with no_ubsan.

Change-Id: I4a5cedc8c9a3289c75ce7b914eac286e601ebed0
2023-08-25 13:17:56 +00:00
Eric
1499f0343a ms: update osmocom-bb
..and sync the log levels.

Change-Id: I6f9fca7a4d6a02e82bf406fd136c5bde96bb93af
2023-08-25 13:17:56 +00:00
Eric
8c1d59086e trx: fix dev-args issue
osmo_talloc_replace_string used by the vty needs a talloc string.

Change-Id: Ifed8fd42dc7464899de69985f3b5f5b47c88a7d1
2023-08-24 11:43:47 +02:00
Eric
f57a86131e ms: drop the tx burst padding
useless.

Change-Id: Ied5c3ab5dde975e11b0ef6d9cbc86be19173c4e8
2023-07-28 12:19:41 +00:00
Eric
7d5c16590c ms: fix blocking logging
Change-Id: I0f5dcb13d1bd9e626e1eeab6ca767ca1b5ae43b8
2023-07-25 18:25:49 +02:00
Eric Wild
f8c7a52521 devices: add freq/gain override for uhd
This allows using arbitrary gain and frequencies.

Change-Id: I3c1b9a067cafc6d696b9aa2da8ee0480ec1e094f
2023-07-25 18:25:49 +02:00
Eric
c0f78a37ed devices: unify band handling
This is basically common, but optional code.

Change-Id: I64f5a462451e967d4750d8e4f1d5832cbab41cff
2023-07-25 18:25:49 +02:00
Eric
19e134a626 transceiver: pass cfg struct instead of args
Passing 7 args is a bit much, just pass the config struct instead.

Change-Id: I48386900d15ff4d770c70a4efc246d32f921904b
2023-07-25 18:25:49 +02:00
Oliver Smith
a98521ac05 USRPDevice:updateAlignment: remove byteswap code
After upgrading our CI environment to use Debian 12 with GCC 12, the
byteswap code fails the build with the following. I've talked to Eric
about this and he recommended to just remove the code as practically
nobody will use osmo-trx with a big endian system.

USRPDevice.cpp:591:30: error: 'data' is used uninitialized [-Werror=uninitialized]
  591 |   *wordPtr = host_to_usrp_u32(*wordPtr);
      |              ~~~~~~~~~~~~~~~~^~~~~~~~~~

Related: OS#6057
Change-Id: I806d8c1432cb20efca1830a2752a4cbc70384b54
2023-07-13 14:34:59 +02:00
Eric
df4520df77 ms: make init call less confusing
Change-Id: I122b0c8cf97e5efcbc60cd95e8bd06a50d57eb57
2023-07-07 19:16:40 +02:00
Eric Wild
2f40abd8f5 ms: sch: drop intermediate softvector
Change-Id: Iadc8f224f9e43282339197b11f388fc574656299
2023-07-07 19:16:40 +02:00
Eric
6a3e4b32f0 ms: flexible template for value_type buffer sum
Manual attempts to get the number of complex and single samples right
turned out to be a bit error prone at times...

Change-Id: I3c9953073555e3a7f70b78b0946dfdf949175a82
2023-07-07 19:16:40 +02:00
Eric
c3e515a28b ms: rearrange code to allow clean exits
This allows gracefully terminating the application by introducing queue
timeouts.

Change-Id: I0b8deebc63cf4d936666fd68e1666d1917e89a5d
2023-07-07 19:12:39 +02:00
Eric
bcaafcaa9d ms: prune common sch acq code
Change-Id: Ife639a78a4463f992247c19e0177f683db2ae0b7
2023-07-07 19:12:39 +02:00
Eric
ea7bd5fb91 ms: remove syncthing tool
Only used for testing during the first stages of development.

Change-Id: Ie97069a109324b6e96c66a4b24d03f9745b6a52e
2023-07-07 19:12:39 +02:00
Eric
135d64b1a9 ms: rearrange internal trxcon<->phy if
Change-Id: I20aef3844f7699e164fe089358aa7e2325608c85
2023-07-07 19:12:39 +02:00
Eric
e44cf44af4 ms: pretty tx buf class
Change-Id: I96c5dd79426a52e7fff2df27bdaa3fae0c69491a
2023-07-07 19:12:39 +02:00
Eric
c0f0a6105a ms: cache frequency
Don't waste time setting the same frequency again.

Change-Id: Ide9f45130955e1cc66610a50d6fc1cd79f30aca9
2023-07-07 19:12:39 +02:00
Eric
da5ffd6e01 ms : rename var
Change-Id: Ia90a9f73fdb1f96fa5bd7f27b3c191ce0ba6c65d
2023-07-07 19:12:39 +02:00
Eric
40978041ad ms : rename var
Change-Id: I44e5d1770b248f107abce5683d5f7641655da764
2023-07-07 19:12:39 +02:00
Eric
3e7f4b0da9 ms: use single thread pool
...so we don't spawn threads all the time.
Used for gain avg/setting.

Change-Id: Id675550f55e8ccbbbe6b0d91fbffd01b6ede15f7
2023-07-07 19:12:39 +02:00
Eric
4080cd05ba ms: block burst q to upper layer
The timekeeper should never wait for lazy readers, because that causes
timekeeping and later usb transfers to fail.

Change-Id: Id0aad606a296b2885617013ce6637204357b13d7
2023-07-07 19:10:55 +02:00
Eric
b3157b91bb ms: fix startup & shutdown of blade
One of the mystery bugs was that the blade ms needed two starts
after powercycling the bladerf due to transfer timeouts.
This is now fixed.

Change-Id: I1cd8790191790f4861a70bc55c8f4c9993fa10c8
2023-07-06 18:17:06 +02:00
Eric
805e0d9c6b ms: prettify scheduling + add odroid
Change-Id: Icaf42fd5f76dc2d53dc526558aa8ceaa9c190f7b
2023-07-06 18:17:06 +02:00
Eric
4b2b98b067 ms: fix the gain init for blade
Change-Id: Ic5a25d6f5606fba599b8144fbec7285047dca3c9
2023-07-06 18:17:06 +02:00
Eric
2e6c362b9c ms : fix the template formatting
Those lines predate the .clang-format changes.

Change-Id: I891bdf95004accebbbe54916e4472bd381fac545
2023-07-06 16:06:05 +00:00
Oliver Smith
7e47a521ef systemd: depend on networking-online.target
Related: SYS#6400
Change-Id: Idadcbbf55e976ae035cfac4b85ccd870e0f27b82
2023-05-26 14:10:49 +02:00
Oliver Smith
f35515c015 debian: set compat level to 10
Related: OS#5958
Change-Id: Ib3d33f323b0c3004911ec026612934c12c1162aa
2023-04-25 16:48:33 +02:00
Vadim Yanitskiy
874542ca7c CommonLibs: clean up and fix Makefile.am
* Do not hard-code '-O3' and '-g', let the user decide on them.
* Move '-lpthread' to LIBADD, it shall not be in CFLAGS.
* Also add '-Wall' to AM_CFLAGS.

Change-Id: Ia971fc4124b405514792e5309d42936aaebdc8c1
2023-03-14 13:16:09 +00:00
Vadim Yanitskiy
f476a6755b CommonLibs: remove unused *trx in cfg[_no]_ctr_error_threshold_cmd
Compilation fails when building with -Wall and -Werror:

trx_vty.c: In function ‘cfg_ctr_error_threshold’:
trx_vty.c:469:18: error: unused variable ‘trx’ [-Werror=unused-variable]
  struct trx_ctx *trx = trx_from_vty(vty);
                  ^~~
trx_vty.c: In function ‘cfg_no_ctr_error_threshold’:
trx_vty.c:501:18: error: unused variable ‘trx’ [-Werror=unused-variable]
  struct trx_ctx *trx = trx_from_vty(vty);
                  ^~~

Change-Id: I3d74c3f38aa03608a32b9dee472e49236543c87f
2023-03-14 13:16:09 +00:00
Vadim Yanitskiy
1ddd727bb4 ipc-driver-test: clean up variables in Makefile.am
* AM_CPPFLAGS is for preprocessor flags like '-I' or '-D',
* AM_CFLAGS/AM_CXXFLAGS is for C/C++ compiler flags like '-Wall',
* AM_LDFLAGS is for linker flags like '-no-undefined', not libraries!
* Link ipc-driver-test against libdevice.la,
* Do not put $(UHD_CFLAGS) everywhere.

Change-Id: Iafd68974c9c613fb4e65a01d076b2c687b716c83
2023-03-14 13:16:09 +00:00
Pau Espin Pedrol
8a4362459d Call osmo_fd_unregister() before closing and changing bfd->fd
Change-Id: Iffc7d89166be1cf3cd1f8c3effe90f04d5c5a9c1
2023-03-14 12:54:20 +00:00
Vadim Yanitskiy
5ba130c381 tests: LMSDeviceTest: fix CPPFLAGS vs CXXFLAGS
* AM_CPPFLAGS is for preprocessor flags like '-I' or '-D',
* AM_CXXFLAGS is for C++ compiler flags like '-Wall'.

Change-Id: I82fc1eb6796f5289f0feff7bee5264bfacd6e733
2023-03-11 05:33:37 +07:00
Vadim Yanitskiy
fed58d97b8 tests: use -no-install libtool flag to avoid ./lt-* scripts
This option should be used for any executables which are used only
for testing, or for generating other files and are consequently never
installed.  By specifying this option, we are telling Libtool that
the executable it links will only ever be executed from where it is
built in the build tree.  Libtool is usually able to considerably
speed up the link process for such executables.

Change-Id: Ieb4ddc5799819c24ed357218c7b0197bcb6b5c91
2023-03-11 05:33:36 +07:00
Vadim Yanitskiy
0e13bfd18c tests: there shall be no libraries in LDFLAGS
Change-Id: I95b07b5a7735fb2753281555a4544f1ba967579e
2023-03-11 05:33:10 +07:00
Vadim Yanitskiy
cf1ca2e92e tests: Makefile.am: move -I flags from AM_CFLAGS to AM_CPPFLAGS
* AM_CPPFLAGS is for preprocessor flags like '-I' or '-D',
* AM_CFLAGS is for C (not C++!) compiler flags like '-Wall'.
* AM_CXXFLAGS is for C++ specific compiler flags like '-Wall'.

By having the preprocessor flags in the proper variable we make
sure that they apply to both *.c and *.cpp files.

Change-Id: I7b3504a01e3350834b35c42d8d76d5d88d84a4b9
2023-03-11 05:31:57 +07:00
arehbein
20ecc4f531 Transition to use of 'telnet_init_default'
Related: OS#5809
Change-Id: Icc57c68337d55c6594c1c36e9bf41624d11dab0a
2023-03-09 12:48:32 +00:00
Eric
097a16e384 ms: adjust tx scaling for tx samples
The "safe" scaling factor introduced in
7ac54b10d3 is too low and dates back to
the beginning and the move from usrp1->uhd, but the modulator will
exceed +-1 so "proper" scaling leads to overflows. Let's just do what
osmotrx has been doing for many years...

Change-Id: I75a2eba1f7f7b81249c06ce3fc9dfeee08878cb9
2023-03-02 18:22:37 +01:00
Eric
c9af0b0ba0 ms: update submodule to currently known working version
Change-Id: I690f508d1f0cee0b623196dd4574c3ee456f07fa
2023-03-02 18:22:37 +01:00
Eric Wild
621a49eb69 ms: adjust float<->integral type conversion
Given integral type A and non integral type B and depending on rounding
mode, optimization, compiler, and phase of the moon A(A)*B != A(A*B) so
split the two cases.

While at it, also make the template automagically work for complex types
instead of requiring manual casts, the general idea here is to allow
inlining and vectorization by treating all args as plain arrays, which is fine.

This works as expected with -tune=native, x64 implies sse2, and we do not
target any neon-less arm versions either.

Clang only array length hints can improve this even more.

Change-Id: I93f077f967daf2ed382d12cc20a54846b3688634
2023-03-02 18:22:37 +01:00
Eric
f538397826 .clang-format: adjust template formatting
Change-Id: I9345693982ce33e6bc811be9e98b9d3da2add7a1
2023-03-02 18:22:37 +01:00
Oliver Smith
8c4336dba9 Run struct_endianness.py
Ensure there is no diff to prepare to run this in CI.

Related: OS#5884
Change-Id: I7d571a042009a3d1befb71fdd490fdef39368066
2023-02-20 10:53:48 +01:00
Vadim Yanitskiy
fe1b9cef40 configure.ac: check if LIBTRXCON_DIR (submodule) exists
Fail during the configure stage if the submodule is not fetched.

Change-Id: I06456c2087e160032f5bf8b36304477390f117d0
Related: OS#5599
2023-02-10 22:08:35 +07:00
Pau Espin Pedrol
5e63151f9f Bump version: 1.4.1.29-10b4-dirty → 1.5.0
Change-Id: I84399209ff49769254771e9273bcfc61d47a0226
2023-02-07 17:08:18 +01:00
Eric Wild
10b4e31655 mstrx: do not wait forever if clock locking fails
Change-Id: Ib85f2452b66fb4d9881fe9676e69e4f6a8ba8f74
2023-01-11 18:57:51 +01:00
Eric
0c433350da radio interface: fix init
5561f1129d introduced some changes,
but while RadioInterface lost its call to close() that was previously
used to improperly reset the buffers upon init() that call was
accidentally not removed for RadioInterfaceMulti and
RadioInterfaceResamp, so those reset previously initialized values to 0
during init(), which break osmo-trx for weird setups.

Change-Id: I74fc1586f8ae0832f4093ba8a44a1c70c78ec3d8
2023-01-09 20:17:46 +01:00
59 changed files with 1713 additions and 1463 deletions

View File

@@ -25,7 +25,7 @@ AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:

View File

@@ -22,8 +22,8 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
noinst_LTLIBRARIES = libcommon.la
@@ -37,7 +37,12 @@ libcommon_la_SOURCES = \
trx_rate_ctr.cpp \
trx_vty.c \
debug.c
libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
libcommon_la_LIBADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_LIBS) \
-lpthread \
$(NULL)
noinst_HEADERS = \
BitVector.h \

View File

@@ -57,4 +57,15 @@ struct trx_cfg {
unsigned int stack_size;
unsigned int num_chans;
struct trx_chan chans[TRX_CHAN_MAX];
struct {
bool ul_freq_override;
bool dl_freq_override;
bool ul_gain_override;
bool dl_gain_override;
double ul_freq;
double dl_freq;
double ul_gain;
double dl_gain;
} overrides;
bool use_va;
};

View File

@@ -285,6 +285,79 @@ DEFUN_ATTR(cfg_ul_fn_offset, cfg_ul_fn_offset_cmd,
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_ul_freq_override, cfg_ul_freq_override_cmd,
"ul-freq-override FLOAT",
"Overrides Rx carrier frequency\n"
"Frequency in Hz (e.g. 145300000)\n",
CMD_ATTR_HIDDEN)
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.overrides.ul_freq_override = true;
trx->cfg.overrides.ul_freq = atof(argv[0]);
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_dl_freq_override, cfg_dl_freq_override_cmd,
"dl-freq-override FLOAT",
"Overrides Tx carrier frequency\n"
"Frequency in Hz (e.g. 145300000)\n",
CMD_ATTR_HIDDEN)
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.overrides.dl_freq_override = true;
trx->cfg.overrides.dl_freq = atof(argv[0]);
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_ul_gain_override, cfg_ul_gain_override_cmd,
"ul-gain-override FLOAT",
"Overrides Rx gain\n"
"gain in dB\n",
CMD_ATTR_HIDDEN)
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.overrides.ul_gain_override = true;
trx->cfg.overrides.ul_gain = atof(argv[0]);
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_dl_gain_override, cfg_dl_gain_override_cmd,
"dl-gain-override FLOAT",
"Overrides Tx gain\n"
"gain in dB\n",
CMD_ATTR_HIDDEN)
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.overrides.dl_gain_override = true;
trx->cfg.overrides.dl_gain = atof(argv[0]);
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_use_viterbi, cfg_use_viterbi_cmd,
"viterbi-eq (disable|enable)",
"Use viterbi equalizer for gmsk (default=disable)\n"
"Disable VA\n"
"Enable VA\n",
CMD_ATTR_HIDDEN)
{
struct trx_ctx *trx = trx_from_vty(vty);
if (strcmp("disable", argv[0]) == 0)
trx->cfg.use_va = false;
else if (strcmp("enable", argv[0]) == 0)
trx->cfg.use_va = true;
else
return CMD_WARNING;
return CMD_SUCCESS;
}
DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
"swap-channels (disable|enable)",
"Swap primary and secondary channels of the PHY (if any)\n"
@@ -466,7 +539,6 @@ DEFUN_ATTR(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,
int rc;
struct ctr_threshold ctr;
struct trx_ctx *trx = trx_from_vty(vty);
rc = vty_ctr_name_2_id(argv[0]);
if (rc < 0) {
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
@@ -498,7 +570,6 @@ DEFUN_ATTR(cfg_no_ctr_error_threshold, cfg_no_ctr_error_threshold_cmd,
int rc;
struct ctr_threshold ctr;
struct trx_ctx *trx = trx_from_vty(vty);
rc = vty_ctr_name_2_id(argv[0]);
if (rc < 0) {
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
@@ -609,7 +680,7 @@ static int config_write_trx(struct vty *vty)
vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
if (trx->cfg.base_port != DEFAULT_TRX_PORT)
vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE);
if (trx->cfg.dev_args)
if (strlen(trx->cfg.dev_args))
vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE);
if (trx->cfg.tx_sps != DEFAULT_TX_SPS)
vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
@@ -640,6 +711,16 @@ static int config_write_trx(struct vty *vty)
vty_out(vty, " stack-size %u%s", trx->cfg.stack_size, VTY_NEWLINE);
if (trx->cfg.ul_fn_offset != 0)
vty_out(vty, " ul-fn-offset %d%s", trx->cfg.ul_fn_offset, VTY_NEWLINE);
if (trx->cfg.overrides.dl_freq_override)
vty_out(vty, " dl-freq-override %f%s", trx->cfg.overrides.dl_freq, VTY_NEWLINE);
if (trx->cfg.overrides.ul_freq_override)
vty_out(vty, " ul-freq-override %f%s", trx->cfg.overrides.ul_freq, VTY_NEWLINE);
if (trx->cfg.overrides.dl_gain_override)
vty_out(vty, " dl-gain-override %f%s", trx->cfg.overrides.dl_gain, VTY_NEWLINE);
if (trx->cfg.overrides.ul_gain_override)
vty_out(vty, " ul-gain-override %f%s", trx->cfg.overrides.ul_gain, VTY_NEWLINE);
if (trx->cfg.use_va)
vty_out(vty, " viterbi-eq %s%s", trx->cfg.use_va ? "enable" : "disable", VTY_NEWLINE);
trx_rate_ctr_threshold_write_config(vty, " ");
for (i = 0; i < trx->cfg.num_chans; i++) {
@@ -762,6 +843,7 @@ struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx)
trx->cfg.rx_sps = DEFAULT_RX_SPS;
trx->cfg.filler = FILLER_ZERO;
trx->cfg.rssi_offset = 0.0f;
trx->cfg.dev_args = talloc_strdup(trx, "");
return trx;
}
@@ -804,6 +886,11 @@ int trx_vty_init(struct trx_ctx* trx)
install_element(TRX_NODE, &cfg_chan_cmd);
install_element(TRX_NODE, &cfg_ul_fn_offset_cmd);
install_element(TRX_NODE, &cfg_ul_freq_override_cmd);
install_element(TRX_NODE, &cfg_dl_freq_override_cmd);
install_element(TRX_NODE, &cfg_ul_gain_override_cmd);
install_element(TRX_NODE, &cfg_dl_gain_override_cmd);
install_element(TRX_NODE, &cfg_use_viterbi_cmd);
install_node(&chan_node, dummy_config_write);
install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);

View File

@@ -29,7 +29,7 @@ unlike the built-in complex<> templates, these inline most operations for speed
template<class Real> class Complex {
public:
typedef Real value_type;
Real r, i;
/**@name constructors */

View File

@@ -40,7 +40,9 @@ COMMON_SOURCES = \
ChannelizerBase.cpp \
Channelizer.cpp \
Synthesis.cpp \
proto_trxd.c
proto_trxd.c \
grgsm_vitac/grgsm_vitac.cpp \
grgsm_vitac/viterbi_detector.cc
libtransceiver_common_la_SOURCES = \
$(COMMON_SOURCES) \
@@ -80,23 +82,32 @@ AM_CPPFLAGS += -I${srcdir}
TRXCON_LDADD = \
$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libtrxcon.a \
$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libl1sched.a \
$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libl1gprs.a \
$(LIBOSMOCODING_LIBS)
MS_SOURCES = \
MS_LOWER_SRC = \
ms/sch.c \
ms/ms.cpp \
ms/ms_rx_lower.cpp \
grgsm_vitac/grgsm_vitac.cpp \
grgsm_vitac/viterbi_detector.cc
MS_UPPER_SRC = \
ms/ms_upper.cpp \
ms/l1ctl_server.c \
ms/logging.c \
ms/l1ctl_server_cb.cpp \
ms/ms_trxcon_if.cpp
noinst_HEADERS += \
ms/ms.h \
ms/bladerf_specific.h \
ms/uhd_specific.h \
ms/ms_rx_burst.h \
ms/ms_upper.h \
ms/ms_trxcon_if.h \
ms/itrq.h \
ms/sch.h \
ms/threadpool.h \
grgsm_vitac/viterbi_detector.h \
grgsm_vitac/constants.h \
grgsm_vitac/grgsm_vitac.h
@@ -116,22 +127,13 @@ osmo_trx_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
if ENABLE_MS_TRX
bin_PROGRAMS += osmo-trx-ms-uhd
osmo_trx_ms_uhd_SOURCES = $(MS_SOURCES) ms/ms_upper.cpp ms/l1ctl_server.c ms/logging.c ms/l1ctl_server_cb.cpp
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
bin_PROGRAMS += osmo-trx-syncthing-uhd
osmo_trx_syncthing_uhd_SOURCES = $(MS_SOURCES) ms/ms_rx_burst_test.cpp
osmo_trx_syncthing_uhd_LDADD = \
$(builddir)/device/uhd/libdevice.la \
$(COMMON_LDADD) \
$(UHD_LIBS) \
$(TRXCON_LDADD)
osmo_trx_syncthing_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DSYNCTHINGONLY -DBUILDUHD
endif
endif
@@ -166,22 +168,13 @@ osmo_trx_blade_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
if ENABLE_MS_TRX
bin_PROGRAMS += osmo-trx-ms-blade
osmo_trx_ms_blade_SOURCES = $(MS_SOURCES) ms/ms_upper.cpp ms/l1ctl_server.c ms/logging.c ms/l1ctl_server_cb.cpp
osmo_trx_ms_blade_SOURCES = $(MS_LOWER_SRC) $(MS_UPPER_SRC)
osmo_trx_ms_blade_LDADD = \
$(builddir)/device/bladerf/libdevice.la \
$(COMMON_LDADD) \
$(BLADE_LIBS) \
$(TRXCON_LDADD)
osmo_trx_ms_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DBUILDBLADE
bin_PROGRAMS += osmo-trx-syncthing-blade
osmo_trx_syncthing_blade_SOURCES = $(MS_SOURCES) ms/ms_rx_burst_test.cpp
osmo_trx_syncthing_blade_LDADD = \
$(builddir)/device/bladerf/libdevice.la \
$(COMMON_LDADD) \
$(BLADE_LIBS) \
$(TRXCON_LDADD)
osmo_trx_syncthing_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DSYNCTHINGONLY -DBUILDBLADE -I../device/ipc
endif
endif

View File

@@ -29,6 +29,7 @@
#include <fstream>
#include "Transceiver.h"
#include <Logger.h>
#include <grgsm_vitac/grgsm_vitac.h>
extern "C" {
#include "osmo_signal.h"
@@ -208,6 +209,8 @@ bool Transceiver::init()
return false;
}
initvita();
mDataSockets.resize(mChans, -1);
@@ -614,6 +617,44 @@ double Transceiver::rssiOffset(size_t chan)
return mRadioInterface->rssiOffset(chan) + cfg->rssi_offset;
}
static SoftVector *demodAnyBurst_va(const signalVector &burst, CorrType type, int sps, int rach_max_toa, int tsc)
{
auto conved_beg = reinterpret_cast<const std::complex<float> *>(&burst.begin()[0]);
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
float ncmax;
const unsigned burst_len_bits = 148 + 8;
char demodded_softbits[burst_len_bits];
SoftVector *bits = new SoftVector(burst_len_bits);
if (type == CorrType::TSC) {
auto rach_burst_start = get_norm_chan_imp_resp(conved_beg, chan_imp_resp, &ncmax, tsc);
rach_burst_start = std::max(rach_burst_start, 0);
detect_burst_nb(conved_beg, chan_imp_resp, rach_burst_start, demodded_softbits);
} else {
auto normal_burst_start = get_access_imp_resp(conved_beg, chan_imp_resp, &ncmax, 0);
normal_burst_start = std::max(normal_burst_start, 0);
detect_burst_ab(conved_beg, chan_imp_resp, normal_burst_start, demodded_softbits, rach_max_toa);
}
float *s = &bits->begin()[0];
for (unsigned int i = 0; i < 148; i++)
s[i] = demodded_softbits[i] * -1;
for (unsigned int i = 148; i < burst_len_bits; i++)
s[i] = 0;
return bits;
}
#define USE_VA
#ifdef USE_VA
// 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;
};
#endif
/*
* Pull bursts from the FIFO and handle according to the slot
* and burst correlation type. Equalzation is currently disabled.
@@ -634,6 +675,9 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
TransceiverState *state = &mStates[chan];
bool ctr_changed = false;
double rssi_offset;
static complex burst_shift_buffer[625];
static signalVector shift_vec(burst_shift_buffer, 0, 625, dummy_alloc, dummy_free);
signalVector *shvec_ptr = &shift_vec;
/* Blocking FIFO read */
radioVector *radio_burst = mReceiveFIFO[chan]->read();
@@ -713,8 +757,15 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
max_toa = (type == RACH || type == EXT_RACH) ?
mMaxExpectedDelayAB : mMaxExpectedDelayNB;
if (cfg->use_va) {
// shifted burst copy to make the old demod and detection happy
std::copy(burst->begin() + 20, burst->end() - 20, shift_vec.begin());
} else {
shvec_ptr = burst;
}
/* Detect normal or RACH bursts */
rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
rc = detectAnyBurst(*shvec_ptr, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
if (rc <= 0) {
if (rc == -SIGERR_CLIP) {
LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst";
@@ -728,7 +779,13 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
goto ret_idle;
}
rxBurst = demodAnyBurst(*burst, (CorrType) rc, cfg->rx_sps, &ebp);
if (cfg->use_va) {
scaleVector(*burst, { (1. / (float)((1 << 14) - 1)), 0 });
rxBurst = demodAnyBurst_va(*burst, (CorrType)rc, cfg->rx_sps, max_toa, mTSC);
} else {
rxBurst = demodAnyBurst(*shvec_ptr, (CorrType)rc, cfg->rx_sps, &ebp);
}
bi->toa = ebp.toa;
bi->tsc = ebp.tsc;
bi->ci = ebp.ci;

View File

@@ -162,9 +162,9 @@ struct ctrl_sock_state {
}
~ctrl_sock_state() {
if(conn_bfd.fd >= 0) {
osmo_fd_unregister(&conn_bfd);
close(conn_bfd.fd);
conn_bfd.fd = -1;
osmo_fd_unregister(&conn_bfd);
}
}
};

View File

@@ -47,25 +47,11 @@ extern "C" {
LOGC(DDEV, ERROR) << bladerf_strerror(status); \
}
/* Device Type, Tx-SPS, Rx-SPS */
typedef std::tuple<blade_dev_type, int, int> dev_key;
/* Device parameter descriptor */
struct dev_desc {
unsigned channels;
double mcr;
double rate;
double offset;
std::string str;
};
static const std::map<dev_key, dev_desc> dev_param_map{
static const dev_map_t dev_param_map{
{ std::make_tuple(blade_dev_type::BLADE2, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
};
typedef std::tuple<blade_dev_type, enum gsm_band> dev_band_key;
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map{
static const power_map_t dev_band_nom_power_param_map{
{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
@@ -84,11 +70,10 @@ static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
}
blade_device::blade_device(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths), dev(nullptr), rx_gain_min(0.0),
rx_gain_max(0.0), band_ass_curr_sess(false), band((enum gsm_band)0), tx_spp(0), rx_spp(0), started(false),
aligned(false), drop_cnt(0), prev_ts(0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
blade_device::blade_device(InterfaceType iface, const struct trx_cfg *cfg)
: RadioDevice(iface, cfg), band_manager(dev_band_nom_power_param_map, dev_param_map), dev(nullptr),
rx_gain_min(0.0), rx_gain_max(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), drop_cnt(0),
prev_ts(0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
{
}
@@ -105,47 +90,6 @@ blade_device::~blade_device()
delete rx_buffers[i];
}
void blade_device::assign_band_desc(enum gsm_band req_band)
{
dev_band_map_it it;
it = dev_band_nom_power_param_map.find(dev_band_key(dev_type, req_band));
if (it == dev_band_nom_power_param_map.end()) {
dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
LOGC(DDEV, ERROR) << "No Power parameters exist for device " << desc.str << " on band "
<< gsm_band_name(req_band) << ", using B210 ones as fallback";
it = dev_band_nom_power_param_map.find(dev_band_key(blade_dev_type::BLADE2, req_band));
}
OSMO_ASSERT(it != dev_band_nom_power_param_map.end())
band_desc = it->second;
}
bool blade_device::set_band(enum gsm_band req_band)
{
if (band_ass_curr_sess && req_band != band) {
LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band) << " different from previous band "
<< gsm_band_name(band);
return false;
}
if (req_band != band) {
band = req_band;
assign_band_desc(band);
}
band_ass_curr_sess = true;
return true;
}
void blade_device::get_dev_band_desc(dev_band_desc &desc)
{
if (band == 0) {
LOGC(DDEV, ERROR)
<< "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
assign_band_desc(GSM_BAND_900);
}
desc = band_desc;
}
void blade_device::init_gains()
{
double tx_gain_min, tx_gain_max;
@@ -312,15 +256,15 @@ int blade_device::getNominalTxPower(size_t chan)
return desc.nom_out_tx_power;
}
int blade_device::open(const std::string &args, int ref, bool swap_channels)
int blade_device::open()
{
bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_VERBOSE);
bladerf_set_usb_reset_on_open(true);
auto success = bladerf_open(&dev, args.c_str());
auto success = bladerf_open(&dev, cfg->dev_args);
if (success != 0) {
struct bladerf_devinfo *info;
auto num_devs = bladerf_get_device_list(&info);
LOGC(DDEV, ALERT) << "No bladerf devices found with identifier '" << args << "'";
LOGC(DDEV, ALERT) << "No bladerf devices found with identifier '" << cfg->dev_args << "'";
if (num_devs) {
for (int i = 0; i < num_devs; i++)
LOGC(DDEV, ALERT) << "Found device:" << info[i].product << " serial " << info[i].serial;
@@ -335,6 +279,7 @@ int blade_device::open(const std::string &args, int ref, bool swap_channels)
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);
@@ -346,7 +291,7 @@ int blade_device::open(const std::string &args, int ref, bool swap_channels)
rx_gains.resize(chans);
rx_buffers.resize(chans);
switch (ref) {
switch (cfg->clock_ref) {
case REF_INTERNAL:
case REF_EXTERNAL:
break;
@@ -355,7 +300,7 @@ int blade_device::open(const std::string &args, int ref, bool swap_channels)
return -1;
}
if (ref == REF_EXTERNAL) {
if (cfg->clock_ref == REF_EXTERNAL) {
bool is_locked;
int status = bladerf_set_pll_enable(dev, true);
CHKRET()
@@ -374,7 +319,8 @@ int blade_device::open(const std::string &args, int ref, bool swap_channels)
}
}
LOGC(DDEV, INFO) << "Selected clock source is " << ((ref == REF_INTERNAL) ? "internal" : "external 10Mhz");
LOGC(DDEV, INFO)
<< "Selected clock source is " << ((cfg->clock_ref == REF_INTERNAL) ? "internal" : "external 10Mhz");
set_rates();
@@ -457,7 +403,7 @@ bool blade_device::stop()
for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i]->reset();
band_ass_curr_sess = false;
band_reset();
started = false;
return true;
@@ -580,27 +526,13 @@ bool blade_device::set_freq(double freq, size_t chan, bool tx)
bool blade_device::setTxFreq(double wFreq, size_t chan)
{
uint16_t req_arfcn;
enum gsm_band req_band;
if (chan >= tx_freqs.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
}
ScopedLock lock(tune_lock);
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 0);
if (req_arfcn == 0xffff) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
return false;
}
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
LOGCHAN(chan, DDEV, ALERT)
<< "Unknown GSM band for Tx Frequency " << wFreq << " Hz (ARFCN " << req_arfcn << " )";
return false;
}
if (!set_band(req_band))
if (!update_band_from_freq(wFreq, chan, true))
return false;
if (!set_freq(wFreq, chan, true))
@@ -611,27 +543,13 @@ bool blade_device::setTxFreq(double wFreq, size_t chan)
bool blade_device::setRxFreq(double wFreq, size_t chan)
{
uint16_t req_arfcn;
enum gsm_band req_band;
if (chan >= rx_freqs.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
}
ScopedLock lock(tune_lock);
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
if (req_arfcn == 0xffff) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
return false;
}
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
LOGCHAN(chan, DDEV, ALERT)
<< "Unknown GSM band for Rx Frequency " << wFreq << " Hz (ARFCN " << req_arfcn << " )";
return false;
}
if (!set_band(req_band))
if (!update_band_from_freq(wFreq, chan, false))
return false;
return set_freq(wFreq, chan, false);
@@ -687,8 +605,7 @@ double blade_device::fullScaleOutputValue()
return (double)2047;
}
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
{
return new blade_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
return new blade_device(type, cfg);
}

View File

@@ -22,10 +22,12 @@
#pragma once
#include <map>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "bandmanager.h"
#include "radioDevice.h"
#include "smpl_buf.h"
@@ -33,8 +35,6 @@ extern "C" {
#include <osmocom/gsm/gsm_utils.h>
}
#include <bladerf.h>
enum class blade_dev_type { BLADE1, BLADE2 };
struct dev_band_desc {
@@ -52,13 +52,26 @@ struct dev_band_desc {
double rxgain2rssioffset_rel; /* dB */
};
class blade_device : public RadioDevice {
/* Device parameter descriptor */
struct dev_desc {
unsigned channels;
double mcr;
double rate;
double offset;
std::string desc_str;
};
using dev_key = std::tuple<blade_dev_type, int, int>;
using dev_band_key = std::tuple<blade_dev_type, enum gsm_band>;
using power_map_t = std::map<dev_band_key, dev_band_desc>;
using dev_map_t = std::map<dev_key, dev_desc>;
class blade_device : public RadioDevice, public band_manager<power_map_t, dev_map_t> {
public:
blade_device(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chan_num, double offset,
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths);
blade_device(InterfaceType iface, const struct trx_cfg *cfg);
~blade_device();
int open(const std::string &args, int ref, bool swap_channels);
int open();
bool start();
bool stop();
bool restart();
@@ -154,9 +167,6 @@ class blade_device : public RadioDevice {
std::vector<double> tx_gains, rx_gains;
std::vector<double> tx_freqs, rx_freqs;
bool band_ass_curr_sess; /* true if "band" was set after last POWEROFF */
enum gsm_band band;
struct dev_band_desc band_desc;
size_t tx_spp, rx_spp;
bool started;
@@ -179,9 +189,6 @@ class blade_device : public RadioDevice {
bool flush_recv(size_t num_pkts);
bool set_freq(double freq, size_t chan, bool tx);
void get_dev_band_desc(dev_band_desc &desc);
bool set_band(enum gsm_band req_band);
void assign_band_desc(enum gsm_band req_band);
Thread *async_event_thrd;
Mutex tune_lock;

View File

@@ -4,7 +4,7 @@ AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
noinst_HEADERS = radioDevice.h smpl_buf.h
noinst_HEADERS = radioDevice.h smpl_buf.h bandmanager.h
noinst_LTLIBRARIES = libdevice_common.la

View File

@@ -0,0 +1,137 @@
#pragma once
/*
* (C) 2022 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 <string>
#include <tuple>
#include "Logger.h"
extern "C" {
#include <osmocom/gsm/gsm_utils.h>
}
template <typename powermapt, typename devmapt>
class band_manager {
using powerkeyt = typename powermapt::key_type;
using powermappedt = typename powermapt::mapped_type;
using devkeyt = typename devmapt::key_type;
devkeyt m_dev_type;
const powermapt &m_power_map;
const devmapt &m_dev_map;
powerkeyt m_fallback;
enum gsm_band m_band;
powermappedt m_band_desc;
bool band_ass_curr_sess{}; /* true if "band" was set after last POWEROFF */
// looks up either first tuple element (->enum) or straight enum
template <typename T, typename std::enable_if<std::is_enum<T>::value>::type *dummy = nullptr>
auto key_helper(T &t) -> T
{
return t;
}
template <typename T>
auto key_helper(T t) -> typename std::tuple_element<0, T>::type
{
return std::get<0>(t);
}
void assign_band_desc(enum gsm_band req_band)
{
auto key = key_helper(m_dev_type);
auto fallback_key = key_helper(m_fallback);
auto it = m_power_map.find({ key, req_band });
if (it == m_power_map.end()) {
auto desc = m_dev_map.at(m_dev_type);
LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device " << desc.desc_str
<< " on band " << gsm_band_name(req_band) << ", using fallback..";
it = m_power_map.find({ fallback_key, req_band });
}
OSMO_ASSERT(it != m_power_map.end());
m_band_desc = it->second;
}
bool set_band(enum gsm_band req_band)
{
if (band_ass_curr_sess && req_band != m_band) {
LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
<< " different from previous band " << gsm_band_name(m_band);
return false;
}
if (req_band != m_band) {
m_band = req_band;
assign_band_desc(m_band);
}
band_ass_curr_sess = true;
return true;
}
public:
band_manager(const devkeyt &dev_type, const powermapt &power_map, const devmapt &dev_map, powerkeyt fallback)
: m_dev_type(dev_type), m_power_map(power_map), m_dev_map(dev_map), m_fallback(fallback),
m_band((enum gsm_band)0)
{
}
band_manager(const powermapt &power_map, const devmapt &dev_map)
: m_dev_type(dev_map.begin()->first), m_power_map(power_map), m_dev_map(dev_map),
m_fallback(m_power_map.begin()->first), m_band((enum gsm_band)0)
{
}
void band_reset()
{
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) {
LOGC(DDEV, ERROR)
<< "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
assign_band_desc(GSM_BAND_900);
}
desc = m_band_desc;
}
bool update_band_from_freq(double wFreq, int chan, bool is_tx)
{
enum gsm_band req_band;
auto dirstr = is_tx ? "Tx" : "Rx";
auto req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, !is_tx);
if (req_arfcn == 0xffff) {
LOGCHAN(chan, DDEV, ALERT)
<< "Unknown ARFCN for " << dirstr << " Frequency " << wFreq / 1000 << " kHz";
return false;
}
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for " << dirstr << " Frequency " << wFreq
<< " Hz (ARFCN " << req_arfcn << " )";
return false;
}
return set_band(req_band);
}
};

View File

@@ -51,13 +51,10 @@ class RadioDevice {
MULTI_ARFCN,
};
static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type,
size_t chans = 1, double offset = 0.0,
const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""),
const std::vector<std::string>& rx_paths = std::vector<std::string>(1, ""));
static RadioDevice *make(InterfaceType type, const struct trx_cfg *cfg);
/** Initialize the USRP */
virtual int open(const std::string &args, int ref, bool swap_channels)=0;
virtual int open() = 0;
virtual ~RadioDevice() { }
@@ -164,23 +161,30 @@ class RadioDevice {
double lo_offset;
std::vector<std::string> tx_paths, rx_paths;
std::vector<struct device_counters> m_ctr;
const struct trx_cfg *cfg;
RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chan_num, double offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chan_num), lo_offset(offset),
tx_paths(tx_paths), rx_paths(rx_paths), m_ctr(chans)
{
if (iface == MULTI_ARFCN) {
LOGC(DDEV, INFO) << "Multi-ARFCN: "<< chan_num << " logical chans -> 1 physical chans";
chans = 1;
}
#define charp2str(a) ((a) ? std::string(a) : std::string(""))
for (size_t i = 0; i < chans; i++) {
memset(&m_ctr[i], 0, sizeof(m_ctr[i]));
m_ctr[i].chan = i;
}
}
RadioDevice(InterfaceType type, const struct trx_cfg *cfg)
: tx_sps(cfg->tx_sps), rx_sps(cfg->rx_sps), iface(type), chans(cfg->num_chans), lo_offset(cfg->offset),
m_ctr(chans), cfg(cfg)
{
/* Generate vector of rx/tx_path: */
for (unsigned int i = 0; i < cfg->num_chans; i++) {
rx_paths.push_back(charp2str(cfg->chans[i].rx_path));
tx_paths.push_back(charp2str(cfg->chans[i].tx_path));
}
if (iface == MULTI_ARFCN) {
LOGC(DDEV, INFO) << "Multi-ARFCN: " << chans << " logical chans -> 1 physical chans";
chans = 1;
}
for (size_t i = 0; i < chans; i++) {
memset(&m_ctr[i], 0, sizeof(m_ctr[i]));
m_ctr[i].chan = i;
}
}
bool set_antennas() {
unsigned int i;

View File

@@ -56,11 +56,10 @@ using namespace std;
static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags);
IPCDevice::IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths), sk_chan_state(chans, ipc_per_trx_sock_state()),
tx_attenuation(), tmp_state(IPC_IF_MSG_GREETING_REQ), shm(NULL), shm_dec(0),
rx_buffers(chans), started(false), tx_gains(chans), rx_gains(chans)
IPCDevice::IPCDevice(InterfaceType iface, const struct trx_cfg *cfg)
: RadioDevice(iface, cfg), sk_chan_state(chans, ipc_per_trx_sock_state()), tx_attenuation(),
tmp_state(IPC_IF_MSG_GREETING_REQ), shm(NULL), shm_dec(0), rx_buffers(chans), started(false), tx_gains(chans),
rx_gains(chans)
{
LOGC(DDEV, INFO) << "creating IPC device...";
@@ -537,9 +536,9 @@ void IPCDevice::ipc_sock_close(struct ipc_per_trx_sock_state *state)
LOGP(DDEV, LOGL_NOTICE, "IPC socket has LOST connection\n");
osmo_fd_unregister(bfd);
close(bfd->fd);
bfd->fd = -1;
osmo_fd_unregister(bfd);
/* flush the queue */
while (!llist_empty(&state->upqueue)) {
@@ -771,11 +770,12 @@ static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags)
return rc;
}
int IPCDevice::open(const std::string &args, int ref, bool swap_channels)
int IPCDevice::open()
{
std::string k, v;
std::string::size_type keyend;
int rc;
std::string args(cfg->dev_args);
if ((keyend = args.find('=')) != std::string::npos) {
k = args.substr(0, keyend++);
@@ -810,7 +810,7 @@ int IPCDevice::open(const std::string &args, int ref, bool swap_channels)
while (tmp_state != IPC_IF_MSG_INFO_CNF)
osmo_select_main(0);
ipc_tx_open_req(&master_sk_state, chans, ref);
ipc_tx_open_req(&master_sk_state, chans, cfg->clock_ref);
/* Wait until confirmation is recieved */
while (tmp_state != IPC_IF_MSG_OPEN_CNF)
osmo_select_main(0);
@@ -1259,16 +1259,15 @@ bool IPCDevice::setRxFreq(double wFreq, size_t chan)
return send_chan_wait_rsp(chan, msg, IPC_IF_MSG_SETFREQ_CNF);
}
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
{
if (tx_sps != rx_sps) {
if (cfg->tx_sps != cfg->rx_sps) {
LOGC(DDEV, ERROR) << "IPC Requires tx_sps == rx_sps";
return NULL;
}
if (lo_offset != 0.0) {
if (cfg->offset != 0.0) {
LOGC(DDEV, ERROR) << "IPC doesn't support lo_offset";
return NULL;
}
return new IPCDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
return new IPCDevice(type, cfg);
}

View File

@@ -115,12 +115,11 @@ class IPCDevice : public RadioDevice {
int ipc_chan_sock_write(osmo_fd *bfd);
/** Object constructor */
IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths);
IPCDevice(InterfaceType iface, const struct trx_cfg *cfg);
virtual ~IPCDevice() override;
/** Instantiate the IPC */
virtual int open(const std::string &args, int ref, bool swap_channels) override;
virtual int open() override;
/** Start the IPC */
virtual bool start() override;

View File

@@ -1,9 +1,8 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
AM_LDFLAGS = -lpthread -lrt
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(UHD_CFLAGS)
AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(UHD_CFLAGS)
noinst_HEADERS = IPCDevice.h shm.h ipc_shm.h ipc_chan.h ipc_sock.h
@@ -14,22 +13,22 @@ endif
noinst_LTLIBRARIES = libdevice.la
libdevice_la_SOURCES = IPCDevice.cpp shm.c ipc_shm.c ipc_chan.c ipc_sock.c
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
libdevice_la_CXXFLAGS = $(AM_CXXFLAGS) -DIPCMAGIC
libdevice_la_CPPFLAGS = $(AM_CPPFLAGS) -DIPCMAGIC
libdevice_la_LIBADD = \
$(top_builddir)/Transceiver52M/device/common/libdevice_common.la \
-lpthread \
-lrt \
$(NULL)
if DEVICE_UHD
bin_PROGRAMS = ipc-driver-test
#ipc_driver_test_SHORTNAME = drvt
ipc_driver_test_SOURCES = ipc-driver-test.c uhdwrap.cpp ipc_shm.c ipc_chan.c ipc_sock.c ../uhd/UHDDevice.cpp
ipc_driver_test_SOURCES = ipc-driver-test.c uhdwrap.cpp ../uhd/UHDDevice.cpp
ipc_driver_test_LDADD = \
shm.lo \
$(top_builddir)/Transceiver52M/device/common/libdevice_common.la \
$(COMMON_LA)
libdevice.la \
$(COMMON_LA) \
$(LIBOSMOCORE_LIBS) \
$(UHD_LIBS) \
$(NULL)
ipc_driver_test_CXXFLAGS = $(AM_CXXFLAGS) $(UHD_CFLAGS)
ipc_driver_test_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
ipc_driver_test_CFLAGS = $(AM_CFLAGS) $(UHD_CFLAGS)
ipc_driver_test_LDFLAGS = $(AM_LDFLAGS) $(UHD_LIBS)
endif

View File

@@ -97,9 +97,9 @@ void ipc_sock_close(struct ipc_sock_state *state)
ipc_exit_requested = 1;
osmo_fd_unregister(bfd);
close(bfd->fd);
bfd->fd = -1;
osmo_fd_unregister(bfd);
/* re-enable the generation of ACCEPT for new connections */
osmo_fd_read_enable(&state->listen_bfd);

View File

@@ -35,9 +35,12 @@ extern "C" {
#include "Threads.h"
#include "Utils.h"
int uhd_wrap::open(const std::string &args, int ref, bool swap_channels)
// no vty source for cfg params here, so we have to build our own
static struct trx_cfg actual_cfg = {};
int uhd_wrap::open()
{
int rv = uhd_device::open(args, ref, swap_channels);
int rv = uhd_device::open();
samps_per_buff_rx = rx_stream->get_max_num_samps();
samps_per_buff_tx = tx_stream->get_max_num_samps();
channel_count = usrp_dev->get_rx_num_channels();
@@ -84,36 +87,33 @@ int uhd_wrap::wrap_read(TIMESTAMP *timestamp)
extern "C" void *uhdwrap_open(struct ipc_sk_if_open_req *open_req)
{
unsigned int rx_sps, tx_sps;
actual_cfg.num_chans = open_req->num_chans;
actual_cfg.swap_channels = false;
/* FIXME: this is actually the sps value, not the sample rate!
* sample rate is looked up according to the sps rate by uhd backend */
actual_cfg.rx_sps = open_req->rx_sample_freq_num / open_req->rx_sample_freq_den;
actual_cfg.tx_sps = open_req->tx_sample_freq_num / open_req->tx_sample_freq_den;
/* FIXME: dev arg string* */
/* FIXME: rx frontend bw? */
/* FIXME: tx frontend bw? */
ReferenceType cref;
switch (open_req->clockref) {
case FEATURE_MASK_CLOCKREF_EXTERNAL:
cref = ReferenceType::REF_EXTERNAL;
actual_cfg.clock_ref = ReferenceType::REF_EXTERNAL;
break;
case FEATURE_MASK_CLOCKREF_INTERNAL:
default:
cref = ReferenceType::REF_INTERNAL;
actual_cfg.clock_ref = ReferenceType::REF_INTERNAL;
break;
}
std::vector<std::string> tx_paths;
std::vector<std::string> rx_paths;
for (unsigned int i = 0; i < open_req->num_chans; i++) {
tx_paths.push_back(open_req->chan_info[i].tx_path);
rx_paths.push_back(open_req->chan_info[i].rx_path);
actual_cfg.chans[i].rx_path = open_req->chan_info[i].tx_path;
actual_cfg.chans[i].tx_path = open_req->chan_info[i].rx_path;
}
/* FIXME: this is actually the sps value, not the sample rate!
* sample rate is looked up according to the sps rate by uhd backend */
rx_sps = open_req->rx_sample_freq_num / open_req->rx_sample_freq_den;
tx_sps = open_req->tx_sample_freq_num / open_req->tx_sample_freq_den;
uhd_wrap *uhd_wrap_dev =
new uhd_wrap(tx_sps, rx_sps, RadioDevice::NORMAL, open_req->num_chans, 0.0, tx_paths, rx_paths);
uhd_wrap_dev->open("", cref, false);
uhd_wrap *uhd_wrap_dev = new uhd_wrap(RadioDevice::NORMAL, &actual_cfg);
uhd_wrap_dev->open();
return uhd_wrap_dev;
}

View File

@@ -41,7 +41,7 @@ class uhd_wrap : public uhd_device {
// void ipc_sock_close() override {};
int wrap_read(TIMESTAMP *timestamp);
virtual int open(const std::string &args, int ref, bool swap_channels) override;
virtual int open() override;
// bool start() override;
// bool stop() override;

View File

@@ -52,39 +52,16 @@ extern "C" {
#define LMS_DEV_SDR_MINI_PREFIX_NAME "LimeSDR-Mini"
#define LMS_DEV_NET_MICRO_PREFIX_NAME "LimeNET-Micro"
/* Device parameter descriptor */
struct dev_desc {
/* Does LimeSuite allow switching the clock source for this device?
* LimeSDR-Mini does not have switches but needs soldering to select
* external/internal clock. Any call to LMS_SetClockFreq() will fail.
*/
bool clock_src_switchable;
/* Does LimeSuite allow using REF_INTERNAL for this device?
* LimeNET-Micro does not like selecting internal clock
*/
bool clock_src_int_usable;
/* Sample rate coef (without having TX/RX samples per symbol into account) */
double rate;
/* Sample rate coef (without having TX/RX samples per symbol into account), if multi-arfcn is enabled */
double rate_multiarfcn;
/* Coefficient multiplied by TX sample rate in order to shift Tx time */
double ts_offset_coef;
/* Coefficient multiplied by TX sample rate in order to shift Tx time, if multi-arfcn is enabled */
double ts_offset_coef_multiarfcn;
/* Device Name Prefix as presented by LimeSuite API LMS_GetDeviceInfo() */
std::string name_prefix;
};
static const std::map<enum lms_dev_type, struct dev_desc> dev_param_map {
static const dev_map_t dev_param_map {
{ LMS_DEV_SDR_USB, { true, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_SDR_USB_PREFIX_NAME } },
{ LMS_DEV_SDR_MINI, { false, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 8.2e-5, LMS_DEV_SDR_MINI_PREFIX_NAME } },
{ LMS_DEV_NET_MICRO, { true, false, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_NET_MICRO_PREFIX_NAME } },
{ LMS_DEV_UNKNOWN, { true, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, "UNKNOWN" } },
};
typedef std::tuple<lms_dev_type, enum gsm_band> dev_band_key;
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
static const power_map_t dev_band_nom_power_param_map {
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_850), { 73.0, 11.2, -6.0 } },
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_900), { 73.0, 10.8, -6.0 } },
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_1800), { 65.0, -3.5, -17.0 } }, /* FIXME: OS#4583: 1800Mhz is failing above TxGain=65, which is around -3.5dBm (already < 0 dBm) */
@@ -122,8 +99,8 @@ static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
enum lms_dev_type dev_type = it->first;
struct dev_desc desc = it->second;
if (strncmp(device_info->deviceName, desc.name_prefix.c_str(), desc.name_prefix.length()) == 0) {
LOGC(DDEV, INFO) << "Device identified as " << desc.name_prefix;
if (strncmp(device_info->deviceName, desc.desc_str.c_str(), desc.desc_str.length()) == 0) {
LOGC(DDEV, INFO) << "Device identified as " << desc.desc_str;
return dev_type;
}
it++;
@@ -131,12 +108,10 @@ static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
return LMS_DEV_UNKNOWN;
}
LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
m_lms_dev(NULL), started(false), band_ass_curr_sess(false), band((enum gsm_band)0),
m_dev_type(LMS_DEV_UNKNOWN)
LMSDevice::LMSDevice(InterfaceType iface, const struct trx_cfg *cfg)
: RadioDevice(iface, cfg),
band_manager(m_dev_type, dev_band_nom_power_param_map, dev_param_map, {LMS_DEV_SDR_USB, GSM_BAND_850}), m_lms_dev(NULL),
started(false), m_dev_type(LMS_DEV_UNKNOWN)
{
LOGC(DDEV, INFO) << "creating LMS device...";
@@ -223,48 +198,7 @@ int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::str
return -1;
}
void LMSDevice::assign_band_desc(enum gsm_band req_band)
{
dev_band_map_it it;
it = dev_band_nom_power_param_map.find(dev_band_key(m_dev_type, req_band));
if (it == dev_band_nom_power_param_map.end()) {
dev_desc desc = dev_param_map.at(m_dev_type);
LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device "
<< desc.name_prefix << " on band " << gsm_band_name(req_band)
<< ", using LimeSDR-USB ones as fallback";
it = dev_band_nom_power_param_map.find(dev_band_key(LMS_DEV_SDR_USB, req_band));
}
OSMO_ASSERT(it != dev_band_nom_power_param_map.end());
band_desc = it->second;
}
bool LMSDevice::set_band(enum gsm_band req_band)
{
if (band_ass_curr_sess && req_band != band) {
LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
<< " different from previous band " << gsm_band_name(band);
return false;
}
if (req_band != band) {
band = req_band;
assign_band_desc(band);
}
band_ass_curr_sess = true;
return true;
}
void LMSDevice::get_dev_band_desc(dev_band_desc& desc)
{
if (band == 0) {
LOGC(DDEV, ERROR) << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
assign_band_desc(GSM_BAND_900);
}
desc = band_desc;
}
int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
int LMSDevice::open()
{
lms_info_str_t* info_list;
lms_range_t range_sr;
@@ -292,9 +226,9 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
for (i = 0; i < n; i++)
LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i];
dev_id = info_list_find(info_list, n, args);
dev_id = info_list_find(info_list, n, cfg->dev_args);
if (dev_id == -1) {
LOGC(DDEV, ERROR) << "No LMS device found with address '" << args << "'";
LOGC(DDEV, ERROR) << "No LMS device found with address '" << cfg->dev_args << "'";
delete[] info_list;
return -1;
}
@@ -311,14 +245,15 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
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 ((ref != REF_EXTERNAL) && (ref != REF_INTERNAL)){
if ((cfg->clock_ref != REF_EXTERNAL) && (cfg->clock_ref != REF_INTERNAL)) {
LOGC(DDEV, ERROR) << "Invalid reference type";
goto out_close;
}
/* if reference clock is external, setup must happen _before_ calling LMS_Init */
if (ref == REF_EXTERNAL) {
if (cfg->clock_ref == REF_EXTERNAL) {
LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz";
/* FIXME: Assume an external 10 MHz reference clock. make
external reference frequency configurable */
@@ -333,7 +268,7 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
}
/* if reference clock is internal, setup must happen _after_ calling LMS_Init */
if (ref == REF_INTERNAL) {
if (cfg->clock_ref == REF_INTERNAL) {
LOGC(DDEV, INFO) << "Setting Internal clock reference";
/* Internal freq param is not used */
if (!do_clock_src_freq(REF_INTERNAL, 0))
@@ -469,7 +404,7 @@ bool LMSDevice::stop()
LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]);
}
band_ass_curr_sess = false;
band_reset();
started = false;
return true;
@@ -486,8 +421,8 @@ bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
break;
case REF_INTERNAL:
if (!dev_desc.clock_src_int_usable) {
LOGC(DDEV, ERROR) << "Device type " << dev_desc.name_prefix
<< " doesn't support internal reference clock";
LOGC(DDEV, ERROR)
<< "Device type " << dev_desc.desc_str << " doesn't support internal reference clock";
return false;
}
/* According to lms using LMS_CLOCK_EXTREF with a
@@ -505,8 +440,8 @@ bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
if (LMS_SetClockFreq(m_lms_dev, lms_clk_id, freq) < 0)
return false;
} else {
LOGC(DDEV, INFO) << "Device type " << dev_desc.name_prefix
<< " doesn't support switching clock source through SW";
LOGC(DDEV, INFO)
<< "Device type " << dev_desc.desc_str << " doesn't support switching clock source through SW";
}
return true;
@@ -999,9 +934,6 @@ bool LMSDevice::updateAlignment(TIMESTAMP timestamp)
bool LMSDevice::setTxFreq(double wFreq, size_t chan)
{
uint16_t req_arfcn;
enum gsm_band req_band;
if (chan >= chans) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
@@ -1009,18 +941,7 @@ bool LMSDevice::setTxFreq(double wFreq, size_t chan)
LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz";
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
if (req_arfcn == 0xffff) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
return false;
}
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
<< " Hz (ARFCN " << req_arfcn << " )";
return false;
}
if (!set_band(req_band))
if (!update_band_from_freq(wFreq, chan, true))
return false;
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
@@ -1033,23 +954,9 @@ bool LMSDevice::setTxFreq(double wFreq, size_t chan)
bool LMSDevice::setRxFreq(double wFreq, size_t chan)
{
uint16_t req_arfcn;
enum gsm_band req_band;
LOGCHAN(chan, DDEV, NOTICE) << "Setting Rx Freq to " << wFreq << " Hz";
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
if (req_arfcn == 0xffff) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
return false;
}
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq
<< " Hz (ARFCN " << req_arfcn << " )";
return false;
}
if (!set_band(req_band))
if (!update_band_from_freq(wFreq, chan, false))
return false;
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) {
@@ -1060,18 +967,15 @@ bool LMSDevice::setRxFreq(double wFreq, size_t chan)
return true;
}
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector < std::string > &tx_paths,
const std::vector < std::string > &rx_paths)
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
{
if (tx_sps != rx_sps) {
LOGC(DDEV, ERROR) << "LMS Requires tx_sps == rx_sps";
if (cfg->tx_sps != cfg->rx_sps) {
LOGC(DDEV, ERROR) << "LMS requires tx_sps == rx_sps";
return NULL;
}
if (lo_offset != 0.0) {
if (cfg->offset != 0.0) {
LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset";
return NULL;
}
return new LMSDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
return new LMSDevice(type, cfg);
}

View File

@@ -18,11 +18,13 @@
#ifndef _LMS_DEVICE_H_
#define _LMS_DEVICE_H_
#include <map>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "radioDevice.h"
#include "bandmanager.h"
#include "smpl_buf.h"
#include <sys/time.h>
@@ -69,8 +71,35 @@ struct dev_band_desc {
double rxgain2rssioffset_rel; /* dB */
};
/* Device parameter descriptor */
struct dev_desc {
/* Does LimeSuite allow switching the clock source for this device?
* LimeSDR-Mini does not have switches but needs soldering to select
* external/internal clock. Any call to LMS_SetClockFreq() will fail.
*/
bool clock_src_switchable;
/* Does LimeSuite allow using REF_INTERNAL for this device?
* LimeNET-Micro does not like selecting internal clock
*/
bool clock_src_int_usable;
/* Sample rate coef (without having TX/RX samples per symbol into account) */
double rate;
/* Sample rate coef (without having TX/RX samples per symbol into account), if multi-arfcn is enabled */
double rate_multiarfcn;
/* Coefficient multiplied by TX sample rate in order to shift Tx time */
double ts_offset_coef;
/* Coefficient multiplied by TX sample rate in order to shift Tx time, if multi-arfcn is enabled */
double ts_offset_coef_multiarfcn;
/* Device Name Prefix as presented by LimeSuite API LMS_GetDeviceInfo() */
std::string desc_str;
};
using dev_band_key_t = std::tuple<lms_dev_type, gsm_band>;
using power_map_t = std::map<dev_band_key_t, dev_band_desc>;
using dev_map_t = std::map<lms_dev_type, struct dev_desc>;
/** A class to handle a LimeSuite supported device */
class LMSDevice:public RadioDevice {
class LMSDevice:public RadioDevice, public band_manager<power_map_t, dev_map_t> {
private:
lms_device_t *m_lms_dev;
@@ -87,9 +116,6 @@ private:
TIMESTAMP ts_initial, ts_offset;
std::vector<double> tx_gains, rx_gains;
bool band_ass_curr_sess; /* true if "band" was set after last POWEROFF */
enum gsm_band band;
struct dev_band_desc band_desc;
enum lms_dev_type m_dev_type;
@@ -101,29 +127,25 @@ private:
void update_stream_stats_rx(size_t chan, bool *overrun);
void update_stream_stats_tx(size_t chan, bool *underrun);
bool do_clock_src_freq(enum ReferenceType ref, double freq);
void get_dev_band_desc(dev_band_desc& desc);
bool set_band(enum gsm_band req_band);
void assign_band_desc(enum gsm_band req_band);
public:
/** Object constructor */
LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
~LMSDevice();
LMSDevice(InterfaceType iface, const struct trx_cfg *cfg);
~LMSDevice();
/** Instantiate the LMS */
int open(const std::string &args, int ref, bool swap_channels);
/** Instantiate the LMS */
int open();
/** Start the LMS */
bool start();
/** Start the LMS */
bool start();
/** Stop the LMS */
bool stop();
/** Stop the LMS */
bool stop();
enum TxWindowType getWindowType() {
return TX_WINDOW_LMS1;
}
enum TxWindowType getWindowType()
{
return TX_WINDOW_LMS1;
}
/**
Read samples from the LMS.

View File

@@ -91,19 +91,7 @@ extern "C" {
* USRP1 with timestamps is not supported by UHD.
*/
/* Device Type, Tx-SPS, Rx-SPS */
typedef std::tuple<uhd_dev_type, int, int> dev_key;
/* Device parameter descriptor */
struct dev_desc {
unsigned channels;
double mcr;
double rate;
double offset;
std::string str;
};
static const std::map<dev_key, dev_desc> dev_param_map {
static const dev_map_t dev_param_map {
{ std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } },
{ std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } },
{ std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } },
@@ -129,9 +117,7 @@ static const std::map<dev_key, dev_desc> dev_param_map {
{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
};
typedef std::tuple<uhd_dev_type, enum gsm_band> dev_band_key;
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
static const power_map_t dev_band_nom_power_param_map {
{ std::make_tuple(B200, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
{ std::make_tuple(B200, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
{ std::make_tuple(B200, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
@@ -220,15 +206,10 @@ static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
}
uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths)
: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
rx_gain_min(0.0), rx_gain_max(0.0), band_ass_curr_sess(false),
band((enum gsm_band)0), tx_spp(0), rx_spp(0),
started(false), aligned(false), drop_cnt(0),
prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
uhd_device::uhd_device(InterfaceType iface, const struct trx_cfg *cfg)
: RadioDevice(iface, cfg), band_manager(dev_band_nom_power_param_map, dev_param_map), rx_gain_min(0.0),
rx_gain_max(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), drop_cnt(0), prev_ts(0, 0),
ts_initial(0), ts_offset(0), async_event_thrd(NULL)
{
}
@@ -240,47 +221,6 @@ uhd_device::~uhd_device()
delete rx_buffers[i];
}
void uhd_device::assign_band_desc(enum gsm_band req_band)
{
dev_band_map_it it;
it = dev_band_nom_power_param_map.find(dev_band_key(dev_type, req_band));
if (it == dev_band_nom_power_param_map.end()) {
dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
LOGC(DDEV, ERROR) << "No Power parameters exist for device "
<< desc.str << " on band " << gsm_band_name(req_band)
<< ", using B210 ones as fallback";
it = dev_band_nom_power_param_map.find(dev_band_key(B210, req_band));
}
OSMO_ASSERT(it != dev_band_nom_power_param_map.end())
band_desc = it->second;
}
bool uhd_device::set_band(enum gsm_band req_band)
{
if (band_ass_curr_sess && req_band != band) {
LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
<< " different from previous band " << gsm_band_name(band);
return false;
}
if (req_band != band) {
band = req_band;
assign_band_desc(band);
}
band_ass_curr_sess = true;
return true;
}
void uhd_device::get_dev_band_desc(dev_band_desc& desc)
{
if (band == 0) {
LOGC(DDEV, ERROR) << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
assign_band_desc(GSM_BAND_900);
}
desc = band_desc;
}
void uhd_device::init_gains()
{
double tx_gain_min, tx_gain_max;
@@ -345,7 +285,7 @@ void uhd_device::set_rates()
rx_rate = usrp_dev->get_rx_rate();
ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
LOGC(DDEV, INFO) << "Rates configured for " << desc.desc_str;
}
double uhd_device::setRxGain(double db, size_t chan)
@@ -355,6 +295,9 @@ double uhd_device::setRxGain(double db, size_t chan)
return 0.0f;
}
if (cfg->overrides.ul_gain_override)
return rx_gains[chan];
usrp_dev->set_rx_gain(db, chan);
rx_gains[chan] = usrp_dev->get_rx_gain(chan);
@@ -397,6 +340,9 @@ double uhd_device::setPowerAttenuation(int atten, size_t chan) {
return 0.0f;
}
if (cfg->overrides.dl_gain_override)
return atten; // ensures caller does not apply digital attenuation
get_dev_band_desc(desc);
tx_power = desc.nom_out_tx_power - atten;
db = TxPower2TxGain(desc, tx_power);
@@ -548,7 +494,7 @@ void uhd_device::set_channels(bool swap)
}
}
int uhd_device::open(const std::string &args, int ref, bool swap_channels)
int uhd_device::open()
{
const char *refstr;
int clock_lock_attempts = 15;
@@ -564,10 +510,10 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
#endif
// Find UHD devices
uhd::device_addr_t addr(args);
uhd::device_addr_t addr(cfg->dev_args);
uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
if (dev_addrs.size() == 0) {
LOGC(DDEV, ALERT) << "No UHD devices found with address '" << args << "'";
LOGC(DDEV, ALERT) << "No UHD devices found with address '" << cfg->dev_args << "'";
return -1;
}
@@ -576,7 +522,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
try {
usrp_dev = uhd::usrp::multi_usrp::make(addr);
} catch(uhd::key_error::exception &e) {
LOGC(DDEV, ALERT) << "UHD make failed, device " << args << ", exception:\n" << e.what();
LOGC(DDEV, ALERT) << "UHD make failed, device " << cfg->dev_args << ", exception:\n" << e.what();
return -1;
}
@@ -584,14 +530,16 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
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;
}
try {
set_channels(swap_channels);
} catch (const std::exception &e) {
set_channels(cfg->swap_channels);
} catch (const std::exception &e) {
LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
return -1;
}
@@ -607,7 +555,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
rx_gains.resize(chans);
rx_buffers.resize(chans);
switch (ref) {
switch (cfg->clock_ref) {
case REF_INTERNAL:
refstr = "internal";
break;
@@ -686,6 +634,32 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
// Print configuration
LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
if (cfg->overrides.dl_freq_override) {
uhd::tune_request_t treq_tx = uhd::tune_request_t(cfg->overrides.dl_freq, 0);
auto tres = usrp_dev->set_tx_freq(treq_tx, 0);
tx_freqs[0] = usrp_dev->get_tx_freq(0);
LOGCHAN(0, DDEV, INFO) << "OVERRIDE set_freq(" << tx_freqs[0] << ", TX): " << tres.to_pp_string() << std::endl;
}
if (cfg->overrides.ul_freq_override) {
uhd::tune_request_t treq_rx = uhd::tune_request_t(cfg->overrides.ul_freq, 0);
auto tres = usrp_dev->set_rx_freq(treq_rx, 0);
rx_freqs[0] = usrp_dev->get_rx_freq(0);
LOGCHAN(0, DDEV, INFO) << "OVERRIDE set_freq(" << rx_freqs[0] << ", RX): " << tres.to_pp_string() << std::endl;
}
if (cfg->overrides.ul_gain_override) {
usrp_dev->set_rx_gain(cfg->overrides.ul_gain, 0);
rx_gains[0] = usrp_dev->get_rx_gain(0);
LOGCHAN(0, DDEV, INFO) << " OVERRIDE RX gain:" << rx_gains[0] << std::endl;
}
if (cfg->overrides.dl_gain_override) {
usrp_dev->set_tx_gain(cfg->overrides.dl_gain, 0);
tx_gains[0] = usrp_dev->get_tx_gain(0);
LOGCHAN(0, DDEV, INFO) << " OVERRIDE TX gain:" << tx_gains[0] << std::endl;
}
if (iface == MULTI_ARFCN)
return MULTI_ARFCN;
@@ -796,7 +770,7 @@ bool uhd_device::stop()
for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i]->reset();
band_ass_curr_sess = false;
band_reset();
started = false;
return true;
@@ -1036,17 +1010,22 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
{
std::vector<double> freqs;
uhd::tune_result_t tres;
std::string str_dir = tx ? "Tx" : "Rx";
if (cfg->overrides.dl_freq_override || cfg->overrides.ul_freq_override)
return true;
if (!update_band_from_freq(freq, chan, tx))
return false;
uhd::tune_request_t treq = select_freq(freq, chan, tx);
std::string str_dir;
if (tx) {
tres = usrp_dev->set_tx_freq(treq, chan);
tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
str_dir = "Tx";
} else {
tres = usrp_dev->set_rx_freq(treq, chan);
rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
str_dir = "Rx";
}
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
@@ -1076,59 +1055,20 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
bool uhd_device::setTxFreq(double wFreq, size_t chan)
{
uint16_t req_arfcn;
enum gsm_band req_band;
if (chan >= tx_freqs.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
}
ScopedLock lock(tune_lock);
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
if (req_arfcn == 0xffff) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
return false;
}
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
<< " Hz (ARFCN " << req_arfcn << " )";
return false;
}
if (!set_band(req_band))
return false;
if (!set_freq(wFreq, chan, true))
return false;
return true;
return set_freq(wFreq, chan, true);
}
bool uhd_device::setRxFreq(double wFreq, size_t chan)
{
uint16_t req_arfcn;
enum gsm_band req_band;
if (chan >= rx_freqs.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
}
ScopedLock lock(tune_lock);
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
if (req_arfcn == 0xffff) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
return false;
}
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq
<< " Hz (ARFCN " << req_arfcn << " )";
return false;
}
if (!set_band(req_band))
return false;
return set_freq(wFreq, chan, false);
}
@@ -1378,11 +1318,8 @@ std::string uhd_device::str_code(uhd::async_metadata_t metadata)
}
#ifndef IPCMAGIC
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths)
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
{
return new uhd_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
return new uhd_device(type, cfg);
}
#endif

View File

@@ -30,6 +30,7 @@
#include "config.h"
#endif
#include "bandmanager.h"
#include "radioDevice.h"
#include "smpl_buf.h"
@@ -71,6 +72,19 @@ struct dev_band_desc {
double rxgain2rssioffset_rel; /* dB */
};
struct dev_desc {
unsigned channels;
double mcr;
double rate;
double offset;
std::string desc_str;
};
using dev_key = std::tuple<uhd_dev_type, int, int>;
using dev_band_key = std::tuple<uhd_dev_type, enum gsm_band>;
using power_map_t = std::map<dev_band_key, dev_band_desc>;
using dev_map_t = std::map<dev_key, dev_desc>;
/*
uhd_device - UHD implementation of the Device interface. Timestamped samples
are sent to and received from the device. An intermediate buffer
@@ -78,19 +92,19 @@ struct dev_band_desc {
Events and errors such as underruns are reported asynchronously
by the device and received in a separate thread.
*/
class uhd_device : public RadioDevice {
class uhd_device : public RadioDevice, public band_manager<power_map_t, dev_map_t> {
public:
uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
size_t chan_num, double offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
~uhd_device();
uhd_device(InterfaceType iface, const struct trx_cfg *cfg);
~uhd_device();
int open(const std::string &args, int ref, bool swap_channels);
bool start();
bool stop();
bool restart();
enum TxWindowType getWindowType() { return tx_window; }
int open();
bool start();
bool stop();
bool restart();
enum TxWindowType getWindowType()
{
return tx_window;
}
int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun);
@@ -160,9 +174,6 @@ protected:
std::vector<double> tx_gains, rx_gains;
std::vector<double> tx_freqs, rx_freqs;
bool band_ass_curr_sess; /* true if "band" was set after last POWEROFF */
enum gsm_band band;
struct dev_band_desc band_desc;
size_t tx_spp, rx_spp;
bool started;
@@ -191,10 +202,6 @@ protected:
uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
bool set_freq(double freq, size_t chan, bool tx);
void get_dev_band_desc(dev_band_desc& desc);
bool set_band(enum gsm_band req_band);
void assign_band_desc(enum gsm_band req_band);
Thread *async_event_thrd;
Mutex tune_lock;
};

View File

@@ -60,11 +60,7 @@ const dboardConfigType dboardConfig = TXA_RXB;
const double USRPDevice::masterClockRate = 52.0e6;
USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
size_t chan_num, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths)
USRPDevice::USRPDevice(InterfaceType iface, const struct trx_cfg *cfg) : RadioDevice(iface, cfg)
{
LOGC(DDEV, INFO) << "creating USRP device...";
@@ -94,7 +90,7 @@ USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
#endif
}
int USRPDevice::open(const std::string &, int, bool)
int USRPDevice::open()
{
writeLock.unlock();
@@ -587,8 +583,7 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
{
#ifndef SWLOOPBACK
short data[] = {0x00,0x02,0x00,0x00};
uint32_t *wordPtr = (uint32_t *) data;
*wordPtr = host_to_usrp_u32(*wordPtr);
/* FIXME: big endian */
bool tmpUnderrun;
std::vector<short *> buf(1, data);
@@ -659,22 +654,19 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
bool USRPDevice::setRxFreq(double wFreq) { return true;};
#endif
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths)
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
{
if (tx_sps != rx_sps) {
LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
return NULL;
}
if (chans != 1) {
LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
return NULL;
}
if (lo_offset != 0.0) {
LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
return NULL;
}
return new USRPDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
if (cfg->tx_sps != cfg->rx_sps) {
LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
return NULL;
}
if (cfg->num_chans != 1) {
LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
return NULL;
}
if (cfg->offset != 0.0) {
LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
return NULL;
}
return new USRPDevice(type, cfg);
}

View File

@@ -104,20 +104,21 @@ private:
public:
/** Object constructor */
USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
USRPDevice(InterfaceType iface, const struct trx_cfg *cfg);
/** Instantiate the USRP */
int open(const std::string &, int, bool);
/** Instantiate the USRP */
int open();
/** Start the USRP */
bool start();
/** Start the USRP */
bool start();
/** Stop the USRP */
bool stop();
/** Stop the USRP */
bool stop();
enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
enum TxWindowType getWindowType()
{
return TX_WINDOW_USRP1;
}
/**
Read samples from the USRP.

View File

@@ -38,6 +38,7 @@
#define STEALING_BIT 1
#define N_TRAIN_BITS 26
#define N_SYNC_BITS 64
#define N_ACCESS_BITS 41
#define USEFUL_BITS 142 //(2*(DATA_BITS+STEALING_BIT) + N_TRAIN_BITS )
#define FCCH_BITS USEFUL_BITS
#define BURST_SIZE (USEFUL_BITS+2*TAIL_BITS)
@@ -73,6 +74,12 @@ static const unsigned char SYNC_BITS[] = {
0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1
};
static const unsigned char ACCESS_BITS [] = {
0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0,
0, 1, 1, 1, 1, 0, 0, 0
};
const unsigned FCCH_FRAMES[] = { 0, 10, 20, 30, 40 };
const unsigned SCH_FRAMES[] = { 1, 11, 21, 31, 41 };

View File

@@ -39,13 +39,13 @@
#include "viterbi_detector.h"
#include "grgsm_vitac.h"
//signalVector mChanResp;
gr_complex d_acc_training_seq[N_ACCESS_BITS]; ///<encoded training sequence of a RACH burst
gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a SCH burst
gr_complex d_norm_training_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS]; ///<encoded training sequences of a normal and dummy burst
const int d_chan_imp_length = CHAN_IMP_RESP_LENGTH;
void initvita() {
void initvita()
{
/**
* Prepare SCH sequence bits
*
@@ -53,59 +53,72 @@ void initvita() {
* Burst and two guard periods
* (one guard period is an arbitrary overlap)
*/
gmsk_mapper(SYNC_BITS, N_SYNC_BITS,
d_sch_training_seq, gr_complex(0.0, -1.0));
gmsk_mapper(SYNC_BITS, N_SYNC_BITS, d_sch_training_seq, gr_complex(0.0, -1.0));
for (auto &i : d_sch_training_seq)
i = conj(i);
/* ab */
gmsk_mapper(ACCESS_BITS, N_ACCESS_BITS, d_acc_training_seq, gr_complex(0.0, -1.0));
for (auto &i : d_acc_training_seq)
i = conj(i);
/* Prepare bits of training sequences */
for (int i = 0; i < TRAIN_SEQ_NUM; i++) {
/**
* If first bit of the sequence is 0
* => first symbol is 1, else -1
*/
gr_complex startpoint = train_seq[i][0] == 0 ?
gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
gmsk_mapper(train_seq[i], N_TRAIN_BITS,
d_norm_training_seq[i], startpoint);
gr_complex startpoint = train_seq[i][0] == 0 ? gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
gmsk_mapper(train_seq[i], N_TRAIN_BITS, d_norm_training_seq[i], startpoint);
for (auto &i : d_norm_training_seq[i])
i = conj(i);
}
}
MULTI_VER_TARGET_ATTR NO_UBSAN
void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
template <unsigned int burst_size>
NO_UBSAN static void detect_burst_generic(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start,
char *output_binary, int ss)
{
std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
unsigned int stop_states[2] = { 4, 12 };
gr_complex filtered_burst[BURST_SIZE];
gr_complex filtered_burst[burst_size];
gr_complex rhh[CHAN_IMP_RESP_LENGTH];
float output[BURST_SIZE];
int start_state = 3;
// if(burst_start < 0 ||burst_start > 10)
// fprintf(stderr, "bo %d\n", burst_start);
// burst_start = burst_start >= 0 ? burst_start : 0;
float output[burst_size];
int start_state = ss;
autocorrelation(chan_imp_resp, &rhh_temp[0], d_chan_imp_length * d_OSR);
for (int ii = 0; ii < d_chan_imp_length; ii++)
rhh[ii] = conj(rhh_temp[ii * d_OSR]);
mafi(&input[burst_start], BURST_SIZE, chan_imp_resp,
d_chan_imp_length * d_OSR, filtered_burst);
mafi(&input[burst_start], burst_size, chan_imp_resp, d_chan_imp_length * d_OSR, filtered_burst);
viterbi_detector(filtered_burst, BURST_SIZE, rhh,
start_state, stop_states, 2, output);
viterbi_detector(filtered_burst, burst_size, rhh, start_state, stop_states, 2, output);
for (int i = 0; i < BURST_SIZE; i++)
output_binary[i] = output[i] * -127; // pre flip bits!
for (unsigned int i = 0; i < burst_size; i++)
output_binary[i] = (char)(output[i] * -127); // pre flip bits!
}
void
gmsk_mapper(const unsigned char* input,
int nitems, gr_complex* gmsk_output, gr_complex start_point)
NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary,
int ss)
{
return detect_burst_generic<BURST_SIZE>(input, chan_imp_resp, burst_start, output_binary, ss);
}
NO_UBSAN void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary,
int ss)
{
return detect_burst_generic<8 + 41 + 36 + 3>(input, chan_imp_resp, burst_start, output_binary, ss);
}
NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
{
return detect_burst_nb(input, chan_imp_resp, burst_start, output_binary, 3);
}
NO_UBSAN void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
{
return detect_burst_ab(input, chan_imp_resp, burst_start, output_binary, 3);
}
void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output, gr_complex start_point)
{
gr_complex j = gr_complex(0.0, 1.0);
gmsk_output[0] = start_point;
@@ -122,16 +135,13 @@ gmsk_mapper(const unsigned char* input,
encoded_symbol = current_symbol * previous_symbol;
/* And do GMSK mapping */
gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0)
* gmsk_output[i - 1];
gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0) * gmsk_output[i - 1];
previous_symbol = current_symbol;
}
}
gr_complex
correlate_sequence(const gr_complex* sequence,
int length, const gr_complex* input)
gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_complex *input)
{
gr_complex result(0.0, 0.0);
@@ -142,9 +152,7 @@ correlate_sequence(const gr_complex* sequence,
}
/* Computes autocorrelation for positive arguments */
inline void
autocorrelation(const gr_complex* input,
gr_complex* out, int nitems)
inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems)
{
for (int k = nitems - 1; k >= 0; k--) {
out[k] = gr_complex(0, 0);
@@ -153,9 +161,7 @@ autocorrelation(const gr_complex* input,
}
}
inline void
mafi(const gr_complex* input, int nitems,
gr_complex* filter, int filter_length, gr_complex* output)
inline void mafi(const gr_complex *input, int nitems, gr_complex *filter, int filter_length, gr_complex *output)
{
for (int n = 0; n < nitems; n++) {
int a = n * d_OSR;
@@ -170,66 +176,45 @@ mafi(const gr_complex* input, int nitems,
}
}
int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int search_center, int search_start_pos,
int search_stop_pos, gr_complex *tseq, int tseqlen, float *corr_max)
int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int search_start_pos, int search_stop_pos,
gr_complex *tseq, int tseqlen, float *corr_max)
{
std::vector<gr_complex> correlation_buffer;
const int num_search_windows = search_stop_pos - search_start_pos;
const int power_search_window_len = d_chan_imp_length * d_OSR;
std::vector<float> window_energy_buffer;
std::vector<float> power_buffer;
std::vector<gr_complex> correlation_buffer;
for (int ii = search_start_pos; ii < search_stop_pos; ii++) {
gr_complex correlation = correlate_sequence(tseq, tseqlen, &input[ii]);
power_buffer.reserve(num_search_windows);
correlation_buffer.reserve(num_search_windows);
window_energy_buffer.reserve(num_search_windows);
for (int ii = 0; ii < num_search_windows; ii++) {
gr_complex correlation = correlate_sequence(tseq, tseqlen, &input[search_start_pos + ii]);
correlation_buffer.push_back(correlation);
power_buffer.push_back(std::pow(abs(correlation), 2));
}
int strongest_corr_nr = max_element(power_buffer.begin(), power_buffer.end()) - power_buffer.begin();
/* Compute window energies */
auto window_energy_start_offset = strongest_corr_nr - 6 * d_OSR;
window_energy_start_offset = window_energy_start_offset < 0 ? 0 : window_energy_start_offset; //can end up out of range..
auto window_energy_end_offset = strongest_corr_nr + 6 * d_OSR + d_chan_imp_length * d_OSR;
auto iter = power_buffer.begin() + window_energy_start_offset;
auto iter_end = power_buffer.begin() + window_energy_end_offset;
while (iter != iter_end) {
std::vector<float>::iterator iter_ii = iter;
bool loop_end = false;
float energy = 0;
float windowSum = 0;
int len = d_chan_imp_length * d_OSR;
for (int ii = 0; ii < len; ii++, iter_ii++) {
if (iter_ii == power_buffer.end()) {
loop_end = true;
break;
}
// first window
for (int i = 0; i < power_search_window_len; i++) {
windowSum += power_buffer[i];
}
window_energy_buffer.push_back(windowSum);
energy += (*iter_ii);
}
if (loop_end)
break;
window_energy_buffer.push_back(energy);
iter++;
// slide windows
for (int i = power_search_window_len; i < num_search_windows; i++) {
windowSum += power_buffer[i] - power_buffer[i - power_search_window_len];
window_energy_buffer.push_back(windowSum);
}
/* Calculate the strongest window number */
int strongest_window_nr = window_energy_start_offset +
max_element(window_energy_buffer.begin(), window_energy_buffer.end()) -
int strongest_window_nr = std::max_element(window_energy_buffer.begin(), window_energy_buffer.end()) -
window_energy_buffer.begin();
// auto window_search_start = window_energy_buffer.begin() + strongest_corr_nr - 5* d_OSR;
// auto window_search_end = window_energy_buffer.begin() + strongest_corr_nr + 10* d_OSR;
// window_search_end = window_search_end >= window_energy_buffer.end() ? window_energy_buffer.end() : window_search_end;
// /* Calculate the strongest window number */
// int strongest_window_nr = max_element(window_search_start, window_search_end /* - d_chan_imp_length * d_OSR*/) - window_energy_buffer.begin();
// if (strongest_window_nr < 0)
// strongest_window_nr = 0;
float max_correlation = 0;
for (int ii = 0; ii < d_chan_imp_length * d_OSR; ii++) {
for (int ii = 0; ii < power_search_window_len; ii++) {
gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
if (abs(correlation) > max_correlation)
max_correlation = abs(correlation);
@@ -242,7 +227,27 @@ int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int se
* Compute first sample position, which corresponds
* to the first sample of the impulse response
*/
return search_start_pos + strongest_window_nr - search_center * d_OSR;
return search_start_pos + strongest_window_nr;
}
/*
8 ext tail bits
41 sync seq
36 encrypted bits
3 tail bits
68.25 extended tail bits (!)
center at 8+5 (actually known tb -> known isi, start at 8?) FIXME
*/
int get_access_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int max_delay)
{
const int search_center = 8 + 5;
const int search_start_pos = (search_center - 5) * d_OSR + 1;
const int search_stop_pos = (search_center + 5 + d_chan_imp_length + max_delay) * d_OSR;
const auto tseq = &d_acc_training_seq[TRAIN_BEGINNING];
const auto tseqlen = N_ACCESS_BITS - (2 * TRAIN_BEGINNING);
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
search_center * d_OSR;
}
/*
@@ -260,8 +265,8 @@ int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, f
const int search_stop_pos = (search_center + 5 + d_chan_imp_length) * d_OSR;
const auto tseq = &d_norm_training_seq[bcc][TRAIN_BEGINNING];
const auto tseqlen = N_TRAIN_BITS - (2 * TRAIN_BEGINNING);
return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen,
corr_max);
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
search_center * d_OSR;
}
/*
@@ -281,8 +286,9 @@ int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp)
// strongest_window_nr + chan_imp_resp_center + SYNC_POS *d_OSR - 48 * d_OSR - 2 * d_OSR + 2 ;
float corr_max;
return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen,
&corr_max);
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, &corr_max) -
search_center * d_OSR;
;
}
int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max)
@@ -291,9 +297,9 @@ int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_r
const int search_center = SYNC_POS + TRAIN_BEGINNING;
const int search_start_pos = 0;
// FIXME: proper end offset
const int search_stop_pos = len - (N_SYNC_BITS*8);
const int search_stop_pos = len - (N_SYNC_BITS * 8);
auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen,
corr_max);
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
search_center * d_OSR;
}

View File

@@ -42,6 +42,10 @@
#define MULTI_VER_TARGET_ATTR_CLANGONLY
#endif
/* ancient gcc < 8 has no attribute, clang always pretends to be gcc 4 */
#if !defined(__clang__) && __GNUC__ < 8
#define NO_UBSAN __attribute__((no_sanitize_undefined))
#else
#if defined(__has_attribute)
#if __has_attribute(no_sanitize)
#define NO_UBSAN __attribute__((no_sanitize("undefined")))
@@ -49,6 +53,7 @@
#else
#define NO_UBSAN
#endif
#endif
#define SYNC_SEARCH_RANGE 30
const int d_OSR(4);
@@ -58,8 +63,11 @@ void initvita();
int process_vita_burst(gr_complex *input, int tsc, unsigned char *output_binary);
int process_vita_sc_burst(gr_complex *input, int tsc, unsigned char *output_binary, int *offset);
MULTI_VER_TARGET_ATTR_CLANGONLY
void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary);
void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary, int ss);
void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary, int ss);
void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary);
void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary);
void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output, gr_complex start_point);
gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_complex *input);
inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems);
@@ -67,6 +75,7 @@ inline void mafi(const gr_complex *input, int nitems, gr_complex *filter, int fi
int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp);
int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int bcc);
int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max);
int get_access_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int max_delay);
enum class btype { NB, SCH };
struct fdata {

View File

@@ -39,14 +39,16 @@ const size_t NUM_TRANSFERS = 16 * 2;
const int SAMPLE_SCALE_FACTOR = 15; // actually 16 but sigproc complains about clipping..
// see https://en.cppreference.com/w/cpp/language/parameter_pack "Brace-enclosed initializers" example
template <typename Arg, typename... Args> void expand_args(std::ostream &out, Arg &&arg, Args &&...args)
template <typename Arg, typename... Args>
void expand_args(std::ostream &out, Arg &&arg, Args &&...args)
{
out << '(' << std::forward<Arg>(arg);
(void)(int[]){ 0, (void((out << "," << std::forward<Args>(args))), 0)... };
out << ')' << std::endl;
}
template <class R, class... Args> using RvalFunc = R (*)(Args...);
template <class R, class... Args>
using RvalFunc = R (*)(Args...);
template <class R, class... Args>
R exec_and_check(RvalFunc<R, Args...> func, const char *fname, const char *finame, const char *funcname, int line,
@@ -67,7 +69,8 @@ R exec_and_check(RvalFunc<R, Args...> func, const char *fname, const char *finam
#pragma pack(push, 1)
using blade_sample_type = std::complex<int16_t>;
enum class blade_speed_buffer_type { HS, SS };
template <blade_speed_buffer_type T> struct blade_usb_message {
template <blade_speed_buffer_type T>
struct blade_usb_message {
uint32_t reserved;
uint64_t ts;
uint32_t meta_flags;
@@ -76,7 +79,8 @@ template <blade_speed_buffer_type T> struct blade_usb_message {
static_assert(sizeof(blade_usb_message<blade_speed_buffer_type::SS>) == 2048, "blade buffer mismatch!");
static_assert(sizeof(blade_usb_message<blade_speed_buffer_type::HS>) == 1024, "blade buffer mismatch!");
template <unsigned int SZ, blade_speed_buffer_type T> struct blade_otw_buffer {
template <unsigned int SZ, blade_speed_buffer_type T>
struct blade_otw_buffer {
static_assert((SZ >= 2 && !(SZ % 2)), "min size is 2x usb buffer!");
blade_usb_message<T> m[SZ];
int actual_samples_per_msg()
@@ -172,7 +176,8 @@ template <unsigned int SZ, blade_speed_buffer_type T> struct blade_otw_buffer {
};
#pragma pack(pop)
template <unsigned int SZ, blade_speed_buffer_type T> struct blade_otw_buffer_helper {
template <unsigned int SZ, blade_speed_buffer_type T>
struct blade_otw_buffer_helper {
static_assert((SZ >= 1024 && ((SZ & (SZ - 1)) == 0)), "only buffer size multiples of 1024 allowed!");
static blade_otw_buffer<SZ / 512, T> x;
};
@@ -181,17 +186,19 @@ using dev_buf_t = typeof(blade_otw_buffer_helper<BLADE_BUFFER_SIZE, blade_speed_
// using buf_in_use = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
using bh_fn_t = std::function<int(dev_buf_t *)>;
template <typename T> struct blade_hw {
template <typename T>
struct blade_hw {
struct bladerf *dev;
struct bladerf_stream *rx_stream;
struct bladerf_stream *tx_stream;
// using pkt2buf = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
using tx_buf_q_type = spsc_cond<BLADE_NUM_BUFFERS, dev_buf_t *, true, false>;
using tx_buf_q_type = spsc_cond_timeout<BLADE_NUM_BUFFERS, dev_buf_t *, true, false>;
const unsigned int rxFullScale, txFullScale;
const int rxtxdelay;
float rxgain, txgain;
static std::atomic<bool> stop_me_flag;
static std::atomic<bool> stop_lower_threads_flag;
double rxfreq_cache, txfreq_cache;
struct ms_trx_config {
int tx_freq;
@@ -216,7 +223,9 @@ template <typename T> struct blade_hw {
{
close_device();
}
blade_hw() : rxFullScale(2047), txFullScale(2047), rxtxdelay(-60)
blade_hw()
: rxFullScale(2047), txFullScale(2047), rxtxdelay(-60), rxgain(30), txgain(30), rxfreq_cache(0),
txfreq_cache(0)
{
}
@@ -284,13 +293,13 @@ template <typename T> struct blade_hw {
(bladerf_bandwidth *)NULL);
blade_check(bladerf_set_gain_mode, dev, BLADERF_CHANNEL_RX(0), BLADERF_GAIN_MGC);
blade_check(bladerf_set_gain, dev, BLADERF_CHANNEL_RX(0), (bladerf_gain)30);
blade_check(bladerf_set_gain, dev, BLADERF_CHANNEL_TX(0), (bladerf_gain)30);
usleep(1000);
blade_check(bladerf_enable_module, dev, BLADERF_MODULE_RX, true);
usleep(1000);
blade_check(bladerf_enable_module, dev, BLADERF_MODULE_TX, true);
setRxGain(rxgain, 0);
setTxGain(txgain, 0);
usleep(1000);
bladerf_set_stream_timeout(dev, BLADERF_TX, 10);
bladerf_set_stream_timeout(dev, BLADERF_RX, 10);
blade_check(bladerf_init_stream, &rx_stream, dev, getrxcb(rxh), &buf_mgmt.rx_samples, BLADE_NUM_BUFFERS,
BLADERF_FORMAT_SC16_Q11_META, BLADE_BUFFER_SIZE, NUM_TRANSFERS, (void *)this);
@@ -302,28 +311,33 @@ template <typename T> struct blade_hw {
buf_mgmt.bufptrqueue.spsc_push(&cur_buffer[i]);
}
setRxGain(20);
setTxGain(30);
usleep(1000);
// bladerf_set_stream_timeout(dev, BLADERF_TX, 4);
// bladerf_set_stream_timeout(dev, BLADERF_RX, 4);
return 0;
}
void actually_enable_streams()
{
blade_check(bladerf_enable_module, dev, BLADERF_MODULE_RX, true);
usleep(1000);
blade_check(bladerf_enable_module, dev, BLADERF_MODULE_TX, true);
}
bool tuneTx(double freq, size_t chan = 0)
{
if (txfreq_cache == freq)
return true;
msleep(15);
blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_TX(0), (bladerf_frequency)freq);
txfreq_cache = freq;
msleep(15);
return true;
};
bool tuneRx(double freq, size_t chan = 0)
{
if (rxfreq_cache == freq)
return true;
msleep(15);
blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_RX(0), (bladerf_frequency)freq);
rxfreq_cache = freq;
msleep(15);
return true;
};
@@ -379,7 +393,7 @@ template <typename T> struct blade_hw {
static int to_skip = 0;
dev_buf_t *rcd = (dev_buf_t *)samples;
if (stop_me_flag)
if (stop_lower_threads_flag)
return BLADERF_STREAM_SHUTDOWN;
if (to_skip < 120) // prevents weird overflows on startup
@@ -404,7 +418,7 @@ template <typename T> struct blade_hw {
if (samples) // put buffer address back into queue, ready to be reused
trx->buf_mgmt.bufptrqueue.spsc_push(&ptr);
if (stop_me_flag)
if (stop_lower_threads_flag)
return BLADERF_STREAM_SHUTDOWN;
return BLADERF_STREAM_NO_DATA;
@@ -414,8 +428,9 @@ template <typename T> struct blade_hw {
auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
{
auto fn = [this] {
int status;
status = bladerf_stream(rx_stream, BLADERF_RX_X1);
int status = 0;
if (!stop_lower_threads_flag)
status = bladerf_stream(rx_stream, BLADERF_RX_X1);
if (status < 0)
std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
@@ -426,8 +441,9 @@ template <typename T> struct blade_hw {
auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
{
auto fn = [this] {
int status;
status = bladerf_stream(tx_stream, BLADERF_TX_X1);
int status = 0;
if (!stop_lower_threads_flag)
status = bladerf_stream(tx_stream, BLADERF_TX_X1);
if (status < 0)
std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
@@ -438,9 +454,15 @@ template <typename T> struct blade_hw {
void submit_burst_ts(blade_sample_type *buffer, int len, uint64_t ts)
{
//get empty bufer from list
tx_buf_q_type::elem_t rcd;
// exit by submitting a dummy buffer to assure the libbladerf stream mutex is happy (thread!)
if (!buffer) {
bladerf_submit_stream_buffer(tx_stream, (void *)BLADERF_STREAM_SHUTDOWN, 1000);
return;
}
//get empty bufer from list
while (!buf_mgmt.bufptrqueue.spsc_pop(&rcd))
buf_mgmt.bufptrqueue.spsc_prep_pop();
assert(rcd != nullptr);

View File

@@ -29,7 +29,58 @@
namespace spsc_detail
{
template <bool block_read, bool block_write> class spsc_cond_detail {
template <bool block_read, bool block_write>
class spsc_cond_timeout_detail {
std::condition_variable cond_r, cond_w;
std::mutex lr, lw;
std::atomic_int r_flag, w_flag;
const int timeout_ms = 200;
public:
explicit spsc_cond_timeout_detail() : r_flag(0), w_flag(0)
{
}
~spsc_cond_timeout_detail()
{
}
ssize_t spsc_check_r()
{
std::unique_lock<std::mutex> lk(lr);
if (cond_r.wait_for(lk, std::chrono::milliseconds(timeout_ms), [&] { return r_flag != 0; })) {
r_flag--;
return 1;
} else {
return 0;
}
}
ssize_t spsc_check_w()
{
std::unique_lock<std::mutex> lk(lw);
if (cond_w.wait_for(lk, std::chrono::milliseconds(timeout_ms), [&] { return w_flag != 0; })) {
w_flag--;
return 1;
} else {
return 0;
}
}
void spsc_notify_r()
{
std::unique_lock<std::mutex> lk(lr);
r_flag++;
cond_r.notify_one();
}
void spsc_notify_w()
{
std::unique_lock<std::mutex> lk(lw);
w_flag++;
cond_w.notify_one();
}
};
template <bool block_read, bool block_write>
class spsc_cond_detail {
std::condition_variable cond_r, cond_w;
std::mutex lr, lw;
std::atomic_int r_flag, w_flag;
@@ -74,7 +125,8 @@ template <bool block_read, bool block_write> class spsc_cond_detail {
};
// originally designed for select loop integration
template <bool block_read, bool block_write> class spsc_efd_detail {
template <bool block_read, bool block_write>
class spsc_efd_detail {
int efd_r, efd_w; /* eventfds used to block/notify readers/writers */
public:
@@ -191,4 +243,7 @@ class spsc : public T<block_read, block_write> {
template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
class spsc_evfd : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_efd_detail> {};
template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
class spsc_cond : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_detail> {};
class spsc_cond : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_detail> {};
template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
class spsc_cond_timeout
: public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_timeout_detail> {};

View File

@@ -24,13 +24,10 @@ extern "C" {
#include <osmocom/bb/trxcon/trxcon_fsm.h>
#include <osmocom/bb/trxcon/l1ctl_server.h>
}
#include "ms_trxcon_if.h"
static struct l1ctl_server_cfg server_cfg;
static struct l1ctl_server *server = NULL;
namespace trxcon
{
extern struct trxcon_inst *g_trxcon;
}
static int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg)
{
@@ -41,9 +38,9 @@ static int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg)
static void l1ctl_conn_accept_cb(struct l1ctl_client *l1c)
{
l1c->log_prefix = talloc_strdup(l1c, trxcon::g_trxcon->log_prefix);
l1c->priv = trxcon::g_trxcon;
trxcon::g_trxcon->l2if = l1c;
l1c->log_prefix = talloc_strdup(l1c, g_trxcon->log_prefix);
l1c->priv = g_trxcon;
g_trxcon->l2if = l1c;
}
static void l1ctl_conn_close_cb(struct l1ctl_client *l1c)
@@ -56,17 +53,13 @@ static void l1ctl_conn_close_cb(struct l1ctl_client *l1c)
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_L2IF_FAILURE, NULL);
}
namespace trxcon
{
bool trxc_l1ctl_init(void *tallctx)
{
/* Start the L1CTL server */
server_cfg = (struct l1ctl_server_cfg){
/* TODO: make path configurable */
.sock_path = "/tmp/osmocom_l2",
.num_clients_max = 1,
.conn_read_cb = &l1ctl_rx_cb,
.conn_accept_cb = &l1ctl_conn_accept_cb,
.sock_path = "/tmp/osmocom_l2", .num_clients_max = 1,
.conn_read_cb = &l1ctl_rx_cb, .conn_accept_cb = &l1ctl_conn_accept_cb,
.conn_close_cb = &l1ctl_conn_close_cb,
};
@@ -76,4 +69,3 @@ bool trxc_l1ctl_init(void *tallctx)
}
return true;
}
} // namespace trxcon

View File

@@ -1,5 +1,7 @@
/*
* (C) 2016-2022 by Vadim Yanitskiy <axilirator@gmail.com>
* OsmocomBB <-> SDR connection bridge
*
* (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
*
* All Rights Reserved
*
@@ -19,48 +21,61 @@
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/bb/trxcon/logging.h>
#include <osmocom/bb/trxcon/trxcon.h>
#include <osmocom/bb/trxcon/logging.h>
static struct log_info_cat trxcon_log_info_cat[] = {
[DAPP] = {
.name = "DAPP",
.color = "\033[1;35m",
.description = "Application",
.loglevel = LOGL_NOTICE,
.enabled = 1,
.color = "\033[1;35m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DL1C] = {
.name = "DL1C",
.color = "\033[1;31m",
.description = "Layer 1 control interface",
.loglevel = LOGL_NOTICE,
.enabled = 1,
.color = "\033[1;31m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DL1D] = {
.name = "DL1D",
.color = "\033[1;31m",
.description = "Layer 1 data",
.loglevel = LOGL_NOTICE,
.enabled = 1,
.color = "\033[1;31m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DTRXC] = {
.name = "DTRXC",
.description = "Transceiver control interface",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DTRXD] = {
.name = "DTRXD",
.description = "Transceiver data interface",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DSCH] = {
.name = "DSCH",
.color = "\033[1;36m",
.description = "Scheduler management",
.loglevel = LOGL_NOTICE,
.enabled = 0,
.color = "\033[1;36m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DSCHD] = {
.name = "DSCHD",
.color = "\033[1;36m",
.description = "Scheduler data",
.loglevel = LOGL_NOTICE,
.enabled = 0,
.color = "\033[1;36m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DGPRS] = {
.name = "DGPRS",
.description = "L1 GPRS (MAC layer)",
.color = "\033[1;36m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
};
static struct log_info trxcon_log_info = {
static const struct log_info trxcon_log_info = {
.cat = trxcon_log_info_cat,
.num_cat = ARRAY_SIZE(trxcon_log_info_cat),
};
@@ -71,11 +86,13 @@ static const int trxcon_log_cfg[] = {
[TRXCON_LOGC_L1D] = DL1D,
[TRXCON_LOGC_SCHC] = DSCH,
[TRXCON_LOGC_SCHD] = DSCHD,
[TRXCON_LOGC_GPRS] = DGPRS,
};
void trxc_log_init(void *tallctx)
{
osmo_init_logging2(tallctx, &trxcon_log_info);
log_target_file_switch_to_wqueue(osmo_stderr_target);
trxcon_set_log_cfg(&trxcon_log_cfg[0], ARRAY_SIZE(trxcon_log_cfg));
}

View File

@@ -29,16 +29,10 @@
#include <thread>
#include <fstream>
#include "sigProcLib.h"
#include "ms.h"
#include "ms_rx_burst.h"
#include "grgsm_vitac/grgsm_vitac.h"
extern "C" {
#include "sch.h"
#include "convolve.h"
#include "convert.h"
}
dummylog ms_trx::dummy_log;
@@ -49,199 +43,13 @@ const int offset_start = -15;
static int offset_ctr = 0;
#endif
template <> std::atomic<bool> ms_trx::base::stop_me_flag(false);
void tx_test(ms_trx *t, ts_hitter_q_t *q, unsigned int *tsc)
{
sched_param sch_params;
sch_params.sched_priority = sched_get_priority_max(SCHED_FIFO);
pthread_setschedparam(pthread_self(), SCHED_FIFO, &sch_params);
auto burst = genRandAccessBurst(0, 4, 0);
scaleVector(*burst, t->txFullScale * 0.7);
// float -> int16
blade_sample_type burst_buf[burst->size()];
convert_and_scale<int16_t, float>(burst_buf, burst->begin(), burst->size() * 2, 1);
while (1) {
GSM::Time target;
while (!q->spsc_pop(&target)) {
q->spsc_prep_pop();
}
std::cerr << std::endl << "\x1B[32m hitting " << target.FN() << "\033[0m" << std::endl;
int timing_advance = 0;
int64_t now_ts;
GSM::Time now_time;
target.incTN(3); // ul dl offset
int target_fn = target.FN();
int target_tn = target.TN();
t->timekeeper.get_both(&now_time, &now_ts);
auto diff_fn = GSM::FNDelta(target_fn, now_time.FN());
int diff_tn = (target_tn - (int)now_time.TN()) % 8;
auto tosend = GSM::Time(diff_fn, 0);
if (diff_tn > 0)
tosend.incTN(diff_tn);
else if (diff_tn < 0)
tosend.decTN(-diff_tn);
// in thory fn equal and tn+3 equal is also a problem...
if (diff_fn < 0 || (diff_fn == 0 && (now_time.TN() - target_tn < 1))) {
std::cerr << "## TX too late?! fn DIFF:" << diff_fn << " tn LOCAL: " << now_time.TN()
<< " tn OTHER: " << target_tn << std::endl;
return;
}
auto check = now_time + tosend;
int64_t send_ts =
now_ts + tosend.FN() * 8 * ONE_TS_BURST_LEN + tosend.TN() * ONE_TS_BURST_LEN - timing_advance;
#ifdef DBGXX
std::cerr << "## fn DIFF: " << diff_fn << " ## tn DIFF: " << diff_tn << " tn LOCAL: " << now_time.TN()
<< " tn OTHER: " << target_tn << " tndiff" << diff_tn << " tosend:" << tosend.FN() << ":"
<< tosend.TN() << " calc: " << check.FN() << ":" << check.TN() << " target: " << target.FN()
<< ":" << target.TN() << " ts now: " << now_ts << " target ts:" << send_ts << std::endl;
#endif
unsigned int pad = 4 * 25;
blade_sample_type buf2[burst->size() + pad];
std::fill(buf2, buf2 + pad, 0);
memcpy(&buf2[pad], burst_buf, burst->size() * sizeof(blade_sample_type));
assert(target.FN() == check.FN());
assert(target.TN() == check.TN());
assert(target.FN() % 51 == 21);
#ifdef DBGXX
auto this_offset = offset_start + (offset_ctr++ % offsetrange);
std::cerr << "-- O " << this_offset << std::endl;
send_ts = now_ts - timing_advance +
((target.FN() * 8 + (int)target.TN()) - (now_time.FN() * 8 + (int)now_time.TN())) *
ONE_TS_BURST_LEN;
#endif
t->submit_burst_ts(buf2, burst->size() + pad, send_ts - pad);
#ifdef DBGXX
signalVector test(burst->size() + pad);
convert_and_scale<float, int16_t>(test.begin(), buf2, burst->size() * 2 + pad, 1.f / float(scale));
estim_burst_params ebp;
auto det = detectAnyBurst(test, 0, 4, 4, CorrType::RACH, 40, &ebp);
if (det > 0)
std::cerr << "## Y " << ebp.toa << std::endl;
else
std::cerr << "## NOOOOOOOOO " << ebp.toa << std::endl;
#endif
}
}
#ifdef SYNCTHINGONLY
template <typename A> auto parsec(std::vector<std::string> &v, A &itr, std::string arg, bool *rv)
{
if (*itr == arg) {
*rv = true;
return true;
}
return false;
}
template <typename A, typename B, typename C>
bool parsec(std::vector<std::string> &v, A &itr, std::string arg, B f, C *rv)
{
if (*itr == arg) {
itr++;
if (itr != v.end()) {
*rv = f(itr->c_str());
return true;
}
}
return false;
}
template <typename A> bool parsec(std::vector<std::string> &v, A &itr, std::string arg, int scale, int *rv)
{
return parsec(
v, itr, arg, [scale](const char *v) -> auto{ return atoi(v) * scale; }, rv);
}
template <typename A> bool parsec(std::vector<std::string> &v, A &itr, std::string arg, int scale, unsigned int *rv)
{
return parsec(
v, itr, arg, [scale](const char *v) -> auto{ return atoi(v) * scale; }, rv);
}
int main(int argc, char *argv[])
{
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(2, &cpuset);
auto rv = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
if (rv < 0) {
std::cerr << "affinity: errreur! " << std::strerror(errno);
return 0;
}
unsigned int default_tx_freq(881000 * 1000), default_rx_freq(926000 * 1000);
unsigned int grx = 20, gtx = 20;
bool tx_flag = false;
pthread_setname_np(pthread_self(), "main");
convolve_init();
convert_init();
sigProcLibSetup();
initvita();
int status = 0;
auto trx = new ms_trx();
trx->do_auto_gain = true;
std::vector<std::string> args(argv + 1, argv + argc);
for (auto i = args.begin(); i != args.end(); ++i) {
parsec(args, i, "-r", 1000, &default_rx_freq);
parsec(args, i, "-t", 1000, &default_tx_freq);
parsec(args, i, "-gr", 1, &grx);
parsec(args, i, "-gt", 1, &gtx);
parsec(args, i, "-tx", &tx_flag);
}
std::cerr << "usage: " << argv[0] << " <rxfreq in khz, i.e. 926000> [txfreq in khz, i.e. 881000] [TX]"
<< std::endl
<< "rx" << (argc == 1 ? " (default) " : " ") << default_rx_freq << "hz, tx " << default_tx_freq
<< "hz" << std::endl
<< "gain rx " << grx << " gain tx " << gtx << std::endl
<< (tx_flag ? "##!!## RACH TX ACTIVE ##!!##" : "-- no rach tx --") << std::endl;
status = trx->init_dev_and_streams();
if (status < 0)
return status;
trx->tuneRx(default_rx_freq);
trx->tuneTx(default_tx_freq);
trx->setRxGain(grx);
trx->setTxGain(gtx);
if (status == 0) {
// FIXME: hacks! needs exit flag for detached threads!
std::thread(rcv_bursts_test, &trx->rxqueue, &trx->mTSC, trx->rxFullScale).detach();
if (tx_flag)
std::thread(tx_test, trx, &trx->ts_hitter_q, &trx->mTSC).detach();
trx->start();
do {
sleep(1);
} while (1);
trx->stop_threads();
}
delete trx;
return status;
}
#endif
template <>
std::atomic<bool> ms_trx::base::stop_lower_threads_flag(false);
int ms_trx::init_dev_and_streams()
{
int status = 0;
status = base::init_device(rx_bh(), tx_bh());
status = init_device(rx_bh(), tx_bh());
if (status < 0) {
std::cerr << "failed to init dev!" << std::endl;
return -1;
@@ -270,16 +78,20 @@ bh_fn_t ms_trx::tx_bh()
};
}
void ms_trx::start()
void ms_trx::start_lower_ms()
{
if (stop_lower_threads_flag)
return;
auto fn = get_rx_burst_handler_fn(rx_bh());
rx_task = std::thread(fn);
set_name_aff_sched(rx_task.native_handle(), "rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2);
lower_rx_task = std::thread(fn);
set_name_aff_sched(lower_rx_task.native_handle(), sched_params::thread_names::RXRUN);
usleep(1000);
auto fn2 = get_tx_burst_handler_fn(tx_bh());
tx_task = std::thread(fn2);
set_name_aff_sched(tx_task.native_handle(), "txrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1);
lower_tx_task = std::thread(fn2);
set_name_aff_sched(lower_tx_task.native_handle(), sched_params::thread_names::TXRUN);
actually_enable_streams();
}
void ms_trx::set_upper_ready(bool is_ready)
@@ -289,11 +101,14 @@ void ms_trx::set_upper_ready(bool is_ready)
void ms_trx::stop_threads()
{
std::cerr << "killing threads...\r\n" << std::endl;
stop_me_flag = true;
std::cerr << "killing threads..." << std::endl;
stop_lower_threads_flag = true;
close_device();
rx_task.join();
tx_task.join();
std::cerr << "dev closed..." << std::endl;
lower_rx_task.join();
std::cerr << "L rx dead..." << std::endl;
lower_tx_task.join();
std::cerr << "L tx dead..." << std::endl;
}
void ms_trx::submit_burst(blade_sample_type *buffer, int len, GSM::Time target)
@@ -321,15 +136,16 @@ void ms_trx::submit_burst(blade_sample_type *buffer, int len, GSM::Time target)
return;
}
auto check = now_time + tosend;
int64_t send_ts = now_ts + tosend.FN() * 8 * ONE_TS_BURST_LEN + tosend.TN() * ONE_TS_BURST_LEN - timing_advance;
#ifdef DBGXX
auto check = now_time + tosend;
std::cerr << "## fn DIFF: " << diff_fn << " ## tn DIFF: " << diff_tn << " tn LOCAL/OTHER: " << now_time.TN()
<< "/" << target_tn << " tndiff" << diff_tn << " tosend:" << tosend.FN() << ":" << tosend.TN()
<< " check: " << check.FN() << ":" << check.TN() << " target: " << target.FN() << ":" << target.TN()
<< " ts now: " << now_ts << " target ts:" << send_ts << std::endl;
#endif
#if 1
#if 0
auto check = now_time + tosend;
unsigned int pad = 4 * 4;
blade_sample_type buf2[len + pad];
std::fill(buf2, buf2 + pad, 0);

View File

@@ -38,30 +38,88 @@
#error wat? no device..
#endif
#include "Complex.h"
#include "GSMCommon.h"
#include "itrq.h"
#include "threadpool.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> void clamp_array(T *start2, unsigned int len, T max)
template <typename T>
void clamp_array(T *start2, unsigned int len, T max)
{
for (int i = 0; i < len; i++) {
for (unsigned int i = 0; i < len; i++) {
const T t1 = start2[i] < -max ? -max : start2[i];
const T t2 = t1 > max ? max : t1;
start2[i] = t2;
}
}
template <typename DST_T, typename SRC_T, typename ST>
void convert_and_scale(void *dst, void *src, unsigned int src_len, ST scale)
namespace cvt_internal
{
template <typename SRC_T, typename ST>
void convert_and_scale_i(float *dst, const SRC_T *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;
dst[i] = static_cast<float>(src[i]) * scale;
}
template <typename DST_T, typename SRC_T> void convert_and_scale_default(void *dst, void *src, unsigned int src_len)
template <typename DST_T, typename ST>
void convert_and_scale_i(DST_T *dst, const float *src, unsigned int src_len, ST scale)
{
return convert_and_scale<DST_T, SRC_T>(dst, src, src_len, SAMPLE_SCALE_FACTOR);
for (unsigned int i = 0; i < src_len; i++)
dst[i] = static_cast<DST_T>(src[i] * scale);
}
template <typename ST>
void convert_and_scale_i(float *dst, const float *src, unsigned int src_len, ST scale)
{
for (unsigned int i = 0; i < src_len; i++)
dst[i] = src[i] * scale;
}
template <typename T>
struct is_complex : std::false_type {
using baset = T;
static const unsigned int len_mul = 1;
};
template <typename T>
struct is_complex<std::complex<T>> : std::true_type {
using baset = typename std::complex<T>::value_type;
static const unsigned int len_mul = 2;
};
template <typename T>
struct is_complex<Complex<T>> : std::true_type {
using baset = typename Complex<T>::value_type;
static const unsigned int len_mul = 2;
};
} // namespace cvt_internal
template <typename DST_T, typename SRC_T, typename ST>
void convert_and_scale(DST_T *dst, const SRC_T *src, unsigned int src_len, ST scale)
{
using vd = typename cvt_internal::is_complex<DST_T>::baset;
using vs = typename cvt_internal::is_complex<SRC_T>::baset;
return cvt_internal::convert_and_scale_i((vd *)dst, (vs *)src, src_len, scale);
}
template <typename array_t>
float normed_abs_sum(array_t *src, int len)
{
using vd = typename cvt_internal::is_complex<array_t>::baset;
auto len_mul = cvt_internal::is_complex<array_t>::len_mul;
auto ptr = reinterpret_cast<const vd *>(src);
float sum = 0;
for (unsigned int i = 0; i < len * len_mul; i++)
sum += std::abs(ptr[i]);
sum /= len * len_mul;
return sum;
}
struct one_burst {
@@ -75,7 +133,7 @@ struct one_burst {
};
};
using rx_queue_t = spsc_cond<8 * NUM_RXQ_FRAMES, one_burst, true, true>;
using rx_queue_t = spsc_cond_timeout<8 * NUM_RXQ_FRAMES, one_burst, true, false>;
enum class SCH_STATE { SEARCHING, FOUND };
@@ -159,6 +217,40 @@ 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 {
@@ -169,15 +261,12 @@ struct ms_trx : public BASET {
int timing_advance;
bool do_auto_gain;
std::thread rx_task;
std::thread tx_task;
std::thread *calcrval_task;
std::thread lower_rx_task;
std::thread lower_tx_task;
// provides bursts to upper rx thread
rx_queue_t rxqueue;
#ifdef SYNCTHINGONLY
ts_hitter_q_t ts_hitter_q;
#endif
blade_sample_type *first_sch_buf;
blade_sample_type *burst_copy_buffer;
@@ -189,18 +278,20 @@ 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;
void start();
void start_lower_ms();
std::atomic<bool> upper_is_ready;
void set_upper_ready(bool is_ready);
bool handle_sch_or_nb();
bool handle_sch(bool first = false);
bool decode_sch(float *bits, bool update_global_clock);
bool decode_sch(char *bits, bool update_global_clock);
SCH_STATE search_for_sch(dev_buf_t *rcd);
void grab_bursts(dev_buf_t *rcd);
int init_device();
int init_dev_and_streams();
void stop_threads();
void *rx_cb(ms_trx *t);
@@ -208,9 +299,15 @@ struct ms_trx : public BASET {
void maybe_update_gain(one_burst &brst);
ms_trx()
: 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]), rcv_done{ false }, sch_thread_done{ false }
: 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)
{
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()
@@ -228,11 +325,28 @@ struct ms_trx : public BASET {
timing_advance = val * 4;
}
void set_name_aff_sched(const char *name, int cpunum, int schedtype, int prio)
void set_name_aff_sched(sched_params::thread_names name)
{
set_name_aff_sched(pthread_self(), name, cpunum, schedtype, prio);
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)
{
@@ -243,18 +357,40 @@ struct ms_trx : public BASET {
CPU_ZERO(&cpuset);
CPU_SET(cpunum, &cpuset);
auto rv = pthread_setaffinity_np(h, sizeof(cpuset), &cpuset);
if (rv < 0) {
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;
rv = pthread_setschedparam(h, schedtype, &sch_params);
if (rv < 0) {
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

@@ -1,207 +0,0 @@
/*
* (C) 2022 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 "ms.h"
#include "sigProcLib.h"
#include "signalVector.h"
#include "grgsm_vitac/grgsm_vitac.h"
extern "C" {
#include "sch.h"
}
#if !defined(SYNCTHINGONLY) || !defined(NODAMNLOG)
#define DBGLG(...) ms_trx::dummy_log()
#else
#define DBGLG(...) std::cerr
#endif
#if !defined(SYNCTHINGONLY)
#define DBGLG2(...) ms_trx::dummy_log()
#else
#define DBGLG2(...) std::cerr
#endif
static bool decode_sch(float *bits, bool update_global_clock)
{
struct sch_info sch;
ubit_t info[GSM_SCH_INFO_LEN];
sbit_t data[GSM_SCH_CODED_LEN];
float_to_sbit(&bits[3], &data[0], 62, 39);
float_to_sbit(&bits[106], &data[39], 62, 39);
if (!gsm_sch_decode(info, data)) {
gsm_sch_parse(info, &sch);
DBGLG() << "SCH : Decoded values" << std::endl;
DBGLG() << " BSIC: " << sch.bsic << std::endl;
DBGLG() << " TSC: " << (sch.bsic & 0x7) << std::endl;
DBGLG() << " T1 : " << sch.t1 << std::endl;
DBGLG() << " T2 : " << sch.t2 << std::endl;
DBGLG() << " T3p : " << sch.t3p << std::endl;
DBGLG() << " FN : " << gsm_sch_to_fn(&sch) << std::endl;
return true;
}
return false;
}
static void check_rcv_fn(GSM::Time t, bool first, unsigned int &lastfn, unsigned int &fnbm)
{
if (first && t.TN() == 0) {
lastfn = t.FN();
fnbm = 1 << 0;
first = false;
}
if (!first && t.FN() != (int)lastfn) {
if (fnbm != 255)
std::cerr << "rx " << lastfn << ":" << fnbm << " " << __builtin_popcount(fnbm) << std::endl;
lastfn = t.FN();
fnbm = 1 << t.TN();
}
fnbm |= 1 << t.TN();
}
static void handle_it(one_burst &e, signalVector &burst, unsigned int tsc, int scale)
{
std::fill(burst.begin(), burst.begin() + burst.size(), 0.0);
const auto is_sch = gsm_sch_check_ts(e.gsmts.TN(), e.gsmts.FN());
const auto is_fcch = gsm_fcch_check_ts(e.gsmts.TN(), e.gsmts.FN());
if (is_fcch)
return;
if (is_sch) {
char outbin[148];
convert_and_scale_default<float, int16_t>(burst.begin(), e.burst, ONE_TS_BURST_LEN * 2);
std::stringstream dbgout;
#if 0
{
struct estim_burst_params ebp;
auto rv2 = detectSCHBurst(burst, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp);
auto bits = demodAnyBurst(burst, SCH, 4, &ebp);
// clamp_array(bits->begin(), 148, 1.5f);
for (auto &i : *bits)
i = (i > 0 ? 1 : -1);
auto rv = decode_sch(bits->begin(), false);
dbgout << "U DET@" << (rv2 ? "yes " : " ") << "Timing offset " << ebp.toa
<< " symbols, DECODE: " << (rv ? "yes" : "---") << " ";
delete bits;
}
#endif
{
convert_and_scale<float, float>(burst.begin(), burst.begin(), ONE_TS_BURST_LEN * 2,
1.f / float(scale));
std::complex<float> channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
auto ss = reinterpret_cast<std::complex<float> *>(burst.begin());
int d_c0_burst_start = get_sch_chan_imp_resp(ss, &channel_imp_resp[0]);
detect_burst(ss, &channel_imp_resp[0], d_c0_burst_start, outbin);
SoftVector bits;
bits.resize(148);
for (int i = 0; i < 148; i++) {
bits[i] = (!outbin[i]); // < 1 ? -1 : 1;
}
auto rv = decode_sch(bits.begin(), false);
dbgout << "U SCH@"
<< " " << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << d_c0_burst_start
<< " DECODE:" << (rv ? "yes" : "---") << std::endl;
}
DBGLG() << dbgout.str();
return;
}
#if 1
convert_and_scale<float, int16_t>(burst.begin(), e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(scale));
// std::cerr << "@" << tsc << " " << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << ebp.toa << " "
// << std::endl;
char outbin[148];
auto ss = reinterpret_cast<std::complex<float> *>(burst.begin());
float ncmax, dcmax;
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR], chan_imp_resp2[CHAN_IMP_RESP_LENGTH * d_OSR];
auto normal_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp[0], &ncmax, tsc);
auto dummy_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp2[0], &dcmax, TS_DUMMY);
auto is_nb = ncmax > dcmax;
DBGLG() << " U " << (is_nb ? "NB" : "DB") << "@ o nb: " << normal_burst_start << " o db: " << dummy_burst_start
<< std::endl;
if (is_nb)
detect_burst(ss, &chan_imp_resp[0], normal_burst_start, outbin);
else
detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
;
#ifdef DBGXX
// auto bits = SoftVector(148);
// for (int i = 0; i < 148; i++)
// (bits)[i] = outbin[i] < 1 ? -1 : 1;
#endif
#endif
}
void rcv_bursts_test(rx_queue_t *q, unsigned int *tsc, int scale)
{
static bool first = true;
unsigned int lastfn = 0;
unsigned int fnbm = 0;
signalVector burst(ONE_TS_BURST_LEN, 100, 100);
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(1, &cpuset);
auto rv = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
if (rv < 0) {
std::cerr << "affinity: errreur! " << std::strerror(errno);
exit(0);
}
int prio = sched_get_priority_max(SCHED_RR);
struct sched_param param;
param.sched_priority = prio;
rv = sched_setscheduler(0, SCHED_RR, &param);
if (rv < 0) {
std::cerr << "scheduler: errreur! " << std::strerror(errno);
exit(0);
}
while (1) {
one_burst e;
while (!q->spsc_pop(&e)) {
q->spsc_prep_pop();
}
check_rcv_fn(e.gsmts, first, lastfn, fnbm);
handle_it(e, burst, *tsc, scale);
#ifdef DBGXX
rv = detectSCHBurst(*burst, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp);
if (rv > 0)
std::cerr << "#" << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << ebp.toa << std::endl;
sched_yield();
#endif
}
}

View File

@@ -19,8 +19,6 @@
*
*/
#include "sigProcLib.h"
#include "signalVector.h"
#include <atomic>
#include <cassert>
#include <complex>
@@ -30,6 +28,8 @@
#include "ms.h"
#include "grgsm_vitac/grgsm_vitac.h"
#include "threadpool.h"
extern "C" {
#include "sch.h"
}
@@ -38,28 +38,29 @@ extern "C" {
#undef LOG
#endif
#if !defined(SYNCTHINGONLY) //|| !defined(NODAMNLOG)
#if !defined(NODAMNLOG)
#define DBGLG(...) ms_trx::dummy_log()
#else
#define DBGLG(...) std::cerr
#endif
#if !defined(SYNCTHINGONLY) || !defined(NODAMNLOG)
#if !defined(NODAMNLOG)
#define DBGLG2(...) ms_trx::dummy_log()
#else
#define DBGLG2(...) std::cerr
#endif
#define PRINT_Q_OVERFLOW
bool ms_trx::decode_sch(float *bits, bool update_global_clock)
bool ms_trx::decode_sch(char *bits, bool update_global_clock)
{
int fn;
struct sch_info sch;
ubit_t info[GSM_SCH_INFO_LEN];
sbit_t data[GSM_SCH_CODED_LEN];
float_to_sbit(&bits[3], &data[0], 1, 39);
float_to_sbit(&bits[106], &data[39], 1, 39);
memcpy(&data[0], &bits[3], 39);
memcpy(&data[39], &bits[106], 39);
if (!gsm_sch_decode(info, data)) {
gsm_sch_parse(info, &sch);
@@ -87,18 +88,6 @@ bool ms_trx::decode_sch(float *bits, bool update_global_clock)
// global_time_keeper.FN(fn);
// global_time_keeper.TN(0);
}
#ifdef SYNCTHINGONLY
else {
int t3 = sch.t3p * 10 + 1;
if (t3 == 11) {
// timeslot hitter attempt @ fn 21 in mf
DBGLG2() << "sch @ " << t3 << std::endl;
auto e = GSM::Time(fn, 0);
e += 10;
ts_hitter_q.spsc_push(&e);
}
}
#endif
return true;
}
@@ -113,11 +102,7 @@ void ms_trx::maybe_update_gain(one_burst &brst)
const unsigned int rx_max_cutoff = (rxFullScale * 2) / 3;
static int gain_check = 0;
static float runmean = 0;
float sum = 0;
for (auto i : brst.burst)
sum += abs(i.real()) + abs(i.imag());
sum /= ONE_TS_BURST_LEN * 2;
float sum = normed_abs_sum(&brst.burst[0], ONE_TS_BURST_LEN);
runmean = gain_check ? (runmean * (gain_check + 2) - 1 + sum) / (gain_check + 2) : sum;
if (gain_check == avgburst_num - 1) {
@@ -126,8 +111,11 @@ void ms_trx::maybe_update_gain(one_burst &brst)
gainoffset = runmean < (rxFullScale / 2 ? 2 : 1);
float newgain = runmean < rx_max_cutoff ? rxgain + gainoffset : rxgain - gainoffset;
// FIXME: gian cutoff
if (newgain != rxgain && newgain <= 60)
std::thread([this, newgain] { setRxGain(newgain); }).detach();
if (newgain != rxgain && newgain <= 60) {
auto gain_fun = [this, newgain] { setRxGain(newgain); };
worker_thread.add_task(gain_fun);
}
runmean = 0;
}
gain_check = (gain_check + 1) % avgburst_num;
@@ -150,14 +138,9 @@ bool ms_trx::handle_sch_or_nb()
handle_sch(false);
memcpy(brst.sch_bits, sch_demod_bits, sizeof(sch_demod_bits));
}
#ifndef SYNCTHINGONLY
if (upper_is_ready) { // this is blocking, so only submit if there is a reader - only if upper exists!
#endif
while (!rxqueue.spsc_push(&brst))
;
#ifndef SYNCTHINGONLY
}
#endif
while (upper_is_ready && !rxqueue.spsc_push(&brst))
;
if (do_auto_gain)
maybe_update_gain(brst);
@@ -178,27 +161,18 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
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;
convert_and_scale<float, int16_t>(which_out_buffer, which_in_buffer, buf_len * 2,
1.f / float(rxFullScale));
start = get_sch_buffer_chan_imp_resp(ss, &channel_imp_resp[0], buf_len, &max_corr);
detect_burst(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
} else {
convert_and_scale<float, int16_t>(which_out_buffer, which_in_buffer, buf_len * 2,
1.f / float(rxFullScale));
start = get_sch_chan_imp_resp(ss, &channel_imp_resp[0]);
start = start < 39 ? start : 39;
start = start > -39 ? start : -39;
detect_burst(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
}
detect_burst_nb(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
SoftVector bitss(148);
for (int i = 0; i < 148; i++) {
bitss[i] = (sch_demod_bits[i]);
}
auto sch_decode_success = decode_sch(bitss.begin(), is_first_sch_acq);
auto sch_decode_success = decode_sch(sch_demod_bits, is_first_sch_acq);
if (sch_decode_success) {
const auto ts_offset_symb = 0;
@@ -219,32 +193,38 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
return false;
}
/*
accumulates a full big buffer consisting of 8*12 timeslots, then:
either
1) adjusts gain if necessary and starts over
2) searches and finds SCH and is done
*/
SCH_STATE ms_trx::search_for_sch(dev_buf_t *rcd)
{
static unsigned int sch_pos = 0;
auto to_copy = SCH_LEN_SPS - sch_pos;
if (sch_thread_done)
return SCH_STATE::FOUND;
if (rcv_done)
return SCH_STATE::SEARCHING;
auto to_copy = SCH_LEN_SPS - sch_pos;
if (SCH_LEN_SPS == to_copy) // first time
if (sch_pos == 0) // keep first ts for time delta calc
first_sch_buf_rcv_ts = rcd->get_first_ts();
if (!to_copy) {
if (to_copy) {
auto spsmax = rcd->actual_samples_per_buffer();
if (to_copy > (unsigned int)spsmax)
sch_pos += rcd->readall(first_sch_buf + sch_pos);
else
sch_pos += rcd->read_n(first_sch_buf + sch_pos, 0, to_copy);
} else { // (!to_copy)
sch_pos = 0;
rcv_done = true;
std::thread([this] {
set_name_aff_sched("sch_search", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5);
auto ptr = reinterpret_cast<const int16_t *>(first_sch_buf);
auto sch_search_fun = [this] {
const auto target_val = rxFullScale / 8;
float sum = 0;
for (unsigned int i = 0; i < SCH_LEN_SPS * 2; i++)
sum += std::abs(ptr[i]);
sum /= SCH_LEN_SPS * 2;
float sum = normed_abs_sum(first_sch_buf, SCH_LEN_SPS);
//FIXME: arbitrary value, gain cutoff
if (sum > target_val || rxgain >= 60) // enough ?
@@ -257,16 +237,9 @@ SCH_STATE ms_trx::search_for_sch(dev_buf_t *rcd)
if (!sch_thread_done)
rcv_done = false; // retry!
return (bool)sch_thread_done;
}).detach();
};
worker_thread.add_task(sch_search_fun);
}
auto spsmax = rcd->actual_samples_per_buffer();
if (to_copy > (unsigned int)spsmax)
sch_pos += rcd->readall(first_sch_buf + sch_pos);
else
sch_pos += rcd->read_n(first_sch_buf + sch_pos, 0, to_copy);
return SCH_STATE::SEARCHING;
}

View File

@@ -0,0 +1,78 @@
/*
* (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 <atomic>
#include "ms_trxcon_if.h"
extern "C" {
#include <osmocom/bb/trxcon/trxcon.h>
#include <osmocom/bb/trxcon/l1ctl_server.h>
#include <osmocom/core/panic.h>
}
extern tx_queue_t txq;
extern cmd_queue_t cmdq_to_phy;
extern cmdr_queue_t cmdq_from_phy;
extern std::atomic<bool> g_exit_flag;
// trxcon C call(back) if
extern "C" {
int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon_phyif_burst_req *br)
{
if (br->burst_len == 0) // dummy/nope
return 0;
OSMO_ASSERT(br->burst != 0);
internal_q_tx_buf b(br);
if (!g_exit_flag)
txq.spsc_push(&b);
return 0;
}
int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon_phyif_cmd *cmd)
{
#ifdef TXDEBUG
DBGLG() << "TOP C: " << cmd2str(cmd->type) << std::endl;
#endif
if (!g_exit_flag)
cmdq_to_phy.spsc_push(cmd);
// q for resp polling happens in main loop
return 0;
}
void trxcon_phyif_close(void *phyif)
{
}
void trxcon_l1ctl_close(struct trxcon_inst *trxcon)
{
/* Avoid use-after-free: both *fi and *trxcon are children of
* the L2IF (L1CTL connection), so we need to re-parent *fi
* to NULL before calling l1ctl_client_conn_close(). */
talloc_steal(NULL, trxcon->fi);
l1ctl_client_conn_close((struct l1ctl_client *)trxcon->l2if);
}
int trxcon_l1ctl_send(struct trxcon_inst *trxcon, struct msgb *msg)
{
struct l1ctl_client *l1c = (struct l1ctl_client *)trxcon->l2if;
return l1ctl_client_send(l1c, msg);
}
}

View File

@@ -1,6 +1,6 @@
#pragma once
/*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
@@ -21,5 +21,22 @@
*/
#include "ms.h"
extern "C" {
#include <osmocom/bb/trxcon/phyif.h>
}
void rcv_bursts_test(rx_queue_t *q, unsigned int *tsc, int scale);
extern struct trxcon_inst *g_trxcon;
struct internal_q_tx_buf {
trxcon_phyif_burst_req r;
uint8_t buf[148];
internal_q_tx_buf() = default;
internal_q_tx_buf(const internal_q_tx_buf &) = delete;
internal_q_tx_buf &operator=(const internal_q_tx_buf &) = default;
internal_q_tx_buf(const struct trxcon_phyif_burst_req *br) : r(*br)
{
memcpy(buf, (void *)br->burst, br->burst_len);
}
};
using tx_queue_t = spsc_cond<8 * 1, internal_q_tx_buf, true, false>;
using cmd_queue_t = spsc_cond_timeout<8 * 1, trxcon_phyif_cmd, true, false>;
using cmdr_queue_t = spsc_cond<8 * 1, trxcon_phyif_rsp, false, false>;

View File

@@ -19,6 +19,7 @@
*
*/
#include <csignal>
#include "sigProcLib.h"
#include "ms.h"
#include <signalVector.h>
@@ -26,64 +27,35 @@
#include <radioInterface.h>
#include <grgsm_vitac/grgsm_vitac.h>
// #define TXDEBUG
extern "C" {
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <getopt.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <time.h>
#include <fenv.h>
#include "sch.h"
#include "convolve.h"
#include "convert.h"
#include <osmocom/core/application.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/bb/trxcon/trxcon.h>
#include <osmocom/bb/trxcon/trxcon_fsm.h>
#include <osmocom/bb/trxcon/l1ctl_server.h>
extern void trxc_log_init(void *tallctx);
#ifdef LSANDEBUG
void __lsan_do_recoverable_leak_check();
#endif
}
#include "ms_trxcon_if.h"
#include "ms_upper.h"
namespace trxcon
{
extern "C" {
#include <osmocom/core/fsm.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/signal.h>
#include <osmocom/core/select.h>
#include <osmocom/gsm/gsm_utils.h>
#include <osmocom/core/logging.h>
#include <osmocom/bb/trxcon/logging.h>
#include <osmocom/bb/trxcon/trxcon.h>
#include <osmocom/bb/trxcon/trxcon_fsm.h>
#include <osmocom/bb/trxcon/phyif.h>
#include <osmocom/bb/trxcon/l1ctl_server.h>
}
struct trxcon_inst *g_trxcon;
// trx_instance *trxcon_instance; // local handle
struct internal_q_tx_buf {
trxcon_phyif_burst_req r;
uint8_t buf[148];
};
using tx_queue_t = spsc_cond<8 * 1, internal_q_tx_buf, true, false>;
using cmd_queue_t = spsc_cond<8 * 1, trxcon_phyif_cmd, true, false>;
using cmdr_queue_t = spsc_cond<8 * 1, trxcon_phyif_rsp, false, false>;
static tx_queue_t txq;
static cmd_queue_t cmdq_to_phy;
static cmdr_queue_t cmdq_from_phy;
extern bool trxc_l1ctl_init(void *tallctx);
} // namespace trxcon
extern "C" void trxc_log_init(void *tallctx);
struct trxcon_inst *g_trxcon;
tx_queue_t txq;
cmd_queue_t cmdq_to_phy;
cmdr_queue_t cmdq_from_phy;
#ifdef LOG
#undef LOG
@@ -94,41 +66,62 @@ extern "C" void trxc_log_init(void *tallctx);
std::atomic<bool> g_exit_flag;
void upper_trx::stop_upper_threads()
{
g_exit_flag = true;
pthread_join(thr_control, NULL);
pthread_join(thr_tx, NULL);
}
void upper_trx::start_threads()
{
thr_control = std::thread([this] {
set_name_aff_sched("upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR));
while (!g_exit_flag) {
driveControl();
}
});
msleep(1);
thr_tx = std::thread([this] {
set_name_aff_sched("upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1);
while (!g_exit_flag) {
driveTx();
}
});
DBGLG(...) << "spawning threads.." << std::endl;
// atomic ensures data is not written to q until loop reads
start_lower_ms();
thr_control = spawn_worker_thread(
sched_params::thread_names::U_CTL,
[](void *args) -> void * {
upper_trx *t = reinterpret_cast<upper_trx *>(args);
#ifdef TXDEBUG
struct sched_param param;
int policy;
pthread_getschedparam(pthread_self(), &policy, &param);
printf("ID: %lu, CPU: %d policy = %d priority = %d\n", pthread_self(), sched_getcpu(), policy,
param.sched_priority);
#endif
std::cerr << "started U control!" << std::endl;
while (!g_exit_flag) {
t->driveControl();
}
std::cerr << "exit U control!" << std::endl;
set_name_aff_sched("upper_rx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_RR) - 5);
while (!g_exit_flag) {
// set_upper_ready(true);
driveReceiveFIFO();
trxcon::osmo_select_main(1);
return 0;
},
this);
thr_tx = spawn_worker_thread(
sched_params::thread_names::U_TX,
[](void *args) -> void * {
upper_trx *t = reinterpret_cast<upper_trx *>(args);
#ifdef TXDEBUG
struct sched_param param;
int policy;
pthread_getschedparam(pthread_self(), &policy, &param);
printf("ID: %lu, CPU: %d policy = %d priority = %d\n", pthread_self(), sched_getcpu(), policy,
param.sched_priority);
#endif
std::cerr << "started U tx!" << std::endl;
while (!g_exit_flag) {
t->driveTx();
}
std::cerr << "exit U tx!" << std::endl;
trxcon::trxcon_phyif_rsp r;
if (trxcon::cmdq_from_phy.spsc_pop(&r)) {
DBGLG() << "HAVE RESP:" << r.type << std::endl;
trxcon_phyif_handle_rsp(trxcon::g_trxcon, &r);
}
}
return 0;
},
this);
#ifdef LSANDEBUG
std::thread([this] {
set_name_aff_sched("leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10);
set_name_aff_sched(sched_params::thread_names::LEAKCHECK);
while (1) {
std::this_thread::sleep_for(std::chrono::seconds{ 5 });
@@ -138,9 +131,23 @@ void upper_trx::start_threads()
#endif
}
void upper_trx::start_lower_ms()
void upper_trx::main_loop()
{
ms_trx::start();
set_name_aff_sched(sched_params::thread_names::U_RX);
set_upper_ready(true);
while (!g_exit_flag) {
driveReceiveFIFO();
osmo_select_main(1);
trxcon_phyif_rsp r;
if (cmdq_from_phy.spsc_pop(&r)) {
DBGLG() << "HAVE RESP:" << r.type << std::endl;
trxcon_phyif_handle_rsp(g_trxcon, &r);
}
}
set_upper_ready(false);
std::cerr << "exit U rx!" << std::endl;
mOn = false;
}
// signalvector is owning despite claiming not to, but we can pretend, too..
@@ -172,9 +179,9 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
const auto is_sch = gsm_sch_check_ts(wTime.TN(), wTime.FN());
const auto is_fcch = gsm_fcch_check_ts(wTime.TN(), wTime.FN());
trxcon::trxcon_phyif_rtr_ind i = { static_cast<uint32_t>(wTime.FN()), static_cast<uint8_t>(wTime.TN()) };
trxcon::trxcon_phyif_rtr_rsp r = {};
trxcon_phyif_handle_rtr_ind(trxcon::g_trxcon, &i, &r);
trxcon_phyif_rtr_ind i = { static_cast<uint32_t>(wTime.FN()), static_cast<uint8_t>(wTime.TN()) };
trxcon_phyif_rtr_rsp r = {};
trxcon_phyif_handle_rtr_ind(g_trxcon, &i, &r);
if (!(r.flags & TRXCON_PHYIF_RTR_F_ACTIVE))
return false;
@@ -191,7 +198,7 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
return true;
}
convert_and_scale<float, int16_t>(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
convert_and_scale(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
pow = energyDetect(sv, 20 * 4 /*sps*/);
if (pow < -1) {
@@ -218,7 +225,7 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
// fprintf(stderr, "%s %d\n", (is_nb ? "N":"D"), burst_time.FN());
// if (is_nb)
#endif
detect_burst(ss, &chan_imp_resp[0], normal_burst_start, demodded_softbits);
detect_burst_nb(ss, &chan_imp_resp[0], normal_burst_start, demodded_softbits);
#ifdef DBGXX
// else
// detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
@@ -241,40 +248,39 @@ void upper_trx::driveReceiveFIFO()
return;
if (pullRadioVector(burstTime, RSSI, TOA)) {
trxcon::trxcon_phyif_burst_ind bi;
trxcon_phyif_burst_ind bi;
bi.fn = burstTime.FN();
bi.tn = burstTime.TN();
bi.rssi = RSSI;
bi.toa256 = TOA;
bi.burst = (sbit_t *)demodded_softbits;
bi.burst_len = sizeof(demodded_softbits);
trxcon_phyif_handle_burst_ind(trxcon::g_trxcon, &bi);
trxcon_phyif_handle_burst_ind(g_trxcon, &bi);
}
struct trxcon::trxcon_phyif_rts_ind rts {
struct trxcon_phyif_rts_ind rts {
static_cast<uint32_t>(burstTime.FN()), static_cast<uint8_t>(burstTime.TN())
};
trxcon_phyif_handle_rts_ind(trxcon::g_trxcon, &rts);
trxcon_phyif_handle_rts_ind(g_trxcon, &rts);
}
void upper_trx::driveTx()
{
trxcon::internal_q_tx_buf e;
internal_q_tx_buf e;
static BitVector newBurst(sizeof(e.buf));
while (!trxcon::txq.spsc_pop(&e)) {
trxcon::txq.spsc_prep_pop();
while (!txq.spsc_pop(&e)) {
txq.spsc_prep_pop();
}
// ensure our tx cb is tickled and can exit
if (g_exit_flag) {
blade_sample_type dummy[10] = {};
submit_burst_ts(dummy, 10, 1);
submit_burst_ts(0, 1337, 1);
return;
}
trxcon::internal_q_tx_buf *burst = &e;
internal_q_tx_buf *burst = &e;
#ifdef TXDEBUG
#ifdef TXDEBUG2
DBGLG() << "got burst!" << burst->r.fn << ":" << burst->ts << " current: " << timekeeper.gsmtime().FN()
<< " dff: " << (int64_t)((int64_t)timekeeper.gsmtime().FN() - (int64_t)burst->r.fn) << std::endl;
#endif
@@ -292,10 +298,10 @@ void upper_trx::driveTx()
// float -> int16
blade_sample_type burst_buf[txburst->size()];
convert_and_scale<int16_t, float>(burst_buf, txburst->begin(), txburst->size() * 2, 1);
#ifdef TXDEBUG
convert_and_scale(burst_buf, txburst->begin(), txburst->size() * 2, 1);
#ifdef TXDEBUG2
auto check = signalVector(txburst->size(), 40);
convert_and_scale<float, int16_t, 1>(check.begin(), burst_buf, txburst->size() * 2);
convert_and_scale(check.begin(), burst_buf, txburst->size() * 2, 1);
estim_burst_params ebp;
auto d = detectAnyBurst(check, 2, 4, 4, CorrType::RACH, 40, &ebp);
if (d)
@@ -310,42 +316,44 @@ void upper_trx::driveTx()
}
#ifdef TXDEBUG
static const char *cmd2str(trxcon::trxcon_phyif_cmd_type c)
static const char *cmd2str(trxcon_phyif_cmd_type c)
{
switch (c) {
case trxcon::TRXCON_PHYIF_CMDT_RESET:
case TRXCON_PHYIF_CMDT_RESET:
return "TRXCON_PHYIF_CMDT_RESET";
case trxcon::TRXCON_PHYIF_CMDT_POWERON:
case TRXCON_PHYIF_CMDT_POWERON:
return "TRXCON_PHYIF_CMDT_POWERON";
case trxcon::TRXCON_PHYIF_CMDT_POWEROFF:
case TRXCON_PHYIF_CMDT_POWEROFF:
return "TRXCON_PHYIF_CMDT_POWEROFF";
case trxcon::TRXCON_PHYIF_CMDT_MEASURE:
case TRXCON_PHYIF_CMDT_MEASURE:
return "TRXCON_PHYIF_CMDT_MEASURE";
case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H0:
case TRXCON_PHYIF_CMDT_SETFREQ_H0:
return "TRXCON_PHYIF_CMDT_SETFREQ_H0";
case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H1:
case TRXCON_PHYIF_CMDT_SETFREQ_H1:
return "TRXCON_PHYIF_CMDT_SETFREQ_H1";
case trxcon::TRXCON_PHYIF_CMDT_SETSLOT:
case TRXCON_PHYIF_CMDT_SETSLOT:
return "TRXCON_PHYIF_CMDT_SETSLOT";
case trxcon::TRXCON_PHYIF_CMDT_SETTA:
case TRXCON_PHYIF_CMDT_SETTA:
return "TRXCON_PHYIF_CMDT_SETTA";
default:
return "UNKNOWN COMMAND!";
}
}
static void print_cmd(trxcon::trxcon_phyif_cmd_type c)
static void print_cmd(trxcon_phyif_cmd_type c)
{
DBGLG() << cmd2str(c) << std::endl;
DBGLG() << "handling " << cmd2str(c) << std::endl;
}
#endif
bool upper_trx::driveControl()
{
trxcon::trxcon_phyif_rsp r;
trxcon::trxcon_phyif_cmd cmd;
while (!trxcon::cmdq_to_phy.spsc_pop(&cmd)) {
trxcon::cmdq_to_phy.spsc_prep_pop();
trxcon_phyif_rsp r;
trxcon_phyif_cmd cmd;
while (!cmdq_to_phy.spsc_pop(&cmd)) {
cmdq_to_phy.spsc_prep_pop();
if (g_exit_flag)
return false;
}
if (g_exit_flag)
@@ -356,103 +364,54 @@ bool upper_trx::driveControl()
#endif
switch (cmd.type) {
case trxcon::TRXCON_PHYIF_CMDT_RESET:
case TRXCON_PHYIF_CMDT_RESET:
set_ta(0);
break;
case trxcon::TRXCON_PHYIF_CMDT_POWERON:
case TRXCON_PHYIF_CMDT_POWERON:
if (!mOn) {
set_upper_ready(true);
mOn = true;
start_lower_ms();
}
break;
case trxcon::TRXCON_PHYIF_CMDT_POWEROFF:
case TRXCON_PHYIF_CMDT_POWEROFF:
break;
case trxcon::TRXCON_PHYIF_CMDT_MEASURE:
r.type = trxcon::trxcon_phyif_cmd_type::TRXCON_PHYIF_CMDT_MEASURE;
case TRXCON_PHYIF_CMDT_MEASURE:
r.type = trxcon_phyif_cmd_type::TRXCON_PHYIF_CMDT_MEASURE;
r.param.measure.band_arfcn = cmd.param.measure.band_arfcn;
// FIXME: do we want to measure anything, considering the transceiver just syncs by.. syncing?
r.param.measure.dbm = -80;
tuneRx(trxcon::gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 0) * 1000 * 100);
tuneTx(trxcon::gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 1) * 1000 * 100);
trxcon::cmdq_from_phy.spsc_push(&r);
tuneRx(gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 0) * 1000 * 100);
tuneTx(gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 1) * 1000 * 100);
cmdq_from_phy.spsc_push(&r);
break;
case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H0:
tuneRx(trxcon::gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 0) * 1000 * 100);
tuneTx(trxcon::gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 1) * 1000 * 100);
case TRXCON_PHYIF_CMDT_SETFREQ_H0:
tuneRx(gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 0) * 1000 * 100);
tuneTx(gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 1) * 1000 * 100);
break;
case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H1:
case TRXCON_PHYIF_CMDT_SETFREQ_H1:
break;
case trxcon::TRXCON_PHYIF_CMDT_SETSLOT:
case TRXCON_PHYIF_CMDT_SETSLOT:
break;
case trxcon::TRXCON_PHYIF_CMDT_SETTA:
case TRXCON_PHYIF_CMDT_SETTA:
set_ta(cmd.param.setta.ta);
break;
}
return false;
}
// trxcon C call(back) if
extern "C" {
int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon::trxcon_phyif_burst_req *br)
{
if (br->burst_len == 0) // dummy/nope
return 0;
OSMO_ASSERT(br->burst != 0);
trxcon::internal_q_tx_buf b;
b.r = *br;
memcpy(b.buf, (void *)br->burst, br->burst_len);
if (!g_exit_flag)
trxcon::txq.spsc_push(&b);
return 0;
}
int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon::trxcon_phyif_cmd *cmd)
{
#ifdef TXDEBUG
DBGLG() << "TOP C: " << cmd2str(cmd->type) << std::endl;
#endif
if (!g_exit_flag)
trxcon::cmdq_to_phy.spsc_push(cmd);
// q for resp polling happens in main loop
return 0;
}
void trxcon_phyif_close(void *phyif)
{
}
void trxcon_l1ctl_close(struct trxcon::trxcon_inst *trxcon)
{
/* Avoid use-after-free: both *fi and *trxcon are children of
* the L2IF (L1CTL connection), so we need to re-parent *fi
* to NULL before calling l1ctl_client_conn_close(). */
talloc_steal(NULL, trxcon->fi);
trxcon::l1ctl_client_conn_close((struct trxcon::l1ctl_client *)trxcon->l2if);
}
int trxcon_l1ctl_send(struct trxcon::trxcon_inst *trxcon, struct trxcon::msgb *msg)
{
struct trxcon::l1ctl_client *l1c = (struct trxcon::l1ctl_client *)trxcon->l2if;
return trxcon::l1ctl_client_send(l1c, msg);
}
}
void sighandler(int sigset)
{
// we might get a sigpipe in case the l1ctl ud socket disconnects because mobile quits
if (sigset == SIGPIPE) {
if (sigset == SIGPIPE || sigset == SIGINT) {
g_exit_flag = true;
// we know the flag is atomic and it prevents the trxcon cb handlers from writing
// to the queues, so submit some trash to unblock the threads & exit
trxcon::trxcon_phyif_cmd cmd = {};
trxcon::internal_q_tx_buf b = {};
trxcon::txq.spsc_push(&b);
trxcon::cmdq_to_phy.spsc_push(&cmd);
trxcon_phyif_cmd cmd = {};
internal_q_tx_buf b = {};
txq.spsc_push(&b);
cmdq_to_phy.spsc_push(&cmd);
msleep(200);
return;
}
@@ -462,15 +421,26 @@ int main(int argc, char *argv[])
{
auto tall_trxcon_ctx = talloc_init("trxcon context");
signal(SIGPIPE, sighandler);
fesetround(FE_TOWARDZERO);
signal(SIGINT, sighandler);
trxcon::msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
trxc_log_init(tall_trxcon_ctx);
trxcon::g_trxcon = trxcon::trxcon_inst_alloc(tall_trxcon_ctx, 0, 0);
trxcon::g_trxcon->gsmtap = nullptr;
trxcon::g_trxcon->phyif = nullptr;
trxcon::g_trxcon->phy_quirks.fbsb_extend_fns = 866; // 4 seconds, known to work.
/* Configure pretty logging */
log_set_print_extended_timestamp(osmo_stderr_target, 1);
log_set_print_category_hex(osmo_stderr_target, 0);
log_set_print_category(osmo_stderr_target, 1);
log_set_print_level(osmo_stderr_target, 1);
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
osmo_fsm_log_timeouts(true);
g_trxcon = trxcon_inst_alloc(tall_trxcon_ctx, 0);
g_trxcon->gsmtap = nullptr;
g_trxcon->phyif = nullptr;
g_trxcon->phy_quirks.fbsb_extend_fns = 866; // 4 seconds, known to work.
convolve_init();
convert_init();
@@ -482,15 +452,22 @@ int main(int argc, char *argv[])
trx->do_auto_gain = true;
status = trx->init_dev_and_streams();
trx->set_name_aff_sched("main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5);
if (status < 0) {
std::cerr << "Error initializing hardware, quitting.." << std::endl;
return -1;
}
trx->set_name_aff_sched(sched_params::thread_names::MAIN);
if (!trxcon::trxc_l1ctl_init(tall_trxcon_ctx)) {
if (!trxc_l1ctl_init(tall_trxcon_ctx)) {
std::cerr << "Error initializing l1ctl, quitting.." << std::endl;
return -1;
}
// blocking, will return when global exit is requested
trx->start_threads();
trx->main_loop();
trx->stop_threads();
trx->stop_upper_threads();
return status;
}

View File

@@ -24,25 +24,25 @@
#include <arpa/inet.h>
#include "GSMCommon.h"
#include "radioClock.h"
#include "ms.h"
class upper_trx : public ms_trx {
bool mOn;
volatile bool mOn;
char demodded_softbits[444];
// void driveControl();
bool pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
pthread_t thr_control, thr_tx;
public:
void start_threads();
void main_loop();
void stop_upper_threads();
bool driveControl();
void driveReceiveFIFO();
void driveTx();
bool pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
std::thread thr_control, thr_rx, thr_tx;
public:
void start_threads();
void start_lower_ms();
upper_trx(){};
upper_trx() : mOn(false){};
};

View File

@@ -0,0 +1,92 @@
#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/>.
*
*/
#include <functional>
#include <thread>
#include <atomic>
#include <vector>
#include <future>
#include <mutex>
#include <queue>
struct single_thread_pool {
std::mutex m;
std::condition_variable cv;
std::atomic<bool> stop_flag;
std::atomic<bool> is_ready;
std::deque<std::function<void()>> wq;
std::thread worker_thread;
template <class F>
void add_task(F &&f)
{
std::unique_lock<std::mutex> l(m);
wq.emplace_back(std::forward<F>(f));
cv.notify_one();
return;
}
single_thread_pool() : stop_flag(false), is_ready(false), worker_thread(std::thread([this] { thread_loop(); }))
{
}
~single_thread_pool()
{
stop();
}
std::thread::native_handle_type get_handle()
{
return worker_thread.native_handle();
}
private:
void stop()
{
{
std::unique_lock<std::mutex> l(m);
wq.clear();
stop_flag = true;
cv.notify_one();
}
worker_thread.join();
}
void thread_loop()
{
while (true) {
is_ready = true;
std::function<void()> f;
{
std::unique_lock<std::mutex> l(m);
if (wq.empty()) {
cv.wait(l, [&] { return !wq.empty() || stop_flag; });
}
if (stop_flag)
return;
is_ready = false;
f = std::move(wq.front());
wq.pop_front();
}
f();
}
}
};

View File

@@ -65,7 +65,8 @@ struct uhd_buf_wrap {
using dev_buf_t = uhd_buf_wrap;
using bh_fn_t = std::function<int(dev_buf_t *)>;
template <typename T> struct uhd_hw {
template <typename T>
struct uhd_hw {
uhd::usrp::multi_usrp::sptr dev;
uhd::rx_streamer::sptr rx_stream;
uhd::tx_streamer::sptr tx_stream;
@@ -76,13 +77,14 @@ template <typename T> struct uhd_hw {
const unsigned int rxFullScale, txFullScale;
const int rxtxdelay;
float rxgain, txgain;
static std::atomic<bool> stop_me_flag;
static std::atomic<bool> stop_lower_threads_flag;
double rxfreq_cache, txfreq_cache;
virtual ~uhd_hw()
{
delete[] one_pkt_buf;
}
uhd_hw() : rxFullScale(32767), txFullScale(32767), rxtxdelay(-67)
uhd_hw() : rxFullScale(32767), txFullScale(32767 * 0.3), rxtxdelay(-67), rxfreq_cache(0), txfreq_cache(0)
{
}
@@ -92,15 +94,21 @@ template <typename T> struct uhd_hw {
bool tuneTx(double freq, size_t chan = 0)
{
if (txfreq_cache == freq)
return true;
msleep(25);
dev->set_tx_freq(freq, chan);
txfreq_cache = freq;
msleep(25);
return true;
};
bool tuneRx(double freq, size_t chan = 0)
{
if (rxfreq_cache == freq)
return true;
msleep(25);
dev->set_rx_freq(freq, chan);
rxfreq_cache = freq;
msleep(25);
return true;
};
@@ -133,6 +141,7 @@ template <typename T> struct uhd_hw {
int init_device(bh_fn_t rxh, bh_fn_t txh)
{
auto const lock_delay_ms = 500;
auto clock_lock_attempts = 15; // x lock_delay_ms
auto const mcr = 26e6;
auto const rate = (1625e3 / 6) * 4;
auto const ref = "external";
@@ -157,8 +166,18 @@ template <typename T> struct uhd_hw {
dev->set_tx_bandwidth(bw, channel);
while (!(dev->get_rx_sensor("lo_locked", channel).to_bool() &&
dev->get_mboard_sensor("ref_locked").to_bool()))
dev->get_mboard_sensor("ref_locked").to_bool()) &&
clock_lock_attempts > 0) {
std::cerr << "clock source lock attempts remaining: " << clock_lock_attempts << ".."
<< std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(lock_delay_ms));
clock_lock_attempts--;
}
if (clock_lock_attempts <= 0) {
std::cerr << "Error locking clock, gpsdo missing? quitting.." << std::endl;
return -1;
}
uhd::stream_args_t stream_args("sc16", "sc16");
rx_stream = dev->get_rx_stream(stream_args);
@@ -173,6 +192,11 @@ template <typename T> struct uhd_hw {
return 0;
}
void actually_enable_streams()
{
// nop: stream cmd in handler
}
void *rx_cb(bh_fn_t burst_handler)
{
void *ret = nullptr;
@@ -215,7 +239,7 @@ template <typename T> struct uhd_hw {
stream_cmd.time_spec = uhd::time_spec_t();
rx_stream->issue_stream_cmd(stream_cmd);
while (!stop_me_flag) {
while (!stop_lower_threads_flag) {
rx_cb(burst_handler);
}
};

View File

@@ -77,6 +77,24 @@ static struct ctrl_handle *g_ctrlh;
static RadioDevice *usrp;
static RadioInterface *radio;
/* adjusts read timestamp offset to make the viterbi equalizer happy by including the start tail bits */
template <typename B>
class rif_va_wrapper : public B {
bool use_va;
public:
template <typename... Args>
rif_va_wrapper(bool use_va, Args &&...args) : B(std::forward<Args>(args)...), use_va(use_va)
{
}
bool start() override
{
auto rv = B::start();
B::readTimestamp -= use_va ? 20 : 0;
return rv;
};
};
/* Create radio interface
* The interface consists of sample rate changes, frequency shifts,
* channel multiplexing, and other conversions. The transceiver core
@@ -91,17 +109,17 @@ RadioInterface *makeRadioInterface(struct trx_ctx *trx,
switch (type) {
case RadioDevice::NORMAL:
radio = new RadioInterface(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps, trx->cfg.num_chans);
radio = new rif_va_wrapper<RadioInterface>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps,
trx->cfg.num_chans);
break;
case RadioDevice::RESAMP_64M:
case RadioDevice::RESAMP_100M:
radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps);
radio = new rif_va_wrapper<RadioInterfaceResamp>(trx->cfg.use_va, usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps);
break;
case RadioDevice::MULTI_ARFCN:
radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps, trx->cfg.num_chans);
radio = new rif_va_wrapper<RadioInterfaceMulti>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps,
trx->cfg.num_chans);
break;
default:
LOG(ALERT) << "Unsupported radio interface configuration";
@@ -465,6 +483,12 @@ int trx_validate_config(struct trx_ctx *trx)
return -1;
}
if (trx->cfg.use_va &&
(trx->cfg.egprs || trx->cfg.multi_arfcn || trx->cfg.tx_sps != 4 || trx->cfg.rx_sps != 4)) {
LOG(ERROR) << "Viterbi equalizer only works for gmsk with 4 tx/rx samples per symbol!";
return -1;
}
return 0;
}
@@ -571,24 +595,14 @@ static void trx_stop()
static int trx_start(struct trx_ctx *trx)
{
int type, chans;
unsigned int i;
std::vector<std::string> rx_paths, tx_paths;
RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
/* Create the low level device object */
if (trx->cfg.multi_arfcn)
iface = RadioDevice::MULTI_ARFCN;
/* Generate vector of rx/tx_path: */
for (i = 0; i < trx->cfg.num_chans; i++) {
rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path));
tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path));
}
usrp = RadioDevice::make(trx->cfg.tx_sps, trx->cfg.rx_sps, iface,
trx->cfg.num_chans, trx->cfg.offset,
tx_paths, rx_paths);
type = usrp->open(charp2str(trx->cfg.dev_args), trx->cfg.clock_ref, trx->cfg.swap_channels);
usrp = RadioDevice::make(iface, &trx->cfg);
type = usrp->open();
if (type < 0) {
LOG(ALERT) << "Failed to create radio device" << std::endl;
goto shutdown;
@@ -651,7 +665,7 @@ int main(int argc, char *argv[])
exit(2);
}
rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX);
rc = telnet_init_default(tall_trx_ctx, NULL, OSMO_VTY_PORT_TRX);
if (rc < 0)
exit(1);

View File

@@ -48,9 +48,8 @@ struct trxd_hdr_common {
reserved:1,
version:4;
#elif OSMO_IS_BIG_ENDIAN
uint8_t version:4,
reserved:1,
tn:3;
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t version:4, reserved:1, tn:3;
#endif
uint32_t fn; /* big endian */
} __attribute__ ((packed));
@@ -86,9 +85,8 @@ struct trxd_hdr_v1_specific {
modulation:4,
idle:1;
#elif OSMO_IS_BIG_ENDIAN
uint8_t idle:1,
modulation:4,
tsc:3;
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
uint8_t idle:1, modulation:4, tsc:3;
#endif
int16_t ci; /* big endian, in centiBels */
} __attribute__ ((packed));

View File

@@ -75,7 +75,7 @@ private:
public:
/** start the interface */
bool start();
virtual bool start();
bool stop();
/** initialization */
@@ -151,7 +151,7 @@ private:
public:
RadioInterfaceResamp(RadioDevice* wDevice, size_t tx_sps, size_t rx_sps);
~RadioInterfaceResamp();
virtual ~RadioInterfaceResamp();
bool init(int type);
void close();
@@ -184,7 +184,7 @@ private:
public:
RadioInterfaceMulti(RadioDevice* radio, size_t tx_sps,
size_t rx_sps, size_t chans = 1);
~RadioInterfaceMulti();
virtual ~RadioInterfaceMulti();
bool init(int type);
void close();

View File

@@ -153,8 +153,6 @@ bool RadioInterfaceMulti::init(int type)
return false;
}
close();
convertSendBuffer.resize(1);
convertRecvBuffer.resize(1);

View File

@@ -98,8 +98,6 @@ bool RadioInterfaceResamp::init(int type)
{
float cutoff = 1.0f;
close();
switch (type) {
case RadioDevice::RESAMP_64M:
resamp_inrate = RESAMP_64M_INRATE;

View File

@@ -82,10 +82,10 @@ AC_TYPE_SIZE_T
AC_HEADER_TIME
AC_C_BIGENDIAN
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.9.0)
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
@@ -212,7 +212,12 @@ AS_IF([test "x$with_bladerf" = "xyes"], [
AC_MSG_CHECKING([whether to enable building MS TRX])
AS_IF([test "x$with_mstrx" = "xyes"], [
AC_CONFIG_SUBDIRS([osmocom-bb/src/host/trxcon])
AC_SUBST(LIBTRXCON_DIR, "osmocom-bb/src/host/trxcon")
LIBTRXCON_DIR="osmocom-bb/src/host/trxcon"
if ! test -d "$srcdir/$LIBTRXCON_DIR"; then
AC_MSG_RESULT([no])
AC_MSG_ERROR([$LIBTRXCON_DIR does not exist])
fi
AC_SUBST(LIBTRXCON_DIR)
AC_MSG_RESULT([yes])
], [
# Despite LIBTRXCON_DIR is added to SUBDIRS conditionally,

View File

@@ -34,10 +34,10 @@ BuildRequires: pkgconfig(LimeSuite)
BuildRequires: pkgconfig(usrp) >= 3.3
%endif
BuildRequires: pkgconfig(fftw3f)
BuildRequires: pkgconfig(libosmocoding) >= 1.6.0
BuildRequires: pkgconfig(libosmocore) >= 1.6.0
BuildRequires: pkgconfig(libosmoctrl) >= 1.6.0
BuildRequires: pkgconfig(libosmovty) >= 1.6.0
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}

View File

@@ -1,5 +1,7 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (IPC Backend)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple

View File

@@ -1,5 +1,7 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (LimeSuite backend)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple

View File

@@ -1,5 +1,7 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (UHD Backend)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple

View File

@@ -1,5 +1,7 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (libusrp backend)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple

116
debian/changelog vendored
View File

@@ -1,3 +1,119 @@
osmo-trx (1.6.1) unstable; urgency=medium
[ Eric Wild ]
* devices: fix wrong gain to power mapping
-- Eric Wild <ewild@sysmocom.de> Thu, 09 Nov 2023 14:16:21 +0200
osmo-trx (1.6.0) unstable; urgency=medium
[ Vadim Yanitskiy ]
* configure.ac: check if LIBTRXCON_DIR (submodule) exists
* tests: Makefile.am: move -I flags from AM_CFLAGS to AM_CPPFLAGS
* tests: there shall be no libraries in LDFLAGS
* tests: use -no-install libtool flag to avoid ./lt-* scripts
* tests: LMSDeviceTest: fix CPPFLAGS vs CXXFLAGS
* ipc-driver-test: clean up variables in Makefile.am
* CommonLibs: remove unused *trx in cfg[_no]_ctr_error_threshold_cmd
* CommonLibs: clean up and fix Makefile.am
* ms: logging: print category, level, and extended timestamp
[ Oliver Smith ]
* Run struct_endianness.py
* debian: set compat level to 10
* systemd: depend on networking-online.target
* USRPDevice:updateAlignment: remove byteswap code
[ Eric ]
* .clang-format: adjust template formatting
* ms: update submodule to currently known working version
* ms: adjust tx scaling for tx samples
* ms : fix the template formatting
* ms: fix the gain init for blade
* ms: prettify scheduling + add odroid
* ms: fix startup & shutdown of blade
* ms: block burst q to upper layer
* ms: use single thread pool
* ms : rename var
* ms : rename var
* ms: cache frequency
* ms: pretty tx buf class
* ms: rearrange internal trxcon<->phy if
* ms: remove syncthing tool
* ms: prune common sch acq code
* ms: rearrange code to allow clean exits
* ms: flexible template for value_type buffer sum
* ms: make init call less confusing
* transceiver: pass cfg struct instead of args
* devices: unify band handling
* ms: fix blocking logging
* ms: drop the tx burst padding
* trx: fix dev-args issue
* ms: update osmocom-bb
* ms: restructure the va code to add rach support
* transceiver: add experimental viterbi equalizer support
* ms/va: make ancient gcc < 8 happy
* ms: fix thread prio startup issue
* ms: fix a few coverity complaints related to initialization
* ms: bump osmocom-bb submodule to current head
[ Eric Wild ]
* ms: adjust float<->integral type conversion
* ms: sch: drop intermediate softvector
* devices: add freq/gain override for uhd
[ arehbein ]
* Transition to use of 'telnet_init_default'
[ Pau Espin Pedrol ]
* Call osmo_fd_unregister() before closing and changing bfd->fd
* ms: update osmocom-bb submodule
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 15:56:57 +0200
osmo-trx (1.5.0) unstable; urgency=medium
[ Oliver Smith ]
* configure.ac: add -lboost_thread for uhd < 4.2.0
* gitignore: add uhddev_ipc.cpp
* contrib/jenkins: don't run "make distcheck" on arm
[ Vadim Yanitskiy ]
* threshold_timer_update_intv(): call osmo_timer_del() unconditionally
* Transceiver::expectedCorrType(): RACH is always 8-bit on PTCCH/U
* contrib/jenkins.sh: dump submodule status before building
* configure.ac: fix: properly check whether to enable ms-trx
* configure.ac: allow building without cloning submodules
* configure.ac: cosmetic: rearrange MS TRX related logic
* configure.ac: make use of AC_MSG_CHECKING and AC_MSG_RESULT
[ Max ]
* Set working directory in systemd service file
* Add realtime scheduling and set priority in service file
* ctrl: take both address and port from vty config
[ Eric ]
* ignore vscode dirs
* rename noisevector class -> avgvector
* osmocom-bb for ms-trx side trxcon integration
* add checkpatch config
* bladerf xa4 support
* update osmocom-bb submodule to fix make distcheck
* vita demod by piotr krysik, modified
* properly update osmocom-bb submodule, for real this time..
* ms-trx support
* clean up mutex, scopedlock, and signal classes
* ipc: add missing override
* clang-format: proper c++ standard
* ipc: remove old autotools workaround
* ms: init trash used to escape the usb callbacks
* radio interface: fix init
[ Eric Wild ]
* mstrx: do not wait forever if clock locking fails
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 17:08:17 +0100
osmo-trx (1.4.1) unstable; urgency=medium
[ Oliver Smith ]

2
debian/compat vendored
View File

@@ -1 +1 @@
9
10

6
debian/control vendored
View File

@@ -2,7 +2,7 @@ Source: osmo-trx
Section: net
Priority: optional
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
Build-Depends: debhelper (>= 9),
Build-Depends: debhelper (>= 10),
autotools-dev,
autoconf-archive,
pkg-config,
@@ -14,8 +14,8 @@ Build-Depends: debhelper (>= 9),
libtalloc-dev,
libusrp-dev,
liblimesuite-dev,
libosmocore-dev (>= 1.6.0),
osmo-gsm-manuals-dev
libosmocore-dev (>= 1.9.0),
osmo-gsm-manuals-dev (>= 1.5.0)
Standards-Version: 3.9.6
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx

View File

@@ -1,7 +1,25 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall -I$(top_srcdir)/CommonLibs $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) -g
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
AM_CPPFLAGS = \
-I$(top_srcdir)/CommonLibs \
$(STD_DEFINES_AND_INCLUDES) \
$(NULL)
AM_CXXFLAGS = \
-Wall -g \
$(LIBOSMOCORE_CFLAGS) \
$(LIBOSMOCTRL_CFLAGS) \
$(LIBOSMOVTY_CFLAGS) \
$(NULL)
AM_LDFLAGS = -no-install
LDADD = \
$(COMMON_LA) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_LIBS) \
$(NULL)
EXTRA_DIST = BitVectorTest.ok \
PRBSTest.ok \
@@ -20,21 +38,16 @@ check_PROGRAMS = \
LogTest
BitVectorTest_SOURCES = BitVectorTest.cpp
BitVectorTest_LDADD = $(COMMON_LA)
PRBSTest_SOURCES = PRBSTest.cpp
InterthreadTest_SOURCES = InterthreadTest.cpp
InterthreadTest_LDADD = $(COMMON_LA)
InterthreadTest_LDFLAGS = -lpthread $(AM_LDFLAGS)
InterthreadTest_LDADD = $(LDADD) -lpthread
TimevalTest_SOURCES = TimevalTest.cpp
TimevalTest_LDADD = $(COMMON_LA)
VectorTest_SOURCES = VectorTest.cpp
VectorTest_LDADD = $(COMMON_LA)
LogTest_SOURCES = LogTest.cpp
LogTest_LDADD = $(COMMON_LA)
MOSTLYCLEANFILES += testSource testDestination

View File

@@ -1,6 +1,13 @@
include $(top_srcdir)/Makefile.common
AM_CFLAGS = -Wall -I$(top_srcdir)/Transceiver52M -I$(top_srcdir)/Transceiver52M/arch/common $(STD_DEFINES_AND_INCLUDES) -g
AM_CPPFLAGS = \
-I$(top_srcdir)/Transceiver52M \
-I$(top_srcdir)/Transceiver52M/arch/common \
$(STD_DEFINES_AND_INCLUDES) \
$(NULL)
AM_CFLAGS = -Wall -g
AM_LDFLAGS = -no-install
EXTRA_DIST = convolve_test.ok convolve_test_golden.h
@@ -20,10 +27,11 @@ endif
if DEVICE_LMS
check_PROGRAMS += LMSDeviceTest
LMSDeviceTest_SOURCES = LMSDeviceTest.cpp
LMSDeviceTest_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LMS_LIBS)
LMSDeviceTest_LDADD = \
$(top_builddir)/Transceiver52M/device/lms/libdevice.la \
$(LIBOSMOCORE_LIBS) \
$(COMMON_LA) \
$(LMS_LIBS)
LMSDeviceTest_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
$(LMS_LIBS) \
$(NULL)
LMSDeviceTest_CXXFLAGS = $(AM_CFLAGS) $(LMS_CFLAGS)
endif