Compare commits

..

102 Commits

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Change-Id: I46b3ea6b0094026bd50709739df464438f9e54c4
2023-09-20 15:19:13 +02:00
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
Eric
d0c1055051 ms: init trash used to escape the usb callbacks
Closes: OS#5847

Change-Id: I1b41350f981bd9f68163509b94d5457218b415d6
2022-12-28 17:01:13 +00:00
Vadim Yanitskiy
0ce64705e0 configure.ac: make use of AC_MSG_CHECKING and AC_MSG_RESULT
Always log whether the MS TRX is going to be built, event if it's not.

Change-Id: I1c5648b8090ba2b6638b71f2d3332dfae87b2772
Related: OS#5599
2022-12-28 05:21:19 +07:00
Vadim Yanitskiy
d0b947a0c4 configure.ac: cosmetic: rearrange MS TRX related logic
Make it consistent with the existing AS_IF/AM_CONDITIONAL locations.

Change-Id: I760b93c1b07989b02c4c38cd20ab40e45650f2c1
Related: OS#5599
2022-12-28 05:21:15 +07:00
Vadim Yanitskiy
4a867be165 configure.ac: allow building without cloning submodules
The MS TRX was intentionally added [1] as an optional feature, which
requires a git submodule to be present in order to build libtrxcon.
This feature can be enabled by passing --with-mstrx to the configure.

But autoconf/automake is a mess.  Despite in the root Makefile.am we
are adding full submodule path to the SUBDIRS *conditionally*, the
configure script would still fail if the submodule is not fetched:

  Makefile.am:32: error: required directory
                         ./osmocom-bb/src/host/trxcon
                         does not exist

It would not even enter that directory if it's present, but somehow
it's still required to exist.  For the end user this means that
cloning the submodule becomes a *necessary* step in order to build
osmo-trx from source, even when the MS TRX is not really needed.

The fact that we're unconditionally requiring the submodule, which
is meant to be used by an optional feature feels wrong to me.  It's
also unusual for osmocom projects to require submodules, so it may
(and already did) cause build failures when cloning as usual.

Let's work this problem around by defining LIBTRXCON_DIR variable
in configure.ac and using it in the root Makefile.am.  If the MS
TRX is not enabled explicitly, make autoconf/automake happy by
assigning LIBTRXCON_DIR the submodule's root directory, which is
always present but empty if the submodule is not fetched.

Change-Id: I02ae2b37c82ae2f55e7d9bd92e226f2b8b023968
Related: [1] b7253c6fdc
Related: OS#5599, OS#5846
2022-12-28 04:38:07 +07:00
Vadim Yanitskiy
fdcce1fc6e configure.ac: fix: properly check whether to enable ms-trx
Condition 'test ENABLE_MS_TRX' is always true because 'ENABLE_MS_TRX'
is a string literal, not a variable...  Because of this, automeke will
unconditionally try to configure the submodule.

Change-Id: Icbb9278c688bfe506d5ad726f16a6c200572de1b
Fixes: b7253c6fdc
Related: OS#5599
2022-12-23 22:29:42 +07:00
Eric
2ca77d7ff2 ipc: remove old autotools workaround
Closes: OS#5845
Change-Id: I3f8a0204b1fda52d1228add8afde620274b164a5
2022-12-23 15:37:27 +01:00
Eric
b6fd0709f4 clang-format: proper c++ standard
Change-Id: I9828093a08ed8b4c2a11f482f674368ac137d4dc
2022-12-23 13:41:53 +00:00
Eric
d3e3bba2cd ipc: add missing override
Change-Id: Ib1493ac10b40d24372075d4cebd67015192675e0
2022-12-23 13:41:36 +00:00
Eric
5561f1129d clean up mutex, scopedlock, and signal classes
This also uncovers very interesting design decisions like the copying of
mutexes and condition vars depending on recursive locks that were
previously hidden by shady c function calls..
We have perfectly good c++11 versions for all of that.

While we're at it, also use the initialization list for the other (still
copy constructable) vectors, which cleans up the radio interfaces.

Change-Id: Idc9e3b1144c5b93f5dad2f8e0e30f1058477aa52
2022-12-23 13:41:30 +00:00
Eric
b7253c6fdc ms-trx support
This is basically a trxcon that includes a transceiver, and can just
be used with existing and future apps supporting the trxcon interface,
i.e. mobile or ccch_scan.

Supports bladerf and uhd.
Currently using hardcoded sched/prios aimed at a setup with working,
reliable usb and reserved cores, for example a raspi 4 (ONLY 4, not 3,
not 2, not any other version)

Additionally builds test tools used for development: osmo-trx-syncthing*

see https://osmocom.org/projects/baseband/wiki/MS-side_GPRS for the
project description and details

Change-Id: I36c65a8c725c4da76dc70006cd96b0a2b6878e84
2022-12-23 13:41:13 +00:00
Vadim Yanitskiy
5a23a24bb1 contrib/jenkins.sh: dump submodule status before building
Change-Id: I0f9d8f9213fd59605172fac011306c96c39bd5eb
2022-12-22 21:34:55 +00:00
Eric
b60fb8e1e3 properly update osmocom-bb submodule, for real this time..
Change-Id: Icb3ba575cf5235db7e6c03df3d7d45133d535c50
2022-12-22 16:15:42 +00:00
Max
934e1016ff ctrl: take both address and port from vty config
Change-Id: Id7d1425e603cc62a62a63d1057291861f02782ba
2022-12-17 21:26:49 +03:00
Eric
ac726b1147 vita demod by piotr krysik, modified
Grabbed from gr-gsm 2de47e28ce1fb9a518337bfc0add36c8e3cff5eb
Had a few rounds of extensive cleanup (not the va itself). Uses gcc
multiversioning for x86 targets.

Change-Id: I5466c522cf4de984a4810ec46df43a10b52ed78f
2022-12-13 11:06:33 +01:00
Eric
508270d83d update osmocom-bb submodule to fix make distcheck
see https://gerrit.osmocom.org/c/osmocom-bb/+/30563

Change-Id: Ie7b10891b69c3dc863c76284f422f9e9509ac244
2022-12-13 11:06:33 +01:00
Eric
7d897cb5b0 bladerf xa4 support
This is not really finished, there are multiple reasons to not use this:
1) main clock is not a gsm multiple, so it will continously drift
2) small buffer sizes lead to tx gaps that are hard to detect and break
everything.

Change-Id: I455c34bb9520d5f09eeb1ac76fceb4bdea94d1ac
2022-12-06 09:45:06 +01:00
Eric
94dcf6d29c add checkpatch config
- do not lint the submodule
- do not lint the grgsm code
- do not lint headers, checkpatch does not understand that this is a c++
project and any change to existing headers will cause issues.
- also do not do arcane C varargs checks that do not apply to C++

Change-Id: Ie7a9fbd021e12a88db30212240af2332c6cdcb37
2022-12-02 14:45:17 +01:00
Eric
040497e0a4 osmocom-bb for ms-trx side trxcon integration
Change-Id: I784a3d66b68f6458056d92db2ec596755b704e61
2022-12-01 12:00:04 +01:00
Eric
8984d7f2ca rename noisevector class -> avgvector
The vectors feature is averaging, and not adding noise.

Change-Id: I05def8ab9ea7a2cece8db09c36c303e13ef40927
2022-11-30 16:40:42 +01:00
Eric
aa7a40ee84 ignore vscode dirs
Change-Id: Iad9fd20924b7cfc6dbbfb708aa9c692a3cab574c
2022-11-28 19:19:38 +01:00
Max
f2f35fc592 Add realtime scheduling and set priority in service file
This sets highest realtime priority among all Osmocom services.

Related: OS#5687
Change-Id: Id74acf201d4388493f0eeb38909641c662551164
2022-11-11 17:33:25 +03:00
Vadim Yanitskiy
00ddcfaf50 Transceiver::expectedCorrType(): RACH is always 8-bit on PTCCH/U
It does not make sense for the MS to use 11-bit Access Bursts on
PTCCH/U because the payload does not matter, only the ToA does.

Change-Id: I5fb9f1c6810cdcd26a885b183e414d01d422eb28
2022-10-26 04:13:43 +07:00
Max
2f20c564bf Set working directory in systemd service file
By default systemd will execute service with root directory
(or home directory for user instance) which might result in
attempts to create files in unexpected place. Let's set it
to 'osmocom' subdir of state directory
(/var/lib for system instance) instead.

Related: OS#4821
Change-Id: I041bb9e0b4250826d2d15d0ea2cdb93d7a0cd04b
2022-09-09 18:54:42 +07:00
Vadim Yanitskiy
19faae85c6 threshold_timer_update_intv(): call osmo_timer_del() unconditionally
osmo_timer_del() does check if a timer is active internally.

Change-Id: Icaf6f31a3235a3fb6993fa2bb35190150e35c2c2
2022-08-06 05:03:00 +07:00
Oliver Smith
1c6a3459cd contrib/jenkins: don't run "make distcheck" on arm
Avoid building osmo-trx a second time on arm during "make distcheck". If
the build of osmo-trx already passed on arm, running "make distcheck"
should give the exact same result as on x86_64.

This should shorten the build time significantly for osmo-trx jobs
running on raspberry pis, currently they take half an hour.

Change-Id: Ib4fbf7d54d479bbdda3c1415493bfc57b37d3971
2022-07-11 13:57:49 +02:00
Oliver Smith
32311d8635 gitignore: add uhddev_ipc.cpp
This file gets generated during the build.

Change-Id: I70bfb456e309daa4436767b2e55bce0c038a4975
2022-07-08 14:35:49 +02:00
Oliver Smith
71c46e91df configure.ac: add -lboost_thread for uhd < 4.2.0
Fix for the following error we see since building master-osmo-trx on
debian 10 instead of 9:
  /usr/bin/ld: ipc_driver_test-uhdwrap.o: undefined reference to symbol '_ZTIN5boost6detail16thread_data_baseE'
  /usr/bin/ld: //usr/lib/x86_64-linux-gnu/libboost_thread.so.1.67.0: error adding symbols: DSO missing from command line

After spending a lot of time on researching this, my understanding is
now that uhd.pc should have had "-lboost_thread" in versions up to the
latest release 4.2.0 because before that it would include boost thread
headers in its logging code:
04a83b6e76

ld is able to figure out which library provides the missing symbol, and
apparently depending on the binutils version and linker flags, it may
just ignore this and not show an error. This is why apparently it worked
with debian 9 and still does work in OBS (different flags), and why it
was not fixed upstream in uhd.pc. By now fixing it is not needed in the
latest version anymore, and there are already versions of uhd.pc in
various linux distributions without -lboost_thread, so I think it's
appropriate to add the workaround here in configure.ac.

Fixes: OS#5608
Related: https://stackoverflow.com/q/19901934
Change-Id: I0367f1c2981bf56252e7514d5993cbbec960b21b
2022-07-08 14:35:36 +02:00
Pau Espin Pedrol
a39fa875a3 Bump version: 1.4.0.3-9a3e → 1.4.1
Change-Id: I9a930264b7c40b3e205c9818634597cc21364a50
2022-06-29 09:32:57 +02:00
Harald Welte
9a3e3fceb8 update git URLs (git -> https; gitea)
Change-Id: I1ac62853250a6717f502959bd96fdc3e9ead7a91
2022-06-18 13:52:06 +02:00
143 changed files with 4701 additions and 16082 deletions

5
.checkpatch.conf Normal file
View File

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

View File

@@ -25,7 +25,7 @@ AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: false
AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
@@ -515,7 +515,7 @@ SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp03
Standard: Cpp11
TabWidth: 8
UseTab: Always
...

4
.gitignore vendored
View File

@@ -14,7 +14,7 @@ Transceiver52M/osmo-trx-syncthing-ipc
Transceiver52M/osmo-trx-ms-blade
Transceiver52M/osmo-trx-ms-uhd
Transceiver52M/osmo-trx-ms-ipc
Transceiver52M/device/ipc/uhddev_ipc.cpp
.clang-format
@@ -38,6 +38,7 @@ Transceiver52M/device/ipc/ipc-driver-test
.deps
.libs
.dirstamp
.version
*~
Makefile
config.log
@@ -83,6 +84,7 @@ contrib/osmo-trx.spec
!contrib/osmo-trx.spec.in
utils/osmo-prbs-tool
utils/va-test/osmo-burst-gen
/.qtc_clangd/*
/.cache/*
/.vscode/*

3
.gitmodules vendored Normal file
View File

@@ -0,0 +1,3 @@
[submodule "osmocom-bb"]
path = osmocom-bb
url = https://gitea.osmocom.org/phone-side/osmocom-bb.git

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

@@ -312,7 +312,6 @@ static void threshold_timer_update_intv() {
return;
if (llist_empty(&threshold_list)) {
if (osmo_timer_pending(&threshold_timer))
osmo_timer_del(&threshold_timer);
return;
}

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

@@ -26,9 +26,14 @@ AM_CXXFLAGS = -Wall -pthread
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread
SUBDIRS =
if ENABLE_MS_TRX
SUBDIRS += $(LIBTRXCON_DIR)
endif
# Order must be preserved
SUBDIRS = \
trxcon \
SUBDIRS += \
CommonLibs \
GSM \
Transceiver52M \
@@ -38,12 +43,20 @@ SUBDIRS = \
doc \
$(NULL)
BUILT_SOURCES = $(top_srcdir)/.version
$(top_srcdir)/.version:
echo $(VERSION) > $@-t && mv $@-t $@
dist-hook:
echo $(VERSION) > $(distdir)/.tarball-version
EXTRA_DIST = \
.version \
LEGAL \
COPYING \
README.md \
contrib/osmo-trx.spec.in \
debian \
git-version-gen \
$(NULL)
AM_DISTCHECK_CONFIGURE_FLAGS = \

View File

@@ -28,7 +28,6 @@ STD_DEFINES_AND_INCLUDES = \
COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la
GSM_LA = $(top_builddir)/GSM/libGSM.la
TRXCON_LA = $(top_builddir)/trxcon/libtrxcon.la
if ARCH_ARM
ARCH_LA = $(top_builddir)/Transceiver52M/arch/arm/libarch.la

View File

@@ -27,9 +27,9 @@ GIT Repository
You can clone from the official osmo-trx.git repository using
git clone git://git.osmocom.org/osmo-trx.git
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-trx`
There is a cgit interface at <https://git.osmocom.org/osmo-trx/>
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-trx>
Documentation
-------------

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

@@ -24,13 +24,12 @@ include $(top_srcdir)/Makefile.common
SUBDIRS = arch device
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device/common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) -mcpu=cortex-a72 -mfloat-abi=hard -mfpu=neon-fp-armv8
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) -mcpu=cortex-a72 -mfloat-abi=hard -mfpu=neon-fp-armv8
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
noinst_LTLIBRARIES = libtransceiver_common.la
COMMON_SOURCES = \
l1if.cpp \
radioInterface.cpp \
radioVector.cpp \
radioClock.cpp \
@@ -42,7 +41,6 @@ COMMON_SOURCES = \
Channelizer.cpp \
Synthesis.cpp \
proto_trxd.c \
sch.c \
grgsm_vitac/grgsm_vitac.cpp \
grgsm_vitac/viterbi_detector.cc
@@ -65,9 +63,7 @@ noinst_HEADERS = \
ChannelizerBase.h \
Channelizer.h \
Synthesis.h \
proto_trxd.h \
grgsm_vitac/viterbi_detector.h \
grgsm_vitac/constants.h
proto_trxd.h
COMMON_LDADD = \
libtransceiver_common.la \
@@ -76,10 +72,50 @@ COMMON_LDADD = \
$(COMMON_LA) \
$(FFTWF_LIBS) \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCODING_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_LIBS)
if ENABLE_MS_TRX
AM_CPPFLAGS += -I$(top_srcdir)/osmocom-bb/src/host/trxcon/include/
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_LOWER_SRC = \
ms/sch.c \
ms/ms.cpp \
ms/threadsched.cpp \
ms/ms_rx_lower.cpp \
grgsm_vitac/grgsm_vitac.cpp \
grgsm_vitac/viterbi_detector.cc
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/threadsched.h \
ms/bladerf_specific.h \
ms/uhd_specific.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
endif
bin_PROGRAMS =
if DEVICE_UHD
@@ -91,23 +127,16 @@ osmo_trx_uhd_LDADD = \
$(UHD_LIBS)
osmo_trx_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
if ENABLE_MS_TRX
bin_PROGRAMS += osmo-trx-ms-uhd
osmo_trx_ms_uhd_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_upper.cpp ms/ms_commandhandler.cpp
osmo_trx_ms_uhd_SOURCES = $(MS_LOWER_SRC) $(MS_UPPER_SRC)
osmo_trx_ms_uhd_LDADD = \
$(builddir)/device/bladerf/libdevice.la \
$(builddir)/device/uhd/libdevice.la \
$(COMMON_LDADD) \
$(UHD_LIBS) \
$(TRXCON_LA)
$(TRXCON_LDADD)
osmo_trx_ms_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DBUILDUHD
bin_PROGRAMS += osmo-trx-syncthing-uhd
osmo_trx_syncthing_uhd_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_burst.cpp
osmo_trx_syncthing_uhd_LDADD = \
$(builddir)/device/bladerf/libdevice.la \
$(COMMON_LDADD) \
$(UHD_LIBS)
osmo_trx_syncthing_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DSYNCTHINGONLY -DBUILDUHD
#osmo_trx_syncthing_LDFLAGS = -fsanitize=address,undefined -shared-libsan
endif
endif
if DEVICE_USRP1
@@ -139,23 +168,16 @@ osmo_trx_blade_LDADD = \
$(BLADE_LIBS)
osmo_trx_blade_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
if ENABLE_MS_TRX
bin_PROGRAMS += osmo-trx-ms-blade
osmo_trx_ms_blade_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_upper.cpp ms/ms_commandhandler.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_LA)
$(TRXCON_LDADD)
osmo_trx_ms_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DBUILDBLADE
bin_PROGRAMS += osmo-trx-syncthing-blade
osmo_trx_syncthing_blade_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_burst.cpp
osmo_trx_syncthing_blade_LDADD = \
$(builddir)/device/bladerf/libdevice.la \
$(COMMON_LDADD) \
$(BLADE_LIBS)
osmo_trx_syncthing_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DSYNCTHINGONLY -DBUILDBLADE -mcpu=cortex-a72 -mfloat-abi=hard -mfpu=neon-fp-armv8 -I../device/ipc
#osmo_trx_syncthing_LDFLAGS = -fsanitize=address,undefined -shared-libsan
endif
endif
if DEVICE_IPC
@@ -165,32 +187,4 @@ osmo_trx_ipc_LDADD = \
$(builddir)/device/ipc/libdevice.la \
$(COMMON_LDADD)
osmo_trx_ipc_CPPFLAGS = $(AM_CPPFLAGS)
bin_PROGRAMS += osmo-trx-ipc2
osmo_trx_ipc2_SOURCES = osmo-trx.cpp
osmo_trx_ipc2_LDADD = \
$(builddir)/device/ipc2/libdevice.la \
$(COMMON_LDADD)
osmo_trx_ipc2_CPPFLAGS = $(AM_CPPFLAGS)
bin_PROGRAMS += osmo-trx-ms-ipc
osmo_trx_ms_ipc_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_upper.cpp ms/ms_commandhandler.cpp
osmo_trx_ms_ipc_LDADD = \
$(COMMON_LDADD) \
$(TRXCON_LA)
osmo_trx_ms_ipc_CPPFLAGS = $(AM_CPPFLAGS) -DBUILDIPC -I./device/ipc2 -I../device/ipc2
bin_PROGRAMS += osmo-trx-syncthing-ipc
osmo_trx_syncthing_ipc_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_burst.cpp
osmo_trx_syncthing_ipc_LDADD = $(COMMON_LDADD)
osmo_trx_syncthing_ipc_CPPFLAGS = $(AM_CPPFLAGS) -DSYNCTHINGONLY -DBUILDIPC -I./device/ipc2 -I../device/ipc2
endif
noinst_HEADERS += \
ms/syncthing.h \
ms/bladerf_specific.h \
ms/uhd_specific.h \
ms/ms_rx_upper.h \
itrq.h
# -fsanitize=address,undefined -shared-libsan -O0
#

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);
@@ -578,7 +581,7 @@ CorrType Transceiver::expectedCorrType(GSM::Time currTime,
case XIII: {
int mod52 = burstFN % 52;
if ((mod52 == 12) || (mod52 == 38))
return cfg->ext_rach ? EXT_RACH : RACH;
return RACH; /* RACH is always 8-bit on PTCCH/U */
else if ((mod52 == 25) || (mod52 == 51))
return IDLE;
else /* Enable 8-PSK burst detection if EDGE is enabled */
@@ -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;
@@ -902,19 +959,18 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
}
} else if (match_cmd(command, "SETMAXDLY", &params)) {
//set expected maximum time-of-arrival
//set expected maximum time-of-arrival for Access Bursts
int maxDelay;
sscanf(params, "%d", &maxDelay);
mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
} else if (match_cmd(command, "SETMAXDLYNB", &params)) {
//set expected maximum time-of-arrival
//set expected maximum time-of-arrival for Normal Bursts
int maxDelay;
sscanf(params, "%d", &maxDelay);
mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
} else if (match_cmd(command, "SETRXGAIN", &params)) {
//set expected maximum time-of-arrival
int newGain;
sscanf(params, "%d", &newGain);
newGain = mRadioInterface->setRxGain(newGain, chan);

View File

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

@@ -17,7 +17,6 @@
#include "convert.h"
__attribute__((xray_never_instrument))
void base_convert_float_short(short *out, const float *in,
float scale, int len)
{
@@ -25,7 +24,6 @@ void base_convert_float_short(short *out, const float *in,
out[i] = in[i] * scale;
}
__attribute__((xray_never_instrument))
void base_convert_short_float(float *out, const short *in, int len)
{
for (int i = 0; i < len; i++)

View File

@@ -24,7 +24,6 @@
#endif
/* Base multiply and accumulate complex-real */
__attribute__((xray_never_instrument))
static void mac_real(const float *x, const float *h, float *y)
{
y[0] += x[0] * h[0];
@@ -32,7 +31,6 @@ static void mac_real(const float *x, const float *h, float *y)
}
/* Base multiply and accumulate complex-complex */
__attribute__((xray_never_instrument))
static void mac_cmplx(const float *x, const float *h, float *y)
{
y[0] += x[0] * h[0] - x[1] * h[1];
@@ -40,7 +38,6 @@ static void mac_cmplx(const float *x, const float *h, float *y)
}
/* Base vector complex-complex multiply and accumulate */
__attribute__((xray_never_instrument))
static void mac_real_vec_n(const float *x, const float *h, float *y,
int len)
{
@@ -49,7 +46,6 @@ static void mac_real_vec_n(const float *x, const float *h, float *y,
}
/* Base vector complex-complex multiply and accumulate */
__attribute__((xray_never_instrument))
static void mac_cmplx_vec_n(const float *x, const float *h, float *y,
int len)
{
@@ -58,7 +54,6 @@ static void mac_cmplx_vec_n(const float *x, const float *h, float *y,
}
/* Base complex-real convolution */
__attribute__((xray_never_instrument))
int _base_convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
@@ -74,7 +69,6 @@ int _base_convolve_real(const float *x, int x_len,
}
/* Base complex-complex convolution */
__attribute__((xray_never_instrument))
int _base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
@@ -91,7 +85,6 @@ int _base_convolve_complex(const float *x, int x_len,
}
/* Buffer validity checks */
__attribute__((xray_never_instrument))
int bounds_check(int x_len, int h_len, int y_len,
int start, int len)
{
@@ -112,7 +105,6 @@ int bounds_check(int x_len, int h_len, int y_len,
}
/* API: Non-aligned (no SSE) complex-real */
__attribute__((xray_never_instrument))
int base_convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
@@ -130,7 +122,6 @@ int base_convolve_real(const float *x, int x_len,
}
/* API: Non-aligned (no SSE) complex-complex */
__attribute__((xray_never_instrument))
int base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
@@ -148,7 +139,6 @@ int base_convolve_complex(const float *x, int x_len,
}
/* Aligned filter tap allocation */
__attribute__((xray_never_instrument))
void *convolve_h_alloc(size_t len)
{
#ifdef HAVE_SSE3

View File

@@ -60,7 +60,6 @@ void convert_init(void)
#endif
}
__attribute__((xray_never_instrument))
void convert_float_short(short *out, const float *in, float scale, int len)
{
if (!(len % 16))
@@ -71,7 +70,6 @@ void convert_float_short(short *out, const float *in, float scale, int len)
c.convert_scale_ps_si16(out, in, scale, len);
}
__attribute__((xray_never_instrument))
void convert_short_float(float *out, const short *in, int len)
{
if (!(len % 16))

View File

@@ -26,7 +26,6 @@
#include <emmintrin.h>
/* 8*N single precision floats scaled and converted to 16-bit signed integer */
__attribute__((xray_never_instrument))
void _sse_convert_scale_ps_si16_8n(short *restrict out,
const float *restrict in,
float scale, int len)
@@ -55,7 +54,6 @@ void _sse_convert_scale_ps_si16_8n(short *restrict out,
}
/* 8*N single precision floats scaled and converted with remainder */
__attribute__((xray_never_instrument))
void _sse_convert_scale_ps_si16(short *restrict out,
const float *restrict in, float scale, int len)
{
@@ -68,7 +66,6 @@ void _sse_convert_scale_ps_si16(short *restrict out,
}
/* 16*N single precision floats scaled and converted to 16-bit signed integer */
__attribute__((xray_never_instrument))
void _sse_convert_scale_ps_si16_16n(short *restrict out,
const float *restrict in,
float scale, int len)

View File

@@ -25,7 +25,6 @@
#include <smmintrin.h>
/* 16*N 16-bit signed integer converted to single precision floats */
__attribute__((xray_never_instrument))
void _sse_convert_si16_ps_16n(float *restrict out,
const short *restrict in, int len)
{
@@ -60,7 +59,6 @@ void _sse_convert_si16_ps_16n(float *restrict out,
}
/* 16*N 16-bit signed integer conversion with remainder */
__attribute__((xray_never_instrument))
void _sse_convert_si16_ps(float *restrict out,
const short *restrict in, int len)
{

View File

@@ -91,7 +91,6 @@ void convolve_init(void)
}
/* API: Aligned complex-real */
__attribute__((xray_never_instrument))
int convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len, int start, int len)
@@ -131,7 +130,6 @@ int convolve_real(const float *x, int x_len,
}
/* API: Aligned complex-complex */
__attribute__((xray_never_instrument))
int convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,

View File

@@ -27,7 +27,6 @@
#include <pmmintrin.h>
/* 4-tap SSE complex-real convolution */
__attribute__((xray_never_instrument))
void sse_conv_real4(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
@@ -69,7 +68,6 @@ void sse_conv_real4(const float *x, int x_len,
}
/* 8-tap SSE complex-real convolution */
__attribute__((xray_never_instrument))
void sse_conv_real8(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
@@ -121,7 +119,6 @@ void sse_conv_real8(const float *x, int x_len,
}
/* 12-tap SSE complex-real convolution */
__attribute__((xray_never_instrument))
void sse_conv_real12(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
@@ -188,7 +185,6 @@ void sse_conv_real12(const float *x, int x_len,
}
/* 16-tap SSE complex-real convolution */
__attribute__((xray_never_instrument))
void sse_conv_real16(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
@@ -268,7 +264,6 @@ void sse_conv_real16(const float *x, int x_len,
}
/* 20-tap SSE complex-real convolution */
__attribute__((xray_never_instrument))
void sse_conv_real20(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
@@ -359,7 +354,6 @@ void sse_conv_real20(const float *x, int x_len,
}
/* 4*N-tap SSE complex-real convolution */
__attribute__((xray_never_instrument))
void sse_conv_real4n(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
@@ -407,7 +401,6 @@ void sse_conv_real4n(const float *x, int x_len,
}
/* 4*N-tap SSE complex-complex convolution */
__attribute__((xray_never_instrument))
void sse_conv_cmplx_4n(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
@@ -466,7 +459,6 @@ void sse_conv_cmplx_4n(const float *x, int x_len,
}
/* 8*N-tap SSE complex-complex convolution */
__attribute__((xray_never_instrument))
void sse_conv_cmplx_8n(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,

View File

@@ -3,7 +3,7 @@ include $(top_srcdir)/Makefile.common
SUBDIRS = common
if DEVICE_IPC
SUBDIRS += ipc ipc2
SUBDIRS += ipc
endif
if DEVICE_USRP1

View File

@@ -37,77 +37,27 @@ extern "C" {
#include <osmocom/vty/cpu_sched_vty.h>
}
#define USRP_TX_AMPL 0.3
#define UMTRX_TX_AMPL 0.7
#define LIMESDR_TX_AMPL 0.3
#define SAMPLE_BUF_SZ (1 << 20)
/*
* UHD timeout value on streaming (re)start
*
* Allow some time for streaming to commence after the start command is issued,
* but consider a wait beyond one second to be a definite error condition.
*/
#define UHD_RESTART_TIMEOUT 1.0
/*
* UmTRX specific settings
*/
#define UMTRX_VGA1_DEF -18
/*
* USRP version dependent device timings
*/
#define B2XX_TIMING_1SPS 1.7153e-4
#define B2XX_TIMING_4SPS 1.1696e-4
#define B2XX_TIMING_4_4SPS 6.18462e-5
#define B2XX_TIMING_MCBTS 7e-5
#define CHKRET() { \
#define CHKRET() \
{ \
if (status != 0) \
fprintf(stderr, "%s:%s:%d %s\n", __FILE__,__FUNCTION__, __LINE__, bladerf_strerror(status)); \
LOGC(DDEV, ERROR) << bladerf_strerror(status); \
}
/*
* Tx / Rx sample offset values. In a perfect world, there is no group delay
* though analog components, and behaviour through digital filters exactly
* matches calculated values. In reality, there are unaccounted factors,
* which are captured in these empirically measured (using a loopback test)
* timing correction values.
*
* Notes:
* USRP1 with timestamps is not supported by UHD.
*/
/* 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 {
{ std::make_tuple(blade_dev_type::BLADE2, 1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS" } },
{ std::make_tuple(blade_dev_type::BLADE2, 4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } },
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 } },
{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_1900), { 89.75, 7.7, -11.0 } },
};
/* So far measurements done for B210 show really close to linear relationship
* between gain and real output power, so we simply adjust the measured offset
*/
@@ -120,15 +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)
{
}
@@ -145,57 +90,14 @@ 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;
int status;
const struct bladerf_range *r;
bladerf_get_gain_range(dev, BLADERF_RX, &r);
rx_gain_min = r->min;
rx_gain_max = r->max;
LOGC(DDEV, INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
@@ -208,19 +110,21 @@ void blade_device::init_gains()
bladerf_get_gain_mode(dev, BLADERF_CHANNEL_RX(i), &m);
LOGC(DDEV, INFO) << (m == BLADERF_GAIN_MANUAL ? "gain manual" : "gain AUTO");
status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0);//gain);
status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0);
CHKRET()
int actual_gain;
status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain);
CHKRET()
LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain << " scale " << r->scale << " actual " << actual_gain;
LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain << " scale "
<< r->scale << " actual " << actual_gain;
rx_gains[i] = actual_gain;
status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0);//gain);
status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0);
CHKRET()
status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain);
CHKRET()
LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain << " scale " << r->scale << " actual " << actual_gain;
LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain << " scale "
<< r->scale << " actual " << actual_gain;
rx_gains[i] = actual_gain;
}
@@ -232,40 +136,35 @@ void blade_device::init_gains()
for (size_t i = 0; i < tx_gains.size(); i++) {
double gain = (tx_gain_min + tx_gain_max) / 2;
status = bladerf_set_gain(dev, BLADERF_CHANNEL_TX(i), 30);//gain);
status = bladerf_set_gain(dev, BLADERF_CHANNEL_TX(i), 30);
CHKRET()
int actual_gain;
status = bladerf_get_gain(dev, BLADERF_CHANNEL_TX(i), &actual_gain);
CHKRET()
LOGC(DDEV, INFO) << "Default setting Tx gain for channel " << i << " to " << gain << " scale " << r->scale << " actual " << actual_gain;
LOGC(DDEV, INFO) << "Default setting Tx gain for channel " << i << " to " << gain << " scale "
<< r->scale << " actual " << actual_gain;
tx_gains[i] = actual_gain;
}
return;
}
void blade_device::set_rates()
{
//dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
struct bladerf_rational_rate rate = { 0, static_cast<uint64_t>((1625e3 * 4)), 6 }, actual;
auto status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_RX(0), &rate, &actual);
CHKRET()
status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_TX(0), &rate, &actual);
CHKRET()
tx_rate = rx_rate = (double)rate.num / (double)rate.den;
LOGC(DDEV, INFO) << "Rates set to" << tx_rate << " / " << rx_rate;
bladerf_set_bandwidth(dev, BLADERF_CHANNEL_RX(0), (bladerf_bandwidth)2e6, (bladerf_bandwidth *)NULL);
bladerf_set_bandwidth(dev, BLADERF_CHANNEL_TX(0), (bladerf_bandwidth)2e6, (bladerf_bandwidth *)NULL);
ts_offset = 60;//static_cast<TIMESTAMP>(desc.offset * rx_rate);
//LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
ts_offset = 60; // FIXME: actual blade offset, should equal b2xx
}
double blade_device::setRxGain(double db, size_t chan)
@@ -311,7 +210,8 @@ double blade_device::rssiOffset(size_t chan)
return rssiOffset;
}
double blade_device::setPowerAttenuation(int atten, size_t chan) {
double blade_device::setPowerAttenuation(int atten, size_t chan)
{
double tx_power, db;
dev_band_desc desc;
@@ -324,19 +224,20 @@ double blade_device::setPowerAttenuation(int atten, size_t chan) {
tx_power = desc.nom_out_tx_power - atten;
db = TxPower2TxGain(desc, tx_power);
bladerf_set_gain(dev, BLADERF_CHANNEL_TX(chan), 30);//db);
bladerf_set_gain(dev, BLADERF_CHANNEL_TX(chan), 30);
int actual_gain;
bladerf_get_gain(dev, BLADERF_CHANNEL_RX(chan), &actual_gain);
tx_gains[chan] = actual_gain;
LOGC(DDEV, INFO) << "Set TX gain to " << tx_gains[chan] << "dB, ~"
<< TxGain2TxPower(desc, tx_gains[chan]) << " dBm "
LOGC(DDEV, INFO)
<< "Set TX gain to " << tx_gains[chan] << "dB, ~" << TxGain2TxPower(desc, tx_gains[chan]) << " dBm "
<< "(asked for " << db << " dB, ~" << tx_power << " dBm)";
return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
}
double blade_device::getPowerAttenuation(size_t chan) {
double blade_device::getPowerAttenuation(size_t chan)
{
dev_band_desc desc;
if (chan >= tx_gains.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
@@ -355,17 +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;
@@ -380,20 +279,19 @@ 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);
// Use the first found device
LOGC(DDEV, INFO) << "Using discovered bladerf device " << info.serial;
tx_freqs.resize(chans);
rx_freqs.resize(chans);
tx_gains.resize(chans);
rx_gains.resize(chans);
rx_buffers.resize(chans);
switch (ref) {
switch (cfg->clock_ref) {
case REF_INTERNAL:
case REF_EXTERNAL:
break;
@@ -402,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()
@@ -421,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();
@@ -438,38 +337,28 @@ int blade_device::open(const std::string &args, int ref, bool swap_channels)
const unsigned int num_transfers = 32;
const unsigned int timeout_ms = 3500;
bladerf_sync_config(dev, BLADERF_RX_X1, BLADERF_FORMAT_SC16_Q11_META,
num_buffers, buffer_size, num_transfers,
bladerf_sync_config(dev, BLADERF_RX_X1, BLADERF_FORMAT_SC16_Q11_META, num_buffers, buffer_size, num_transfers,
timeout_ms);
bladerf_sync_config(dev, BLADERF_TX_X1, BLADERF_FORMAT_SC16_Q11_META,
num_buffers, buffer_size, num_transfers,
bladerf_sync_config(dev, BLADERF_TX_X1, BLADERF_FORMAT_SC16_Q11_META, num_buffers, buffer_size, num_transfers,
timeout_ms);
/* Number of samples per over-the-wire packet */
tx_spp = rx_spp = buffer_size;
// Create receive buffer
size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i] = new smpl_buf(buf_len);
// Create vector buffer
pkt_bufs = std::vector<std::vector<short> >(chans, std::vector<short>(2 * rx_spp));
for (size_t i = 0; i < pkt_bufs.size(); i++)
pkt_ptrs.push_back(&pkt_bufs[i].front());
// Initialize and shadow gain values
init_gains();
return NORMAL;
}
bool blade_device::restart()
{
/* Allow 100 ms delay to align multi-channel streams */
@@ -498,11 +387,9 @@ bool blade_device::start()
return false;
}
// Start streaming
if (!restart())
return false;
started = true;
return true;
}
@@ -516,14 +403,13 @@ 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;
}
int blade_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun)
int blade_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun)
{
ssize_t rc;
uint64_t ts;
@@ -550,33 +436,25 @@ int blade_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun
return 0;
}
struct bladerf_metadata meta = {};
meta.timestamp = ts;
//static bool first_rx = true;
// meta.timestamp = (first_rx) ? ts : 0;
// meta.flags = (!first_rx) ? 0:BLADERF_META_FLAG_RX_NOW;
// if(first_rx)
// first_rx = false;
// Receive samples from the usrp until we have enough
while (rx_buffers[0]->avail_smpls(timestamp) < len) {
thread_enable_cancel(false);
int status = bladerf_sync_rx(dev, pkt_ptrs[0], len, &meta, 200U);
thread_enable_cancel(true);
if (status != 0)
std::cerr << "RX fucked: " << bladerf_strerror(status);
LOGC(DDEV, ERROR) << "RX broken: " << bladerf_strerror(status);
if (meta.flags & BLADERF_META_STATUS_OVERRUN)
std::cerr << "RX fucked OVER: " << bladerf_strerror(status);
LOGC(DDEV, ERROR) << "RX borken, OVERRUN: " << bladerf_strerror(status);
size_t num_smpls = meta.actual_count;
; ts = meta.timestamp;
;
ts = meta.timestamp;
for (size_t i = 0; i < rx_buffers.size(); i++) {
rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(),
num_smpls,
ts);
rc = rx_buffers[i]->write((short *)&pkt_bufs[i].front(), num_smpls, ts);
// Continue on local overrun, exit on other errors
if ((rc < 0)) {
@@ -590,7 +468,6 @@ int blade_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun
meta.timestamp = ts + num_smpls;
}
// We have enough samples
for (size_t i = 0; i < rx_buffers.size(); i++) {
rc = rx_buffers[i]->read(bufs[i], len, timestamp);
if ((rc < 0) || (rc != len)) {
@@ -603,8 +480,7 @@ int blade_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun
return len;
}
int blade_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
unsigned long long timestamp)
int blade_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun, unsigned long long timestamp)
{
*underrun = false;
static bool first_tx = true;
@@ -617,13 +493,10 @@ int blade_device::writeSamples(std::vector<short *> &bufs, int len, bool *underr
thread_enable_cancel(false);
int status = bladerf_sync_tx(dev, (const void *)bufs[0], len, &meta, 200U);
//size_t num_smpls = tx_stream->send(bufs, len, metadata);
thread_enable_cancel(true);
if (status != 0)
std::cerr << "TX fucked: " << bladerf_strerror(status);
// LOGCHAN(0, DDEV, INFO) << "tx " << timestamp << " " << len << " t+l: "<< timestamp+len << std::endl;
LOGC(DDEV, ERROR) << "TX broken: " << bladerf_strerror(status);
return len;
}
@@ -635,7 +508,6 @@ bool blade_device::updateAlignment(TIMESTAMP timestamp)
bool blade_device::set_freq(double freq, size_t chan, bool tx)
{
if (tx) {
bladerf_set_frequency(dev, BLADERF_CHANNEL_TX(chan), freq);
bladerf_frequency f;
@@ -649,33 +521,18 @@ bool blade_device::set_freq(double freq, size_t chan, bool tx)
}
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << (tx ? "TX" : "RX") << "): " << std::endl;
return true;
}
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))
@@ -686,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);
@@ -737,11 +580,8 @@ bool blade_device::requiresRadioAlign()
return false;
}
GSM::Time blade_device::minLatency() {
/* Empirical data from a handful of
relatively recent machines shows that the B100 will underrun when
the transmit threshold is reduced to a time of 6 and a half frames,
so we set a minimum 7 frame threshold. */
GSM::Time blade_device::minLatency()
{
return GSM::Time(6, 7);
}
@@ -765,13 +605,7 @@ double blade_device::fullScaleOutputValue()
return (double)2047;
}
#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 blade_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
return new blade_device(type, cfg);
}
#endif

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,12 +35,7 @@ extern "C" {
#include <osmocom/gsm/gsm_utils.h>
}
#include <bladerf.h>
enum class blade_dev_type {
BLADE1,
BLADE2
};
enum class blade_dev_type { BLADE1, BLADE2 };
struct dev_band_desc {
/* Maximum UHD Tx Gain which can be set/used without distorting the
@@ -55,25 +52,37 @@ 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();
enum TxWindowType getWindowType() { return tx_window; }
enum TxWindowType getWindowType()
{
return tx_window;
}
int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun);
int readSamples(std::vector<short *> &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun);
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp);
int writeSamples(std::vector<short *> &bufs, int len, bool *underrun, TIMESTAMP timestamp);
bool updateAlignment(TIMESTAMP timestamp);
@@ -88,8 +97,14 @@ public:
double setRxGain(double db, size_t chan);
double getRxGain(size_t chan);
double maxRxGain(void) { return rx_gain_max; }
double minRxGain(void) { return rx_gain_min; }
double maxRxGain(void)
{
return rx_gain_max;
}
double minRxGain(void)
{
return rx_gain_min;
}
double rssiOffset(size_t chan);
double setPowerAttenuation(int atten, size_t chan);
@@ -101,16 +116,31 @@ public:
double getRxFreq(size_t chan);
double getRxFreq();
bool setRxAntenna(const std::string &ant, size_t chan) { return {};};
std::string getRxAntenna(size_t chan) { return {};};
bool setTxAntenna(const std::string &ant, size_t chan) { return {};};
std::string getTxAntenna(size_t chan) { return {};};
bool setRxAntenna(const std::string &ant, size_t chan)
{
return {};
};
std::string getRxAntenna(size_t chan)
{
return {};
};
bool setTxAntenna(const std::string &ant, size_t chan)
{
return {};
};
std::string getTxAntenna(size_t chan)
{
return {};
};
bool requiresRadioAlign();
GSM::Time minLatency();
inline double getSampleRate() { return tx_rate; }
inline double getSampleRate()
{
return tx_rate;
}
/** Receive and process asynchronous message
@return true if message received or false on timeout or error
@@ -137,9 +167,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;
@@ -162,9 +189,6 @@ protected:
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,15 +161,22 @@ 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)
#define charp2str(a) ((a) ? std::string(a) : std::string(""))
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: "<< chan_num << " logical chans -> 1 physical chans";
LOGC(DDEV, INFO) << "Multi-ARFCN: " << chans << " logical chans -> 1 physical chans";
chans = 1;
}

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;
@@ -199,7 +198,7 @@ class IPCDevice : public RadioDevice {
virtual double minRxGain(void) override;
/* FIXME: return rx_gains[chan] ? receive factor from IPC Driver? */
double rssiOffset(size_t chan) { return 0.0f; };
double rssiOffset(size_t chan) override { return 0.0f; };
double setPowerAttenuation(int atten, size_t chan) override;
double getPowerAttenuation(size_t chan = 0) 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,30 +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
#work around distclean issue on older autotools vers:
#a direct build of ../uhd/UHDDevice.cpp tries to clean
#../uhd/.dep/UHDDevice.Plo twice and fails
uhddev_ipc.cpp:
echo "#include \"../uhd/UHDDevice.cpp\"" >$@
CLEANFILES= uhddev_ipc.cpp
BUILT_SOURCES = uhddev_ipc.cpp
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 uhddev_ipc.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

@@ -1,314 +0,0 @@
/*
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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/>.
* See the COPYING file in the main directory for details.
*/
#include <sys/time.h>
#include <osmocom/core/timer_compat.h>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "Logger.h"
#include "Threads.h"
#include "IPCDevice.h"
#include "smpl_buf.h"
#define SAMPLE_BUF_SZ (1 << 20)
static const auto ONE_BIT_DURATION ((12./5200.)/(156.25*4.));
static const auto ONE_SAMPLE_DURATION_US ((ONE_BIT_DURATION/4.)*1000*1000);
using namespace std;
IPCDevice2::IPCDevice2(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_buffers(chans),
started(false), tx_gains(chans), rx_gains(chans)
{
LOGC(DDEV, INFO) << "creating IPC device...";
if (!(tx_sps == 4) || !(rx_sps == 4)) {
LOGC(DDEV, FATAL) << "IPC shm if create failed!";
exit(0);
}
/* Set up per-channel Rx timestamp based Ring buffers */
for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
if (!m.create()) {
LOGC(DDEV, FATAL) << "IPC shm if create failed!";
exit(0);
}
}
IPCDevice2::~IPCDevice2()
{
LOGC(DDEV, INFO) << "Closing IPC device";
/* disable all channels */
for (size_t i = 0; i < rx_buffers.size(); i++)
delete rx_buffers[i];
}
int IPCDevice2::open(const std::string &args, int ref, bool swap_channels)
{
std::string k, v;
/* configure antennas */
if (!set_antennas()) {
LOGC(DDEV, FATAL) << "IPC antenna setting failed";
goto out_close;
}
return iface == MULTI_ARFCN ? MULTI_ARFCN : NORMAL;
out_close:
LOGC(DDEV, FATAL) << "Error in IPC open, closing";
return -1;
}
bool IPCDevice2::start()
{
LOGC(DDEV, INFO) << "starting IPC...";
if (started) {
LOGC(DDEV, ERR) << "Device already started";
return true;
}
int max_bufs_to_flush = 120;
flush_recv(max_bufs_to_flush);
started = true;
return true;
}
bool IPCDevice2::stop()
{
if (!started)
return true;
LOGC(DDEV, NOTICE) << "All channels stopped, terminating...";
/* reset internal buffer timestamps */
for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i]->reset();
started = false;
return true;
}
double IPCDevice2::maxRxGain()
{
return 70;
}
double IPCDevice2::minRxGain()
{
return 0;
}
int IPCDevice2::getNominalTxPower(size_t chan)
{
return 10;
}
double IPCDevice2::setPowerAttenuation(int atten, size_t chan)
{
return atten;
}
double IPCDevice2::getPowerAttenuation(size_t chan)
{
return 0;
}
double IPCDevice2::setRxGain(double dB, size_t chan)
{
if (dB > maxRxGain())
dB = maxRxGain();
if (dB < minRxGain())
dB = minRxGain();
LOGCHAN(chan, DDEV, NOTICE) << "Setting RX gain to " << dB << " dB";
return dB;
}
bool IPCDevice2::flush_recv(size_t num_pkts)
{
ts_initial = 10000;
LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
return true;
}
bool IPCDevice2::setRxAntenna(const std::string &ant, size_t chan)
{
return true;
}
std::string IPCDevice2::getRxAntenna(size_t chan)
{
return "";
}
bool IPCDevice2::setTxAntenna(const std::string &ant, size_t chan)
{
return true;
}
std::string IPCDevice2::getTxAntenna(size_t chan)
{
return "";
}
bool IPCDevice2::requiresRadioAlign()
{
return false;
}
GSM::Time IPCDevice2::minLatency()
{
/* UNUSED */
return GSM::Time(0, 0);
}
/** Returns the starting write Timestamp*/
TIMESTAMP IPCDevice2::initialWriteTimestamp(void)
{
return ts_initial;
}
/** Returns the starting read Timestamp*/
TIMESTAMP IPCDevice2::initialReadTimestamp(void)
{
return ts_initial;
}
static timespec readtime, writetime;
static void wait_for_sample_time(timespec* last, unsigned int len) {
timespec ts, diff;
clock_gettime(CLOCK_MONOTONIC, &ts);
timespecsub(&ts, last, &diff);
auto elapsed_us = (diff.tv_sec * 1000000) + (diff.tv_nsec / 1000);
auto max_wait_us = ONE_SAMPLE_DURATION_US * len;
if(elapsed_us < max_wait_us)
usleep(max_wait_us-elapsed_us);
*last = ts;
}
// NOTE: Assumes sequential reads
int IPCDevice2::readSamples(std::vector<short *> &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun)
{
int rc, num_smpls; //, expect_smpls;
ssize_t avail_smpls;
unsigned int i = 0;
*overrun = false;
*underrun = false;
timestamp += 0;
/* Check that timestamp is valid */
rc = rx_buffers[0]->avail_smpls(timestamp);
if (rc < 0) {
LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc);
LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp);
return 0;
}
/* Receive samples from HW until we have enough */
while ((avail_smpls = rx_buffers[i]->avail_smpls(timestamp)) < len) {
uint64_t recv_timestamp = timestamp;
m.read_ul(len - avail_smpls, &recv_timestamp, reinterpret_cast<sample_t *>(bufs[0]));
num_smpls = len - avail_smpls;
wait_for_sample_time(&readtime, num_smpls);
if (num_smpls == -ETIMEDOUT)
continue;
LOGCHAN(i, DDEV, DEBUG)
"Received timestamp = " << (TIMESTAMP)recv_timestamp << " (" << num_smpls << ")";
rc = rx_buffers[i]->write(bufs[i], num_smpls, (TIMESTAMP)recv_timestamp);
if (rc < 0) {
LOGCHAN(i, DDEV, ERROR)
<< rx_buffers[i]->str_code(rc) << " num smpls: " << num_smpls << " chan: " << i;
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
if (rc != smpl_buf::ERROR_OVERFLOW)
return 0;
}
}
/* We have enough samples */
rc = rx_buffers[i]->read(bufs[i], len, timestamp);
if ((rc < 0) || (rc != len)) {
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_code(rc) << ". " << rx_buffers[i]->str_status(timestamp)
<< ", (len=" << len << ")";
return 0;
}
return len;
}
int IPCDevice2::writeSamples(std::vector<short *> &bufs, int len, bool *underrun, unsigned long long timestamp)
{
*underrun = false;
LOGCHAN(0, DDEV, DEBUG) << "send buffer of len " << len << " timestamp " << std::hex << timestamp;
// rc = ipc_shm_enqueue(shm_io_tx_streams[i], timestamp, len, (uint16_t *)bufs[i]);
m.write_dl(len, timestamp, reinterpret_cast<sample_t *>(bufs[0]));
wait_for_sample_time(&writetime, len);
return 0;
}
bool IPCDevice2::updateAlignment(TIMESTAMP timestamp)
{
return true;
}
bool IPCDevice2::setTxFreq(double wFreq, size_t chan)
{
return true;
}
bool IPCDevice2::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)
{
if (tx_sps != rx_sps) {
LOGC(DDEV, ERROR) << "IPC Requires tx_sps == rx_sps";
return NULL;
}
if (lo_offset != 0.0) {
LOGC(DDEV, ERROR) << "IPC doesn't support lo_offset";
return NULL;
}
return new IPCDevice2(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
}

View File

@@ -1,186 +0,0 @@
/*
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* SPDX-License-Identifier: AGPL-3.0+
*
* 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/>.
* See the COPYING file in the main directory for details.
*/
#ifndef _IPC_DEVICE_H_
#define _IPC_DEVICE_H_
#include <climits>
#include <string>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "radioDevice.h"
#include "ipcif.h"
class smpl_buf;
class IPCDevice2 : public RadioDevice {
trxmsif m;
protected:
std::vector<smpl_buf *> rx_buffers;
double actualSampleRate;
bool started;
TIMESTAMP ts_initial;
std::vector<double> tx_gains, rx_gains;
bool flush_recv(size_t num_pkts);
void update_stream_stats_rx(size_t chan, bool *overrun);
void update_stream_stats_tx(size_t chan, bool *underrun);
bool send_chan_wait_rsp(uint32_t chan, struct msgb *msg_to_send, uint32_t expected_rsp_msg_id);
bool send_all_chan_wait_rsp(uint32_t msgid_to_send, uint32_t msgid_to_expect);
public:
/** Object constructor */
IPCDevice2(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);
virtual ~IPCDevice2() override;
/** Instantiate the IPC */
virtual int open(const std::string &args, int ref, bool swap_channels) override;
/** Start the IPC */
virtual bool start() override;
/** Stop the IPC */
virtual bool stop() override;
/* FIXME: any != USRP1 will do for now... */
enum TxWindowType getWindowType() override
{
return TX_WINDOW_FIXED;
}
/**
Read samples from the IPC.
@param buf preallocated buf to contain read result
@param len number of samples desired
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
@param timestamp The timestamp of the first samples to be read
@param underrun Set if IPC does not have data to transmit, e.g. data not being sent fast enough
@return The number of samples actually read
*/
virtual int readSamples(std::vector<short *> &buf, int len, bool *overrun, TIMESTAMP timestamp = 0xffffffff,
bool *underrun = NULL) override;
/**
Write samples to the IPC.
@param buf Contains the data to be written.
@param len number of samples to write.
@param underrun Set if IPC does not have data to transmit, e.g. data not being sent fast enough
@param timestamp The timestamp of the first sample of the data buffer.
@return The number of samples actually written
*/
virtual int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
TIMESTAMP timestamp = 0xffffffff) override;
/** Update the alignment between the read and write timestamps */
virtual bool updateAlignment(TIMESTAMP timestamp) override;
/** Set the transmitter frequency */
virtual bool setTxFreq(double wFreq, size_t chan = 0) override;
/** Set the receiver frequency */
virtual bool setRxFreq(double wFreq, size_t chan = 0) override;
/** Returns the starting write Timestamp*/
virtual TIMESTAMP initialWriteTimestamp(void) override;
/** Returns the starting read Timestamp*/
virtual TIMESTAMP initialReadTimestamp(void) override;
/** returns the full-scale transmit amplitude **/
virtual double fullScaleInputValue() override
{
return (double)SHRT_MAX * 1;
}
/** returns the full-scale receive amplitude **/
virtual double fullScaleOutputValue() override
{
return (double)SHRT_MAX * 1;
}
/** sets the receive chan gain, returns the gain setting **/
virtual double setRxGain(double dB, size_t chan = 0) override;
/** get the current receive gain */
virtual double getRxGain(size_t chan = 0) override
{
return rx_gains[chan];
}
/** return maximum Rx Gain **/
virtual double maxRxGain(void) override;
/** return minimum Rx Gain **/
virtual double minRxGain(void) override;
/* FIXME: return rx_gains[chan] ? receive factor from IPC Driver? */
double rssiOffset(size_t chan) override
{
return 0.0f;
};
double setPowerAttenuation(int atten, size_t chan) override;
double getPowerAttenuation(size_t chan = 0) override;
virtual int getNominalTxPower(size_t chan = 0) override;
/** sets the RX path to use, returns true if successful and false otherwise */
virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) override;
/* return the used RX path */
virtual std::string getRxAntenna(size_t chan = 0) override;
/** sets the RX path to use, returns true if successful and false otherwise */
virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) override;
/* return the used RX path */
virtual std::string getTxAntenna(size_t chan = 0) override;
/** return whether user drives synchronization of Tx/Rx of USRP */
virtual bool requiresRadioAlign() override;
/** return whether user drives synchronization of Tx/Rx of USRP */
virtual GSM::Time minLatency() override;
/** Return internal status values */
virtual inline double getTxFreq(size_t chan = 0) override
{
return 0;
}
virtual inline double getRxFreq(size_t chan = 0) override
{
return 0;
}
virtual inline double getSampleRate() override
{
return actualSampleRate;
}
};
#endif // _IPC_DEVICE_H_

View File

@@ -1,14 +0,0 @@
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
noinst_HEADERS = IPCDevice.h
noinst_LTLIBRARIES = libdevice.la
libdevice_la_SOURCES = IPCDevice.cpp
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
libdevice_la_CXXFLAGS = $(AM_CXXFLAGS) -DIPCMAGIC

View File

@@ -1,161 +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/>.
*
*/
#pragma once
#include <atomic>
#include <complex>
#include <cassert>
#include "shmif.h"
const int max_ul_rdlen = 1024 * 10;
const int max_dl_rdlen = 1024 * 10;
using sample_t = std::complex<int16_t>;
struct shm_if {
std::atomic<bool> ms_connected;
struct {
shm::shmmutex m;
shm::shmcond c;
std::atomic<uint64_t> ts;
std::atomic<size_t> len_req; // <-
std::atomic<size_t> len_written; // ->
sample_t buffer[max_ul_rdlen];
} ul;
struct {
shm::shmmutex writemutex;
shm::shmcond rdy2write;
shm::shmmutex readmutex;
shm::shmcond rdy2read;
std::atomic<uint64_t> ts;
std::atomic<size_t> len_req;
std::atomic<size_t> len_written;
sample_t buffer[max_dl_rdlen];
} dl;
};
// unique up to signed_type/2 diff
template <typename A> auto unsigned_diff(A a, A b) -> typename std::make_signed<A>::type
{
using stype = typename std::make_signed<A>::type;
return (a > b) ? static_cast<stype>(a - b) : -static_cast<stype>(b - a);
};
class trxmsif {
shm::shm<shm_if> m;
shm_if *ptr;
int dl_readoffset;
int samp2byte(int v)
{
return v * sizeof(sample_t);
}
public:
trxmsif() : m("trx-ms-if"), dl_readoffset(0)
{
}
bool create()
{
m.create();
ptr = m.p();
return m.isgood();
}
bool connect()
{
m.open();
ptr = m.p();
ptr->ms_connected = true;
return m.isgood();
}
bool good()
{
return m.isgood();
}
void write_dl(size_t howmany, uint64_t write_ts, sample_t *inbuf)
{
auto &dl = ptr->dl;
auto buf = &dl.buffer[0];
// if (ptr->ms_connected != true)
// return;
assert(sizeof(dl.buffer) >= samp2byte(howmany));
{
shm::signal_guard g(dl.writemutex, dl.rdy2write, dl.rdy2read);
memcpy(buf, inbuf, samp2byte(howmany));
dl.ts = write_ts;
dl.len_written = howmany;
}
}
void read_dl(size_t howmany, uint64_t* read_ts, sample_t *outbuf)
{
auto &dl = ptr->dl;
auto buf = &dl.buffer[0];
size_t len_avail = dl.len_written;
uint64_t ts = dl.ts;
auto left_to_read = len_avail - dl_readoffset;
// no data, wait for new buffer, maybe some data left afterwards
if (!left_to_read) {
shm::signal_guard g(dl.readmutex, dl.rdy2read, dl.rdy2write);
*read_ts = dl.ts;
len_avail = dl.len_written;
dl_readoffset += howmany;
assert(len_avail >= howmany);
memcpy(outbuf, buf, samp2byte(howmany));
return;
}
*read_ts = dl.ts + dl_readoffset;
left_to_read = len_avail - dl_readoffset;
// data left from prev read
if (left_to_read >= howmany) {
memcpy(outbuf, buf, samp2byte(howmany));
dl_readoffset += howmany;
return;
} else {
memcpy(outbuf, buf, samp2byte(left_to_read));
dl_readoffset = 0;
auto still_left_to_read = howmany - left_to_read;
{
shm::signal_guard g(dl.readmutex, dl.rdy2read, dl.rdy2write);
len_avail = dl.len_written;
dl_readoffset += still_left_to_read;
assert(len_avail >= still_left_to_read);
memcpy(outbuf + left_to_read, buf, samp2byte(still_left_to_read));
}
}
}
void read_ul(size_t howmany, uint64_t* read_ts, sample_t *outbuf)
{
// if (ptr->ms_connected != true) {
memset(outbuf, 0, samp2byte(howmany));
return;
// }
}
};

View File

@@ -1,219 +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/>.
*
*/
#pragma once
#include <cstring>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <pthread.h>
#include <cerrno>
namespace shm
{
class shmmutex {
pthread_mutex_t mutex;
public:
shmmutex()
{
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
pthread_mutex_init(&mutex, &attr);
pthread_mutexattr_destroy(&attr);
}
~shmmutex()
{
pthread_mutex_destroy(&mutex);
}
void lock()
{
pthread_mutex_lock(&mutex);
}
bool try_lock()
{
return pthread_mutex_trylock(&mutex);
}
void unlock()
{
pthread_mutex_unlock(&mutex);
}
pthread_mutex_t *p()
{
return &mutex;
}
};
class shmcond {
pthread_cond_t cond;
public:
shmcond()
{
pthread_condattr_t attr;
pthread_condattr_init(&attr);
pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
pthread_cond_init(&cond, &attr);
pthread_condattr_destroy(&attr);
}
~shmcond()
{
pthread_cond_destroy(&cond);
}
void wait(shmmutex *lock)
{
pthread_cond_wait(&cond, lock->p());
}
void signal()
{
pthread_cond_signal(&cond);
}
void signal_all()
{
pthread_cond_broadcast(&cond);
}
};
template <typename IFT> class shm {
char shmname[512];
size_t IFT_sz = sizeof(IFT);
IFT *shmptr;
bool good;
int ipc_shm_setup(const char *shm_name)
{
int fd;
int rc;
void *ptr;
if ((fd = shm_open(shm_name, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) {
rc = -errno;
return rc;
}
if (ftruncate(fd, IFT_sz) < 0) {
rc = -errno;
shm_unlink(shm_name);
::close(fd);
}
if ((ptr = mmap(NULL, IFT_sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
rc = -errno;
shm_unlink(shm_name);
::close(fd);
}
shmptr = new (ptr) IFT(); //static_cast<IFT *>(ptr);
::close(fd);
return 0;
}
int ipc_shm_connect(const char *shm_name)
{
int fd;
int rc;
void *ptr;
if ((fd = shm_open(shm_name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)) < 0) {
rc = -errno;
return rc;
}
struct stat shm_stat;
if (fstat(fd, &shm_stat) < 0) {
rc = -errno;
shm_unlink(shm_name);
::close(fd);
}
if ((ptr = mmap(NULL, shm_stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
rc = -errno;
shm_unlink(shm_name);
::close(fd);
}
shmptr = static_cast<IFT *>(ptr);
::close(fd);
return 0;
}
public:
using IFT_t = IFT;
explicit shm(const char *name) : good(false)
{
strncpy((char *)shmname, name, 512);
}
void create()
{
if (ipc_shm_setup(shmname) == 0)
good = true;
}
void open()
{
if (ipc_shm_connect(shmname) == 0)
good = true;
}
bool isgood() const
{
return good;
}
void close()
{
if (isgood())
shm_unlink(shmname);
}
IFT *p()
{
return shmptr;
}
};
class signal_guard {
shmmutex &m;
shmcond &s;
public:
explicit signal_guard(shmmutex &m, shmcond &wait_for, shmcond &to_signal) : m(m), s(to_signal)
{
m.lock();
wait_for.wait(&m);
}
~signal_guard()
{
s.signal();
m.unlock();
}
};
} // namespace shm

View File

@@ -1 +0,0 @@
#include "../uhd/UHDDevice.cpp"

View File

@@ -1,255 +0,0 @@
/*
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Eric Wild <ewild@sysmocom.de>
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
*/
extern "C" {
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include "shm.h"
#include "ipc_shm.h"
#include "ipc-driver-test.h"
}
#include "../uhd/UHDDevice.h"
#include "uhdwrap.h"
#include "Logger.h"
#include "Threads.h"
#include "Utils.h"
int uhd_wrap::open(const std::string &args, int ref, bool swap_channels)
{
int rv = uhd_device::open(args, ref, swap_channels);
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();
wrap_rx_buffs = std::vector<std::vector<short> >(channel_count, std::vector<short>(2 * samps_per_buff_rx));
for (size_t i = 0; i < wrap_rx_buffs.size(); i++)
wrap_rx_buf_ptrs.push_back(&wrap_rx_buffs[i].front());
wrap_tx_buffs = std::vector<std::vector<short> >(channel_count, std::vector<short>(2 * 5000));
for (size_t i = 0; i < wrap_tx_buffs.size(); i++)
wrap_tx_buf_ptrs.push_back(&wrap_tx_buffs[i].front());
return rv;
}
uhd_wrap::~uhd_wrap()
{
// drvtest::gshutdown = 1;
//t->join();
}
size_t uhd_wrap::bufsizerx()
{
return samps_per_buff_rx;
}
size_t uhd_wrap::bufsizetx()
{
return samps_per_buff_tx;
}
int uhd_wrap::chancount()
{
return channel_count;
}
int uhd_wrap::wrap_read(TIMESTAMP *timestamp)
{
uhd::rx_metadata_t md;
size_t num_rx_samps = rx_stream->recv(wrap_rx_buf_ptrs, samps_per_buff_rx, md, 0.1, true);
*timestamp = md.time_spec.to_ticks(rx_rate);
return num_rx_samps; //uhd_device::readSamples(bufs, len, overrun, timestamp, underrun);
}
extern "C" void *uhdwrap_open(struct ipc_sk_if_open_req *open_req)
{
unsigned int rx_sps, tx_sps;
/* 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;
break;
case FEATURE_MASK_CLOCKREF_INTERNAL:
default:
cref = 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);
}
/* 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);
return uhd_wrap_dev;
}
extern "C" int32_t uhdwrap_get_bufsizerx(void *dev)
{
uhd_wrap *d = (uhd_wrap *)dev;
return d->bufsizerx();
}
extern "C" int32_t uhdwrap_get_timingoffset(void *dev)
{
uhd_wrap *d = (uhd_wrap *)dev;
return d->getTimingOffset();
}
extern "C" int32_t uhdwrap_read(void *dev, uint32_t num_chans)
{
TIMESTAMP t;
uhd_wrap *d = (uhd_wrap *)dev;
if (num_chans != d->wrap_rx_buf_ptrs.size()) {
perror("omg chans?!");
}
int32_t read = d->wrap_read(&t);
/* multi channel rx on b210 will return 0 due to alignment adventures, do not put 0 samples into a ipc buffer... */
if (read <= 0)
return read;
for (uint32_t i = 0; i < num_chans; i++) {
ipc_shm_enqueue(ios_rx_from_device[i], t, read, (uint16_t *)&d->wrap_rx_buffs[i].front());
}
return read;
}
extern "C" int32_t uhdwrap_write(void *dev, uint32_t num_chans, bool *underrun)
{
uhd_wrap *d = (uhd_wrap *)dev;
uint64_t timestamp;
int32_t len = -1;
for (uint32_t i = 0; i < num_chans; i++) {
len = ipc_shm_read(ios_tx_to_device[i], (uint16_t *)&d->wrap_tx_buffs[i].front(), 5000, &timestamp, 1);
if (len < 0)
return 0;
}
return d->writeSamples(d->wrap_tx_buf_ptrs, len, underrun, timestamp);
}
extern "C" double uhdwrap_set_freq(void *dev, double f, size_t chan, bool for_tx)
{
uhd_wrap *d = (uhd_wrap *)dev;
if (for_tx)
return d->setTxFreq(f, chan);
else
return d->setRxFreq(f, chan);
}
extern "C" double uhdwrap_set_gain(void *dev, double f, size_t chan, bool for_tx)
{
uhd_wrap *d = (uhd_wrap *)dev;
// if (for_tx)
// return d->setTxGain(f, chan);
// else
return d->setRxGain(f, chan);
}
extern "C" double uhdwrap_set_txatt(void *dev, double a, size_t chan)
{
uhd_wrap *d = (uhd_wrap *)dev;
return d->setPowerAttenuation(a, chan);
}
extern "C" int32_t uhdwrap_start(void *dev, int chan)
{
uhd_wrap *d = (uhd_wrap *)dev;
return d->start();
}
extern "C" int32_t uhdwrap_stop(void *dev, int chan)
{
uhd_wrap *d = (uhd_wrap *)dev;
return d->stop();
}
extern "C" void uhdwrap_fill_info_cnf(struct ipc_sk_if *ipc_prim)
{
struct ipc_sk_if_info_chan *chan_info;
uhd::device_addr_t args("");
uhd::device_addrs_t devs_found = uhd::device::find(args);
if (devs_found.size() < 1) {
std::cout << "\n No device found!";
exit(0);
}
uhd::usrp::multi_usrp::sptr usrp = uhd::usrp::multi_usrp::make(devs_found[0]);
auto rxchans = usrp->get_rx_num_channels();
auto txchans = usrp->get_tx_num_channels();
auto rx_range = usrp->get_rx_gain_range();
auto tx_range = usrp->get_tx_gain_range();
//auto nboards = usrp->get_num_mboards();
auto refs = usrp->get_clock_sources(0);
auto devname = usrp->get_mboard_name(0);
ipc_prim->u.info_cnf.feature_mask = 0;
if (std::find(refs.begin(), refs.end(), "internal") != refs.end())
ipc_prim->u.info_cnf.feature_mask |= FEATURE_MASK_CLOCKREF_INTERNAL;
if (std::find(refs.begin(), refs.end(), "external") != refs.end())
ipc_prim->u.info_cnf.feature_mask |= FEATURE_MASK_CLOCKREF_EXTERNAL;
// at least one duplex channel
auto num_chans = rxchans == txchans ? txchans : 1;
ipc_prim->u.info_cnf.iq_scaling_val_rx = 0.3;
ipc_prim->u.info_cnf.iq_scaling_val_tx = 1;
ipc_prim->u.info_cnf.max_num_chans = num_chans;
OSMO_STRLCPY_ARRAY(ipc_prim->u.info_cnf.dev_desc, devname.c_str());
chan_info = ipc_prim->u.info_cnf.chan_info;
for (unsigned int i = 0; i < ipc_prim->u.info_cnf.max_num_chans; i++) {
auto rxant = usrp->get_rx_antennas(i);
auto txant = usrp->get_tx_antennas(i);
for (unsigned int j = 0; j < txant.size(); j++) {
OSMO_STRLCPY_ARRAY(chan_info->tx_path[j], txant[j].c_str());
}
for (unsigned int j = 0; j < rxant.size(); j++) {
OSMO_STRLCPY_ARRAY(chan_info->rx_path[j], rxant[j].c_str());
}
chan_info->min_rx_gain = rx_range.start();
chan_info->max_rx_gain = rx_range.stop();
chan_info->min_tx_gain = tx_range.start();
chan_info->max_tx_gain = tx_range.stop();
chan_info->nominal_tx_power = 7.5; // FIXME: would require uhd dev + freq info
chan_info++;
}
}

View File

@@ -1,83 +0,0 @@
/*
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Eric Wild <ewild@sysmocom.de>
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef IPC_UHDWRAP_H
#define IPC_UHDWRAP_H
#ifdef __cplusplus
#include "../uhd/UHDDevice.h"
class uhd_wrap : public uhd_device {
public:
// std::thread *t;
size_t samps_per_buff_rx;
size_t samps_per_buff_tx;
int channel_count;
std::vector<std::vector<short> > wrap_rx_buffs;
std::vector<std::vector<short> > wrap_tx_buffs;
std::vector<short *> wrap_rx_buf_ptrs;
std::vector<short *> wrap_tx_buf_ptrs;
template <typename... Args> uhd_wrap(Args... args) : uhd_device(args...)
{
// t = new std::thread(magicthread);
// give the thread some time to start and set up
// std::this_thread::sleep_for(std::chrono::seconds(1));
}
virtual ~uhd_wrap();
// void ipc_sock_close() override {};
int wrap_read(TIMESTAMP *timestamp);
virtual int open(const std::string &args, int ref, bool swap_channels) override;
// bool start() override;
// bool stop() override;
// virtual TIMESTAMP initialWriteTimestamp() override;
// virtual TIMESTAMP initialReadTimestamp() override;
int getTimingOffset()
{
return ts_offset;
}
size_t bufsizerx();
size_t bufsizetx();
int chancount();
};
#else
void *uhdwrap_open(struct ipc_sk_if_open_req *open_req);
int32_t uhdwrap_get_bufsizerx(void *dev);
int32_t uhdwrap_get_timingoffset(void *dev);
int32_t uhdwrap_read(void *dev, uint32_t num_chans);
int32_t uhdwrap_write(void *dev, uint32_t num_chans, bool *underrun);
double uhdwrap_set_freq(void *dev, double f, size_t chan, bool for_tx);
double uhdwrap_set_gain(void *dev, double f, size_t chan, bool for_tx);
int32_t uhdwrap_start(void *dev, int chan);
int32_t uhdwrap_stop(void *dev, int chan);
void uhdwrap_fill_info_cnf(struct ipc_sk_if *ipc_prim);
double uhdwrap_set_txatt(void *dev, double a, size_t chan);
#endif
#endif // IPC_B210_H

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,19 +127,14 @@ 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(InterfaceType iface, const struct trx_cfg *cfg);
~LMSDevice();
/** Instantiate the LMS */
int open(const std::string &args, int ref, bool swap_channels);
int open();
/** Start the LMS */
bool start();
@@ -121,7 +142,8 @@ public:
/** Stop the LMS */
bool stop();
enum TxWindowType getWindowType() {
enum TxWindowType getWindowType()
{
return TX_WINDOW_LMS1;
}

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,13 +530,15 @@ 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);
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(InterfaceType iface, const struct trx_cfg *cfg);
~uhd_device();
int open(const std::string &args, int ref, bool swap_channels);
int open();
bool start();
bool stop();
bool restart();
enum TxWindowType getWindowType() { return tx_window; }
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) {
if (cfg->tx_sps != cfg->rx_sps) {
LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
return NULL;
}
if (chans != 1) {
if (cfg->num_chans != 1) {
LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
return NULL;
}
if (lo_offset != 0.0) {
if (cfg->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);
return new USRPDevice(type, cfg);
}

View File

@@ -104,12 +104,10 @@ 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);
int open();
/** Start the USRP */
bool start();
@@ -117,7 +115,10 @@ private:
/** 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

@@ -1,4 +1,25 @@
#pragma once
/* -*- c++ -*- */
/*
* @file
* @author (C) 2009-2017 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* Gr-gsm 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include <complex>
@@ -17,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)
@@ -52,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

@@ -2,6 +2,7 @@
/*
* @file
* @author (C) 2009-2017 by Piotr Krysik <ptrkrysik@gmail.com>
* @author Contributions by sysmocom - s.f.m.c. GmbH / Eric Wild <ewild@sysmocom.de>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
@@ -38,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
*
@@ -52,99 +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
void
detect_burst(const gr_complex* input,
gr_complex* chan_imp_resp, int burst_start,
unsigned 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] > 0;
for (unsigned int i = 0; i < burst_size; i++)
output_binary[i] = output[i] > 0 ? -127 : 127; // pre flip bits!
}
int process_vita_burst(gr_complex* input, int tsc, unsigned char* output_binary) {
gr_complex channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
int normal_burst_start, dummy_burst_start;
float dummy_corr_max, normal_corr_max;
dummy_burst_start = get_norm_chan_imp_resp(input,
&channel_imp_resp[0], &dummy_corr_max, TS_DUMMY);
normal_burst_start = get_norm_chan_imp_resp(input,
&channel_imp_resp[0], &normal_corr_max, tsc);
if (normal_corr_max > dummy_corr_max) {
/* Perform MLSE detection */
detect_burst(input, &channel_imp_resp[0],
normal_burst_start, output_binary);
return 0;
} else {
memcpy(output_binary, dummy_burst, 148);
//std::cerr << std::endl << "#NOPE#" << dd.fpath << std::endl << std::endl;
return -1;
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);
}
int process_vita_sc_burst(gr_complex* input, int tsc, unsigned char* output_binary, int* offset) {
gr_complex channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
/* Get channel impulse response */
int d_c0_burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]);
// *offset = d_c0_burst_start;
/* Perform MLSE detection */
detect_burst(input, &channel_imp_resp[0],
d_c0_burst_start, output_binary);
return 0;
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)
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;
@@ -161,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);
@@ -181,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);
@@ -192,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;
@@ -209,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);
// 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);
}
energy += (*iter_ii);
}
if (loop_end)
break;
window_energy_buffer.push_back(energy);
iter++;
}
/* 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);
@@ -281,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;
}
/*
@@ -299,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;
}
/*
@@ -320,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)
@@ -333,6 +300,6 @@ int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_r
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

@@ -1,17 +1,60 @@
#pragma once
/* -*- c++ -*- */
/*
* @file
* @author (C) 2009-2017 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option)
* any later version.
*
* Gr-gsm 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include <vector>
#include "constants.h"
/* may only be used for for the DEFINITIONS!
* see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91664
*/
#if defined(__has_attribute)
#if __has_attribute(target_clones) && defined(__x86_64) && false
#if __has_attribute(target_clones) && defined(__x86_64) && true
#define MULTI_VER_TARGET_ATTR __attribute__((target_clones("avx", "sse4.2", "sse3", "sse2", "sse", "default")))
#else
#define MULTI_VER_TARGET_ATTR
#endif
#endif
/* ... but apparently clang disagrees... */
#if defined(__clang__)
#define MULTI_VER_TARGET_ATTR_CLANGONLY MULTI_VER_TARGET_ATTR
#else
#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")))
#endif
#else
#define NO_UBSAN
#endif
#endif
#define SYNC_SEARCH_RANGE 30
const int d_OSR(4);
@@ -20,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
void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, unsigned 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);
@@ -29,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

@@ -1,124 +0,0 @@
#include <mutex>
#include <queue>
#include <deque>
#include <condition_variable>
#include <iostream>
extern "C" {
#include <unistd.h>
#include <sys/eventfd.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/select.h>
}
#include "l1if.h"
using namespace std;
using namespace std::chrono_literals;
template<typename Data>
class spsc_q{
std::queue<Data> m_q;
std::mutex m_mtx;
std::condition_variable m_cond;
bool killme;
public:
spsc_q() : killme{ false } { }
void push(Data i){
std::unique_lock<std::mutex> lock(m_mtx);
m_q.push(i);
m_cond.notify_one();
}
Data pop(){
std::unique_lock<std::mutex> lock(m_mtx);
m_cond.wait_for(lock, 100ms, [&](){ return !m_q.empty() || killme; });
if (killme || m_q.empty()){
return {};
}
Data x = m_q.front();
m_q.pop();
return x;
}
void stop(){
killme = true;
m_cond.notify_all();
}
auto sz() { return m_q.size(); }
};
/*
* trxif_from_trx_c <-> push_c
* trxif_to_trx_c <-> pop_c
* trxif_from_trx_d <-> push_d
* trxif_to_trx_d <-> pop_d
* ...
*
*
*/
class trxl1if {
public:
spsc_q<TRX_C*> c_to_trx;
spsc_q<TRX_C*> c_from_trx;
spsc_q<trxd_to_trx*> d_to_trx;
spsc_q<trxd_from_trx*> d_from_trx;
struct osmo_fd g_event_ofd_C;
struct osmo_fd g_event_ofd_D;
};
trxl1if trxif;
void push_c(TRX_C* i) {
uint64_t one = 1;
int rc;
trxif.c_from_trx.push(i);
// std::clog << trxif.c_from_trx.sz() << std::endl;
rc = ::write(trxif.g_event_ofd_C.fd, &one, sizeof(one));
return;
};
TRX_C* pop_c() {
return trxif.c_to_trx.pop();
};
void push_d(trxd_from_trx* i) {
uint64_t one = 1;
int rc;
trxif.d_from_trx.push(i);
rc = ::write(trxif.g_event_ofd_D.fd, &one, sizeof(one));
return;
};
trxd_to_trx* pop_d() {
return trxif.d_to_trx.pop();
};
extern "C" {
char* trxif_from_trx_c() {
uint64_t one = 1;
::read(trxif.g_event_ofd_C.fd, &one, sizeof(one));
return (char*)trxif.c_from_trx.pop();
}
void trxif_to_trx_c(char* msg) {
trxif.c_to_trx.push((TRX_C*)msg);
}
trxd_from_trx* trxif_from_trx_d() {
uint64_t one = 1;
::read(trxif.g_event_ofd_D.fd, &one, sizeof(one));
return trxif.d_from_trx.pop();
}
void trxif_to_trx_d(trxd_to_trx* msg) {
trxif.d_to_trx.push(msg);
}
struct osmo_fd* get_c_fd() { return &trxif.g_event_ofd_C;}
struct osmo_fd* get_d_fd() { return &trxif.g_event_ofd_D;}
}

View File

@@ -1,74 +0,0 @@
#ifdef __cplusplus
extern "C" {
#endif
#include <stdint.h>
#ifdef __cplusplus
}
#endif
/* ------------------------------------------------------------------------ */
/* Data interface handlers */
/* ------------------------------------------------------------------------ */
/* DATA interface */
/* */
/* Messages on the data interface carry one radio burst per UDP message. */
/* */
/* Received Data Burst: */
/* 1 byte timeslot index */
/* 4 bytes GSM frame number, BE */
/* 1 byte RSSI in -dBm */
/* 2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, BE */
/* 148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1" */
/* 2 bytes are not used, but being sent by OsmoTRX */
/* */
/* Transmit Data Burst: */
/* 1 byte timeslot index */
/* 4 bytes GSM frame number, BE */
/* 1 byte transmit level wrt ARFCN max, -dB (attenuation) */
/* 148 bytes output symbol values, 0 & 1 */
/* ------------------------------------------------------------------------ */
struct __attribute__((packed)) trxd_to_trx {
uint8_t ts;
uint32_t fn;
uint8_t txlev;
uint8_t symbols[148];
};
struct __attribute__((packed)) trxd_from_trx {
uint8_t ts;
uint32_t fn;
uint8_t rssi;
uint16_t toa;
uint8_t symbols[148];
uint8_t pad[2];
};
#define TRXC_BUF_SIZE 1024
struct TRX_C {
char cmd[TRXC_BUF_SIZE];
};
#ifdef __cplusplus
void push_c(TRX_C* i);
TRX_C* pop_c();
void push_d(trxd_from_trx* i);
trxd_to_trx* pop_d();
#else
char* trxif_from_trx_c();
void trxif_to_trx_c(char* msg);
struct trxd_from_trx* trxif_from_trx_d();
void trxif_to_trx_d(struct trxd_to_trx* msg);
struct osmo_fd* get_c_fd();
struct osmo_fd* get_d_fd();
#endif

View File

@@ -1,5 +1,4 @@
#pragma once
/*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
@@ -39,17 +38,18 @@ const size_t BLADE_NUM_BUFFERS = 32 * 1;
const size_t NUM_TRANSFERS = 16 * 2;
const int SAMPLE_SCALE_FACTOR = 15; // actually 16 but sigproc complains about clipping..
template <typename Arg, typename... Args> void doPrint(std::ostream &out, Arg &&arg, Args &&...args)
// 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)
{
out << '(' << std::forward<Arg>(arg);
using expander = int[];
(void)expander{ 0, (void(out << ',' << std::forward<Args>(args)), 0)... };
(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...);
// specialisation for funcs which return a value
template <class R, class... Args>
R exec_and_check(RvalFunc<R, Args...> func, const char *fname, const char *finame, const char *funcname, int line,
Args... args)
@@ -58,7 +58,7 @@ R exec_and_check(RvalFunc<R, Args...> func, const char *fname, const char *finam
if (rval != 0) {
std::cerr << ((rval >= 0) ? "OK:" : bladerf_strerror(rval)) << ':' << finame << ':' << line << ':'
<< funcname << ':' << fname;
doPrint(std::cerr, args...);
expand_args(std::cerr, args...);
}
return rval;
}
@@ -69,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;
@@ -78,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()
@@ -110,7 +112,7 @@ template <unsigned int SZ, blade_speed_buffer_type T> struct blade_otw_buffer {
int readall(blade_sample_type *outaddr)
{
blade_sample_type *addr = outaddr;
for (int i = 0; i < SZ; i++) {
for (unsigned int i = 0; i < SZ; i++) {
memcpy(addr, &m[i].d[0], actual_samples_per_msg() * sizeof(blade_sample_type));
addr += actual_samples_per_msg();
}
@@ -158,7 +160,7 @@ template <unsigned int SZ, blade_speed_buffer_type T> struct blade_otw_buffer {
{
assert(num <= actual_samples_per_buffer());
int len_rem = num;
for (int i = 0; i < SZ; i++) {
for (unsigned int i = 0; i < SZ; i++) {
m[i] = {};
m[i].ts = first_ts + i * actual_samples_per_msg();
if (len_rem) {
@@ -174,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;
};
@@ -183,16 +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_lower_threads_flag;
double rxfreq_cache, txfreq_cache;
struct ms_trx_config {
int tx_freq;
@@ -217,21 +223,23 @@ 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)
{
}
void close_device()
{
if (dev) {
if (rx_stream) {
bladerf_deinit_stream(rx_stream);
}
if (tx_stream) {
bladerf_deinit_stream(tx_stream);
}
if (rx_stream) {
bladerf_deinit_stream(rx_stream);
}
bladerf_enable_module(dev, BLADERF_MODULE_RX, false);
bladerf_enable_module(dev, BLADERF_MODULE_TX, false);
@@ -246,6 +254,7 @@ template <typename T> struct blade_hw {
bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_DEBUG);
bladerf_set_usb_reset_on_open(true);
blade_check(bladerf_open, &dev, "");
if (!dev) {
std::cerr << "open failed, device missing?" << std::endl;
@@ -260,7 +269,8 @@ template <typename T> struct blade_hw {
bool is_locked;
blade_check(bladerf_set_pll_enable, dev, true);
blade_check(bladerf_set_pll_refclk, dev, 10000000UL);
uint64_t refclock = 10000000UL;
blade_check(bladerf_set_pll_refclk, dev, refclock);
for (int i = 0; i < 20; i++) {
usleep(50 * 1000);
bladerf_get_pll_lock_state(dev, &is_locked);
@@ -273,15 +283,6 @@ template <typename T> struct blade_hw {
return -1;
}
// bladerf_sample_rate r = (1625e3 * 4)/6, act;
// blade_check(bladerf_set_sample_rate,dev, BLADERF_CHANNEL_RX(0), r, &act);
// blade_check(bladerf_set_sample_rate,dev, BLADERF_CHANNEL_TX(0), r, &act);
// auto ratrate = (1625e3 * 4) / 6;
// rate.integer = (uint32_t)ratrate;
// rate.den = 10000;
// rate.num = (ratrate - rate.integer) * rate.den;
blade_check(bladerf_set_rational_sample_rate, dev, BLADERF_CHANNEL_RX(0), &rate, &actual);
blade_check(bladerf_set_rational_sample_rate, dev, BLADERF_CHANNEL_TX(0), &rate, &actual);
@@ -294,46 +295,51 @@ 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)50);
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);
blade_check(bladerf_init_stream, &tx_stream, dev, gettxcb(txh), &buf_mgmt.tx_samples, BLADE_NUM_BUFFERS,
BLADERF_FORMAT_SC16_Q11_META, BLADE_BUFFER_SIZE, NUM_TRANSFERS, (void *)this);
for (int i = 0; i < BLADE_NUM_BUFFERS; i++) {
for (unsigned int i = 0; i < BLADE_NUM_BUFFERS; i++) {
auto cur_buffer = reinterpret_cast<tx_buf_q_type::elem_t *>(buf_mgmt.tx_samples);
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;
};
@@ -389,6 +395,9 @@ template <typename T> struct blade_hw {
static int to_skip = 0;
dev_buf_t *rcd = (dev_buf_t *)samples;
if (stop_lower_threads_flag)
return BLADERF_STREAM_SHUTDOWN;
if (to_skip < 120) // prevents weird overflows on startup
to_skip++;
else {
@@ -411,72 +420,60 @@ 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_lower_threads_flag)
return BLADERF_STREAM_SHUTDOWN;
return BLADERF_STREAM_NO_DATA;
};
}
auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
{
auto fn = [this] {
int status;
set_name_aff_sched("rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2);
status = bladerf_stream(rx_stream, BLADERF_RX_X1);
using thist = decltype(this);
auto fn = [](void *args) -> void * {
thist t = reinterpret_cast<thist>(args);
int status = 0;
if (!stop_lower_threads_flag)
status = bladerf_stream(t->rx_stream, BLADERF_RX_X1);
if (status < 0)
std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
return NULL;
return 0;
};
return fn;
}
auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
{
auto fn = [this] {
int status;
set_name_aff_sched("txrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1);
status = bladerf_stream(tx_stream, BLADERF_TX_X1);
using thist = decltype(this);
auto fn = [](void *args) -> void * {
thist t = reinterpret_cast<thist>(args);
int status = 0;
if (!stop_lower_threads_flag)
status = bladerf_stream(t->tx_stream, BLADERF_TX_X1);
if (status < 0)
std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
return NULL;
return 0;
};
return fn;
}
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);
rcd->write_n_burst(buffer, len, ts + rxtxdelay); // blade xa4 specific delay!
// blade_check(bladerf_submit_stream_buffer_nb, tx_stream, (void *)rcd, 100U);
blade_check(bladerf_submit_stream_buffer_nb, tx_stream, (void *)rcd);
}
void set_name_aff_sched(const char *name, int cpunum, int schedtype, int prio)
{
pthread_setname_np(pthread_self(), name);
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpunum, &cpuset);
auto rv = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
if (rv < 0) {
std::cerr << name << " affinity: errreur! " << std::strerror(errno);
return exit(0);
}
sched_param sch_params;
sch_params.sched_priority = prio;
rv = pthread_setschedparam(pthread_self(), schedtype, &sch_params);
if (rv < 0) {
std::cerr << name << " sched: errreur! " << std::strerror(errno);
return exit(0);
}
}
};

View File

@@ -1,259 +0,0 @@
#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 <complex>
#include <cstring>
#include <functional>
#include <iostream>
#include <thread>
#include <Timeval.h>
#include <vector>
#include <ipcif.h>
// typedef unsigned long long TIMESTAMP;
using blade_sample_type = std::complex<int16_t>;
const int SAMPLE_SCALE_FACTOR = 1;
struct uhd_buf_wrap {
uint64_t ts;
uint32_t num_samps;
blade_sample_type *buf;
auto actual_samples_per_buffer()
{
return num_samps;
}
long get_first_ts()
{
return ts; //md->time_spec.to_ticks(rxticks);
}
int readall(blade_sample_type *outaddr)
{
memcpy(outaddr, buf, num_samps * sizeof(blade_sample_type));
return num_samps;
}
int read_n(blade_sample_type *outaddr, int start, int num)
{
// assert(start >= 0);
auto to_read = std::min((int)num_samps - start, num);
// assert(to_read >= 0);
memcpy(outaddr, buf + start, to_read * sizeof(blade_sample_type));
return to_read;
}
};
using dev_buf_t = uhd_buf_wrap;
using bh_fn_t = std::function<int(dev_buf_t *)>;
template <typename T> struct ipc_hw {
// uhd::usrp::multi_usrp::sptr dev;
// uhd::rx_streamer::sptr rx_stream;
// uhd::tx_streamer::sptr tx_stream;
blade_sample_type *one_pkt_buf;
std::vector<blade_sample_type *> pkt_ptrs;
size_t rx_spp;
double rxticks;
const unsigned int rxFullScale, txFullScale;
const int rxtxdelay;
float rxgain, txgain;
trxmsif m;
virtual ~ipc_hw()
{
delete[] one_pkt_buf;
}
ipc_hw() : rxFullScale(32767), txFullScale(32767), rxtxdelay(-67)
{
}
bool tuneTx(double freq, size_t chan = 0)
{
msleep(25);
// dev->set_tx_freq(freq, chan);
msleep(25);
return true;
};
bool tuneRx(double freq, size_t chan = 0)
{
msleep(25);
// dev->set_rx_freq(freq, chan);
msleep(25);
return true;
};
bool tuneRxOffset(double offset, size_t chan = 0)
{
return true;
};
double setRxGain(double dB, size_t chan = 0)
{
rxgain = dB;
msleep(25);
// dev->set_rx_gain(dB, chan);
msleep(25);
return dB;
};
double setTxGain(double dB, size_t chan = 0)
{
txgain = dB;
msleep(25);
// dev->set_tx_gain(dB, chan);
msleep(25);
return dB;
};
int setPowerAttenuation(int atten, size_t chan = 0)
{
return atten;
};
int init_device(bh_fn_t rxh, bh_fn_t txh)
{
// std::thread([] {
// osmo_ctx_init("bernd");
// osmo_select_init();
// main_ipc();
// while (true)
// osmo_select_main(0);
// }).detach();
return m.connect() ? 0: -1;
}
void *rx_cb(bh_fn_t burst_handler)
{
void *ret;
static int to_skip = 0;
blade_sample_type pbuf[508 * 2];
uint64_t t;
int len = 508 * 2;
m.read_dl(508 * 2, &t, pbuf);
// auto len = ipc_shm_read(ios_tx_to_device[0], (uint16_t *)&pbuf, 508 * 2, &t, 1);
// if(len < 0) {
// std::cerr << "fuck, rx fail!" << std::endl;
// exit(0);
// }
// uhd::rx_metadata_t md;
// auto num_rx_samps = rx_stream->recv(pkt_ptrs.front(), rx_spp, md, 3.0, true);
// if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) {
// std::cerr << boost::format("Timeout while streaming") << std::endl;
// exit(0);
// }
// if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW) {
// std::cerr << boost::format("Got an overflow indication. Please consider the following:\n"
// " Your write medium must sustain a rate of %fMB/s.\n"
// " Dropped samples will not be written to the file.\n"
// " Please modify this example for your purposes.\n"
// " This message will not appear again.\n") %
// 1.f;
// exit(0);
// ;
// }
// if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) {
// std::cerr << str(boost::format("Receiver error: %s") % md.strerror());
// exit(0);
// }
dev_buf_t rcd = { t, static_cast<uint32_t>(len), pbuf };
if (to_skip < 120) // prevents weird overflows on startup
to_skip++;
else {
burst_handler(&rcd);
}
return ret;
}
auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
{
auto fn = [this, burst_handler] {
pthread_setname_np(pthread_self(), "rxrun");
// wait_for_shm_open();
// uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
// stream_cmd.stream_now = true;
// stream_cmd.time_spec = uhd::time_spec_t();
// rx_stream->issue_stream_cmd(stream_cmd);
while (1) {
rx_cb(burst_handler);
}
};
return fn;
}
auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
{
auto fn = [] {
// wait_for_shm_open();
// dummy
};
return fn;
}
void submit_burst_ts(blade_sample_type *buffer, int len, uint64_t ts)
{
// uhd::tx_metadata_t m = {};
// m.end_of_burst = true;
// m.start_of_burst = true;
// m.has_time_spec = true;
// m.time_spec = m.time_spec.from_ticks(ts + rxtxdelay, rxticks); // uhd specific b210 delay!
// std::vector<void *> ptrs(1, buffer);
// tx_stream->send(ptrs, len, m);
// uhd::async_metadata_t async_md;
// bool tx_ack = false;
// while (!tx_ack && tx_stream->recv_async_msg(async_md)) {
// tx_ack = (async_md.event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK);
// }
// std::cout << (tx_ack ? "yay" : "nay") << " " << async_md.time_spec.to_ticks(rxticks) << std::endl;
}
void set_name_aff_sched(const char *name, int cpunum, int schedtype, int prio)
{
pthread_setname_np(pthread_self(), name);
// cpu_set_t cpuset;
// CPU_ZERO(&cpuset);
// CPU_SET(cpunum, &cpuset);
// auto rv = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
// if (rv < 0) {
// std::cerr << name << " affinity: errreur! " << std::strerror(errno);
// return exit(0);
// }
// sched_param sch_params;
// sch_params.sched_priority = prio;
// rv = pthread_setschedparam(pthread_self(), schedtype, &sch_params);
// if (rv < 0) {
// std::cerr << name << " sched: errreur! " << std::strerror(errno);
// return exit(0);
// }
}
};

View File

@@ -1,473 +0,0 @@
/*
* Copyright 2020 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
* Author: Pau Espin Pedrol <pespin@sysmocom.de>
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
*/
#define _GNU_SOURCE
#include <pthread.h>
#include <debug.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <assert.h>
#include <sys/socket.h>
#include <sys/un.h>
#include <inttypes.h>
#include <sys/mman.h>
#include <sys/stat.h> /* For mode constants */
#include <fcntl.h> /* For O_* constants */
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include <osmocom/core/msgb.h>
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include <shm.h>
#include <ipc_shm.h>
#include <ipc_chan.h>
#include <ipc_sock.h>
#define DEFAULT_SHM_NAME "/osmo-trx-ipc-driver-shm2"
#define IPC_SOCK_PATH_PREFIX "/tmp"
static void *tall_ctx;
struct ipc_sock_state *global_ipc_sock_state;
/* 8 channels are plenty */
struct ipc_sock_state *global_ctrl_socks[8];
struct ipc_shm_io *ios_tx_to_device[8];
struct ipc_shm_io *ios_rx_from_device[8];
void *shm;
// void *global_dev;
static pthread_mutex_t wait_open_lock;
static pthread_cond_t wait_open_cond;
static struct ipc_shm_region *decoded_region;
static struct {
int msocknum;
char *ud_prefix_dir;
} cmdline_cfg = { 1, IPC_SOCK_PATH_PREFIX };
static const struct log_info_cat default_categories[] = {
[DMAIN] = {
.name = "DMAIN",
.color = NULL,
.description = "Main generic category",
.loglevel = LOGL_DEBUG,.enabled = 1,
},
[DDEV] = {
.name = "DDEV",
.description = "Device/Driver specific code",
.color = NULL,
.enabled = 1, .loglevel = LOGL_DEBUG,
},
};
const struct log_info log_infox = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
};
volatile int ipc_exit_requested = 0;
static int ipc_shm_setup(const char *shm_name, size_t shm_len)
{
int fd;
int rc;
LOGP(DMAIN, LOGL_NOTICE, "Opening shm path %s\n", shm_name);
if ((fd = shm_open(shm_name, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) {
LOGP(DMAIN, LOGL_ERROR, "shm_open %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_shm_open;
}
LOGP(DMAIN, LOGL_NOTICE, "Truncating %d to size %zu\n", fd, shm_len);
if (ftruncate(fd, shm_len) < 0) {
LOGP(DMAIN, LOGL_ERROR, "ftruncate %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_mmap;
}
LOGP(DMAIN, LOGL_NOTICE, "mmaping shared memory fd %d\n", fd);
if ((shm = mmap(NULL, shm_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
LOGP(DMAIN, LOGL_ERROR, "mmap %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_mmap;
}
LOGP(DMAIN, LOGL_NOTICE, "mmap'ed shared memory at addr %p\n", shm);
/* After a call to mmap(2) the file descriptor may be closed without affecting the memory mapping. */
close(fd);
return 0;
err_mmap:
shm_unlink(shm_name);
close(fd);
err_shm_open:
return rc;
}
struct msgb *ipc_msgb_alloc(uint8_t msg_type)
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
msg = msgb_alloc(sizeof(struct ipc_sk_if) + 1000, "ipc_sock_tx");
if (!msg)
return NULL;
msgb_put(msg, sizeof(struct ipc_sk_if) + 1000);
ipc_prim = (struct ipc_sk_if *)msg->data;
ipc_prim->msg_type = msg_type;
return msg;
}
static int ipc_tx_info_cnf()
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
msg = ipc_msgb_alloc(IPC_IF_MSG_INFO_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_if *)msg->data;
ipc_prim->u.info_cnf.feature_mask = FEATURE_MASK_CLOCKREF_EXTERNAL;
ipc_prim->u.info_cnf.iq_scaling_val_rx = 1;
ipc_prim->u.info_cnf.iq_scaling_val_tx = 1;
ipc_prim->u.info_cnf.max_num_chans = 1;
OSMO_STRLCPY_ARRAY(ipc_prim->u.info_cnf.dev_desc, "bernd");
struct ipc_sk_if_info_chan *chan_info = ipc_prim->u.info_cnf.chan_info;
OSMO_STRLCPY_ARRAY(chan_info->tx_path[0], "TX/RX");
OSMO_STRLCPY_ARRAY(chan_info->rx_path[0], "RX2");
chan_info->min_rx_gain = 0;
chan_info->max_rx_gain = 60;
chan_info->min_tx_gain = 0;
chan_info->max_tx_gain = 60;
chan_info->nominal_tx_power = 10;
return ipc_sock_send(msg);
}
static int ipc_tx_open_cnf(int rc, uint32_t num_chans, int32_t timingoffset)
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
struct ipc_sk_if_open_cnf_chan *chan_info;
unsigned int i;
msg = ipc_msgb_alloc(IPC_IF_MSG_OPEN_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_if *)msg->data;
ipc_prim->u.open_cnf.return_code = rc;
ipc_prim->u.open_cnf.path_delay = timingoffset; // 6.18462e-5 * 1625e3 / 6;
OSMO_STRLCPY_ARRAY(ipc_prim->u.open_cnf.shm_name, DEFAULT_SHM_NAME);
chan_info = ipc_prim->u.open_cnf.chan_info;
for (i = 0; i < num_chans; i++) {
snprintf(chan_info->chan_ipc_sk_path, sizeof(chan_info->chan_ipc_sk_path), "%s/ipc_sock%d_%d",
cmdline_cfg.ud_prefix_dir, cmdline_cfg.msocknum, i);
/* FIXME: dynamc chan limit, currently 8 */
if (i < 8)
ipc_sock_init(chan_info->chan_ipc_sk_path, &global_ctrl_socks[i], ipc_chan_sock_accept, i);
chan_info++;
}
return ipc_sock_send(msg);
}
int ipc_rx_greeting_req(struct ipc_sk_if_greeting *greeting_req)
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
msg = ipc_msgb_alloc(IPC_IF_MSG_GREETING_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_if *)msg->data;
ipc_prim->u.greeting_cnf.req_version =
greeting_req->req_version == IPC_SOCK_API_VERSION ? IPC_SOCK_API_VERSION : 0;
ipc_sock_send(msg);
return 0;
}
int ipc_rx_info_req(struct ipc_sk_if_info_req *info_req)
{
ipc_tx_info_cnf();
return 0;
}
int ipc_rx_open_req(struct ipc_sk_if_open_req *open_req)
{
/* calculate size needed */
unsigned int len;
unsigned int i;
// global_dev = uhdwrap_open(open_req);
/* b210 packet size is 2040, but our tx size is 2500, so just do *2 */
int shmbuflen = 5000 * 2;
len = ipc_shm_encode_region(NULL, open_req->num_chans, 4, shmbuflen);
/* Here we verify num_chans, rx_path, tx_path, clockref, etc. */
int rc = ipc_shm_setup(DEFAULT_SHM_NAME, len);
len = ipc_shm_encode_region((struct ipc_shm_raw_region *)shm, open_req->num_chans, 4, shmbuflen);
// LOGP(DMAIN, LOGL_NOTICE, "%s\n", osmo_hexdump((const unsigned char *)shm, 80));
/* set up our own copy of the decoded area, we have to do it here,
* since the uhd wrapper does not allow starting single channels
* additionally go for the producer init for both, so only we are responsible for the init, instead
* of splitting it with the client and causing potential races if one side uses it too early */
decoded_region = ipc_shm_decode_region(0, (struct ipc_shm_raw_region *)shm);
for (i = 0; i < open_req->num_chans; i++) {
// ios_tx_to_device[i] = ipc_shm_init_consumer(decoded_region->channels[i]->dl_stream);
ios_tx_to_device[i] = ipc_shm_init_producer(decoded_region->channels[i]->dl_stream);
ios_rx_from_device[i] = ipc_shm_init_producer(decoded_region->channels[i]->ul_stream);
}
ipc_tx_open_cnf(-rc, open_req->num_chans, 0);
return 0;
}
volatile bool ul_running = false;
volatile bool dl_running = false;
void *uplink_thread(void *x_void_ptr)
{
uint32_t chann = decoded_region->num_chans;
ul_running = true;
pthread_setname_np(pthread_self(), "uplink_rx");
while (!ipc_exit_requested) {
// int32_t read = uhdwrap_read(global_dev, chann);
// ipc_shm_enqueue(ios_rx_from_device[i], tstamp, num_rx_samps, (uint16_t *)&d->wrap_rx_buffs[i].front());
if (read < 0)
return 0;
}
return 0;
}
void *downlink_thread(void *x_void_ptr)
{
int chann = decoded_region->num_chans;
dl_running = true;
pthread_setname_np(pthread_self(), "downlink_tx");
while (!ipc_exit_requested) {
bool underrun;
// uhdwrap_write(global_dev, chann, &underrun);
// len = ipc_shm_read(ios_tx_to_device[i], (uint16_t *)&d->wrap_tx_buffs[i].front(), 5000, &timestamp, 1);
// return d->writeSamples(d->wrap_tx_buf_ptrs, len, underrun, timestamp);
}
return 0;
}
int ipc_rx_chan_start_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr)
{
struct msgb *msg;
struct ipc_sk_chan_if *ipc_prim;
int rc = 0;
// rc = uhdwrap_start(global_dev, chan_nr);
pthread_cond_broadcast(&wait_open_cond);
// /* no per-chan start/stop */
// if (!dl_running || !ul_running) {
// /* chan != first chan start will "fail", which is fine, usrp can't start/stop chans independently */
// if (rc) {
// LOGP(DMAIN, LOGL_INFO, "starting rx/tx threads.. req for chan:%d\n", chan_nr);
// pthread_t rx, tx;
// pthread_create(&rx, NULL, uplink_thread, 0);
// pthread_create(&tx, NULL, downlink_thread, 0);
// }
// } else
// LOGP(DMAIN, LOGL_INFO, "starting rx/tx threads request ignored.. req for chan:%d\n", chan_nr);
msg = ipc_msgb_alloc(IPC_IF_MSG_START_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
ipc_prim->u.start_cnf.return_code = rc ? 0 : -1;
return ipc_chan_sock_send(msg, chan_nr);
}
int ipc_rx_chan_stop_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr)
{
struct msgb *msg;
struct ipc_sk_chan_if *ipc_prim;
int rc = true;
/* no per-chan start/stop */
// rc = uhdwrap_stop(global_dev, chan_nr);
msg = ipc_msgb_alloc(IPC_IF_MSG_STOP_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
ipc_prim->u.stop_cnf.return_code = rc ? 0 : -1;
return ipc_chan_sock_send(msg, chan_nr);
}
int ipc_rx_chan_setgain_req(struct ipc_sk_chan_if_gain *req, uint8_t chan_nr)
{
struct msgb *msg;
struct ipc_sk_chan_if *ipc_prim;
double rv = req->gain;
// rv = uhdwrap_set_gain(global_dev, req->gain, chan_nr, req->is_tx);
msg = ipc_msgb_alloc(IPC_IF_MSG_SETGAIN_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
ipc_prim->u.set_gain_cnf.is_tx = req->is_tx;
ipc_prim->u.set_gain_cnf.gain = rv;
return ipc_chan_sock_send(msg, chan_nr);
}
int ipc_rx_chan_setfreq_req(struct ipc_sk_chan_if_freq_req *req, uint8_t chan_nr)
{
struct msgb *msg;
struct ipc_sk_chan_if *ipc_prim;
bool rv = true;
// rv = uhdwrap_set_freq(global_dev, req->freq, chan_nr, req->is_tx);
msg = ipc_msgb_alloc(IPC_IF_MSG_SETFREQ_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
ipc_prim->u.set_freq_cnf.return_code = rv ? 0 : 1;
return ipc_chan_sock_send(msg, chan_nr);
}
int ipc_rx_chan_settxatten_req(struct ipc_sk_chan_if_tx_attenuation *req, uint8_t chan_nr)
{
struct msgb *msg;
struct ipc_sk_chan_if *ipc_prim;
double rv = req->attenuation;
// rv = uhdwrap_set_txatt(global_dev, req->attenuation, chan_nr);
msg = ipc_msgb_alloc(IPC_IF_MSG_SETTXATTN_CNF);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
ipc_prim->u.txatten_cnf.attenuation = rv;
return ipc_chan_sock_send(msg, chan_nr);
}
int ipc_sock_init(const char *path, struct ipc_sock_state **global_state_var,
int (*sock_callback_fn)(struct osmo_fd *fd, unsigned int what), int n)
{
struct ipc_sock_state *state;
struct osmo_fd *bfd;
int rc;
state = talloc_zero(NULL, struct ipc_sock_state);
if (!state)
return -ENOMEM;
*global_state_var = state;
INIT_LLIST_HEAD(&state->upqueue);
state->conn_bfd.fd = -1;
bfd = &state->listen_bfd;
bfd->fd = osmo_sock_unix_init(SOCK_SEQPACKET, 0, path, OSMO_SOCK_F_BIND);
if (bfd->fd < 0) {
LOGP(DMAIN, LOGL_ERROR, "Could not create %s unix socket: %s\n", path, strerror(errno));
talloc_free(state);
return -1;
}
osmo_fd_setup(bfd, bfd->fd, OSMO_FD_READ, sock_callback_fn, state, n);
rc = osmo_fd_register(bfd);
if (rc < 0) {
LOGP(DMAIN, LOGL_ERROR, "Could not register listen fd: %d\n", rc);
close(bfd->fd);
talloc_free(state);
return rc;
}
LOGP(DMAIN, LOGL_INFO, "Started listening on IPC socket: %s\n", path);
return 0;
}
int main_ipc()
{
char *ipc_msock_path = "/tmp/ipc_sock1";
tall_ctx = talloc_named_const(NULL, 0, "trx-ipc-ms");
msgb_talloc_ctx_init(tall_ctx, 0);
osmo_init_logging2(tall_ctx, &log_infox);
log_enable_multithread();
LOGP(DMAIN, LOGL_INFO, "Starting %s\n", "bernd");
ipc_sock_init(ipc_msock_path, &global_ipc_sock_state, ipc_sock_accept, 0);
// while (!ipc_exit_requested)
// osmo_select_main(0);
// if (global_dev) {
// unsigned int i;
// for (i = 0; i < decoded_region->num_chans; i++)
// uhdwrap_stop(global_dev, i);
// }
// ipc_sock_close(global_ipc_sock_state);
int rv;
pthread_condattr_t t2;
rv = pthread_condattr_setpshared(&t2, PTHREAD_PROCESS_SHARED);
rv = pthread_cond_init(&wait_open_cond, &t2);
return 0;
}
int wait_for_shm_open()
{
struct timespec tv;
int rv;
clock_gettime(CLOCK_REALTIME, &tv);
tv.tv_sec += 15;
rv = pthread_mutex_timedlock(&wait_open_lock, &tv);
if (rv != 0)
return -rv;
rv = pthread_cond_timedwait(&wait_open_cond, &wait_open_lock, &tv);
return rv;
}

View File

@@ -27,33 +27,66 @@
#include <sys/eventfd.h>
#include <unistd.h>
#include <stdatomic.h>
#include <stdbool.h>
#include <stdlib.h>
/*
classic lamport circular lockfree spsc queue:
every "side" only writes its own ptr, but may read the other sides ptr
notify reader using eventfd as soon as element is added, reader then reads until
read fails
-> reader pops in a loop until FALSE and might get spurious events because it
read before it was notified, which is fine
-> writing pushes *the same data* in a loop until TRUE, blocks
shutting this down requires
1) to stop reading and pushing
2) ONE side to take care of the eventfds
*/
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 l;
std::mutex lr, lw;
std::atomic_int r_flag, w_flag;
const int timeout_ms = 200;
public:
explicit spsc_cond_detail()
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;
public:
explicit spsc_cond_detail() : r_flag(0), w_flag(0)
{
}
@@ -63,28 +96,37 @@ template <bool block_read, bool block_write> class spsc_cond_detail {
ssize_t spsc_check_r()
{
std::unique_lock<std::mutex> lk(l);
std::unique_lock<std::mutex> lk(lr);
while (r_flag == 0)
cond_r.wait(lk);
r_flag--;
return 1;
}
ssize_t spsc_check_w()
{
std::unique_lock<std::mutex> lk(l);
std::unique_lock<std::mutex> lk(lw);
while (w_flag == 0)
cond_w.wait(lk);
w_flag--;
return 1;
}
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();
}
};
// 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:
@@ -151,7 +193,7 @@ class spsc : public T<block_read, block_write> {
/*! Adds element to the queue by copying the data.
* \param[in] elem input buffer, must match the originally configured queue buffer size!.
* \returns true if queue was not full and element was successfully pushed */
bool spsc_push(ELEM *elem)
bool spsc_push(const ELEM *elem)
{
size_t cur_wp, cur_rp;
cur_wp = writeptr.load(std::memory_order_relaxed);
@@ -202,3 +244,6 @@ 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> {};
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

@@ -0,0 +1,275 @@
/*
* OsmocomBB <-> SDR connection bridge
* UNIX socket server for L1CTL
*
* (C) 2013 by Sylvain Munaut <tnt@246tNt.com>
* (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
* (C) 2022 by by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
*
* All Rights Reserved
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*
*/
#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/un.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
#include <osmocom/core/write_queue.h>
#include <osmocom/bb/trxcon/logging.h>
#include <osmocom/bb/trxcon/l1ctl_server.h>
#define LOGP_CLI(cli, cat, level, fmt, args...) LOGP(cat, level, "%s" fmt, (cli)->log_prefix, ##args)
static int l1ctl_client_read_cb(struct osmo_fd *ofd)
{
struct l1ctl_client *client = (struct l1ctl_client *)ofd->data;
struct msgb *msg;
uint16_t len;
int rc;
/* Attempt to read from socket */
rc = read(ofd->fd, &len, L1CTL_MSG_LEN_FIELD);
if (rc != L1CTL_MSG_LEN_FIELD) {
if (rc <= 0) {
LOGP_CLI(client, DL1D, LOGL_NOTICE, "L1CTL connection error: read() failed (rc=%d): %s\n", rc,
strerror(errno));
} else {
LOGP_CLI(client, DL1D, LOGL_NOTICE, "L1CTL connection error: short read\n");
rc = -EIO;
}
l1ctl_client_conn_close(client);
return rc;
}
/* Check message length */
len = ntohs(len);
if (len > L1CTL_LENGTH) {
LOGP_CLI(client, DL1D, LOGL_ERROR, "Length is too big: %u\n", len);
return -EINVAL;
}
/* Allocate a new msg */
msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM, L1CTL_HEADROOM, "l1ctl_rx_msg");
if (!msg) {
LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to allocate msg\n");
return -ENOMEM;
}
msg->l1h = msgb_put(msg, len);
rc = read(ofd->fd, msg->l1h, msgb_l1len(msg));
if (rc != len) {
LOGP_CLI(client, DL1D, LOGL_ERROR, "Can not read data: len=%d < rc=%d: %s\n", len, rc, strerror(errno));
msgb_free(msg);
return rc;
}
/* Debug print */
LOGP_CLI(client, DL1D, LOGL_DEBUG, "RX: '%s'\n", osmo_hexdump(msg->data, msg->len));
/* Call L1CTL handler */
client->server->cfg->conn_read_cb(client, msg);
return 0;
}
static int l1ctl_client_write_cb(struct osmo_fd *ofd, struct msgb *msg)
{
struct l1ctl_client *client = (struct l1ctl_client *)ofd->data;
int len;
if (ofd->fd <= 0)
return -EINVAL;
len = write(ofd->fd, msg->data, msg->len);
if (len != msg->len) {
LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to write data: written (%d) < msg_len (%d)\n", len,
msg->len);
return -1;
}
return 0;
}
/* Connection handler */
static int l1ctl_server_conn_cb(struct osmo_fd *sfd, unsigned int flags)
{
struct l1ctl_server *server = (struct l1ctl_server *)sfd->data;
struct l1ctl_client *client;
int rc, client_fd;
client_fd = accept(sfd->fd, NULL, NULL);
if (client_fd < 0) {
LOGP(DL1C, LOGL_ERROR,
"Failed to accept() a new connection: "
"%s\n",
strerror(errno));
return client_fd;
}
if (server->cfg->num_clients_max > 0 /* 0 means unlimited */ &&
server->num_clients >= server->cfg->num_clients_max) {
LOGP(DL1C, LOGL_NOTICE,
"L1CTL server cannot accept more "
"than %u connection(s)\n",
server->cfg->num_clients_max);
close(client_fd);
return -ENOMEM;
}
client = talloc_zero(server, struct l1ctl_client);
if (client == NULL) {
LOGP(DL1C, LOGL_ERROR, "Failed to allocate an L1CTL client\n");
close(client_fd);
return -ENOMEM;
}
/* Init the client's write queue */
osmo_wqueue_init(&client->wq, 100);
INIT_LLIST_HEAD(&client->wq.bfd.list);
client->wq.write_cb = &l1ctl_client_write_cb;
client->wq.read_cb = &l1ctl_client_read_cb;
osmo_fd_setup(&client->wq.bfd, client_fd, OSMO_FD_READ, &osmo_wqueue_bfd_cb, client, 0);
/* Register the client's write queue */
rc = osmo_fd_register(&client->wq.bfd);
if (rc != 0) {
LOGP(DL1C, LOGL_ERROR, "Failed to register a new connection fd\n");
close(client->wq.bfd.fd);
talloc_free(client);
return rc;
}
llist_add_tail(&client->list, &server->clients);
client->id = server->next_client_id++;
client->server = server;
server->num_clients++;
LOGP(DL1C, LOGL_NOTICE, "L1CTL server got a new connection (id=%u)\n", client->id);
if (client->server->cfg->conn_accept_cb != NULL)
client->server->cfg->conn_accept_cb(client);
return 0;
}
int l1ctl_client_send(struct l1ctl_client *client, struct msgb *msg)
{
uint8_t *len;
/* Debug print */
LOGP_CLI(client, DL1D, LOGL_DEBUG, "TX: '%s'\n", osmo_hexdump(msg->data, msg->len));
if (msg->l1h != msg->data)
LOGP_CLI(client, DL1D, LOGL_INFO, "Message L1 header != Message Data\n");
/* Prepend 16-bit length before sending */
len = msgb_push(msg, L1CTL_MSG_LEN_FIELD);
osmo_store16be(msg->len - L1CTL_MSG_LEN_FIELD, len);
if (osmo_wqueue_enqueue(&client->wq, msg) != 0) {
LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to enqueue msg!\n");
msgb_free(msg);
return -EIO;
}
return 0;
}
void l1ctl_client_conn_close(struct l1ctl_client *client)
{
struct l1ctl_server *server = client->server;
LOGP_CLI(client, DL1C, LOGL_NOTICE, "Closing L1CTL connection\n");
if (server->cfg->conn_close_cb != NULL)
server->cfg->conn_close_cb(client);
/* Close connection socket */
osmo_fd_unregister(&client->wq.bfd);
close(client->wq.bfd.fd);
client->wq.bfd.fd = -1;
/* Clear pending messages */
osmo_wqueue_clear(&client->wq);
client->server->num_clients--;
llist_del(&client->list);
talloc_free(client);
/* If this was the last client, reset the client IDs generator to 0.
* This way avoid assigning huge unreadable client IDs like 26545. */
if (llist_empty(&server->clients))
server->next_client_id = 0;
}
struct l1ctl_server *l1ctl_server_alloc(void *ctx, const struct l1ctl_server_cfg *cfg)
{
struct l1ctl_server *server;
int rc;
LOGP(DL1C, LOGL_NOTICE, "Init L1CTL server (sock_path=%s)\n", cfg->sock_path);
server = talloc(ctx, struct l1ctl_server);
OSMO_ASSERT(server != NULL);
*server = (struct l1ctl_server){
.clients = LLIST_HEAD_INIT(server->clients),
.cfg = cfg,
};
/* conn_read_cb shall not be NULL */
OSMO_ASSERT(cfg->conn_read_cb != NULL);
/* Bind connection handler */
osmo_fd_setup(&server->ofd, -1, OSMO_FD_READ, &l1ctl_server_conn_cb, server, 0);
rc = osmo_sock_unix_init_ofd(&server->ofd, SOCK_STREAM, 0, cfg->sock_path, OSMO_SOCK_F_BIND);
if (rc < 0) {
LOGP(DL1C, LOGL_ERROR, "Could not create UNIX socket: %s\n", strerror(errno));
talloc_free(server);
return NULL;
}
return server;
}
void l1ctl_server_free(struct l1ctl_server *server)
{
LOGP(DL1C, LOGL_NOTICE, "Shutdown L1CTL server\n");
/* Close all client connections */
while (!llist_empty(&server->clients)) {
struct l1ctl_client *client = llist_entry(server->clients.next, struct l1ctl_client, list);
l1ctl_client_conn_close(client);
}
/* Unbind listening socket */
if (server->ofd.fd != -1) {
osmo_fd_unregister(&server->ofd);
close(server->ofd.fd);
server->ofd.fd = -1;
}
talloc_free(server);
}

View File

@@ -0,0 +1,71 @@
/*
* (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/>.
*
*/
extern "C" {
#include <osmocom/bb/trxcon/trxcon.h>
#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;
static int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg)
{
struct trxcon_inst *trxcon = (struct trxcon_inst *)l1c->priv;
return trxcon_l1ctl_receive(trxcon, msg);
}
static void l1ctl_conn_accept_cb(struct l1ctl_client *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)
{
struct trxcon_inst *trxcon = (struct trxcon_inst *)l1c->priv;
if (trxcon == NULL || trxcon->fi == NULL)
return;
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_L2IF_FAILURE, NULL);
}
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,
.conn_close_cb = &l1ctl_conn_close_cb,
};
server = l1ctl_server_alloc(tallctx, &server_cfg);
if (server == NULL) {
return false;
}
return true;
}

View File

@@ -15,19 +15,16 @@
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
*/
#include <osmocom/core/application.h>
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include "logging.h"
#include <osmocom/bb/trxcon/trxcon.h>
#include <osmocom/bb/trxcon/logging.h>
static struct log_info_cat trx_log_info_cat[] = {
static struct log_info_cat trxcon_log_info_cat[] = {
[DAPP] = {
.name = "DAPP",
.description = "Application",
@@ -46,8 +43,8 @@ static struct log_info_cat trx_log_info_cat[] = {
.color = "\033[1;31m",
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DTRX] = {
.name = "DTRX",
[DTRXC] = {
.name = "DTRXC",
.description = "Transceiver control interface",
.color = "\033[1;33m",
.enabled = 1, .loglevel = LOGL_NOTICE,
@@ -62,7 +59,7 @@ static struct log_info_cat trx_log_info_cat[] = {
.name = "DSCH",
.description = "Scheduler management",
.color = "\033[1;36m",
.enabled = 0, .loglevel = LOGL_NOTICE,
.enabled = 1, .loglevel = LOGL_NOTICE,
},
[DSCHD] = {
.name = "DSCHD",
@@ -70,19 +67,32 @@ static struct log_info_cat trx_log_info_cat[] = {
.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 const struct log_info trx_log_info = {
.cat = trx_log_info_cat,
.num_cat = ARRAY_SIZE(trx_log_info_cat),
static const struct log_info trxcon_log_info = {
.cat = trxcon_log_info_cat,
.num_cat = ARRAY_SIZE(trxcon_log_info_cat),
};
int trx_log_init(void *tall_ctx, const char *category_mask)
static const int trxcon_log_cfg[] = {
[TRXCON_LOGC_FSM] = DAPP,
[TRXCON_LOGC_L1C] = DL1C,
[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(tall_ctx, &trx_log_info);
osmo_init_logging2(tallctx, &trxcon_log_info);
log_target_file_switch_to_wqueue(osmo_stderr_target);
if (category_mask)
log_parse_category_mask(osmo_stderr_target, category_mask);
return 0;
trxcon_set_log_cfg(&trxcon_log_cfg[0], ARRAY_SIZE(trxcon_log_cfg));
}

160
Transceiver52M/ms/ms.cpp Normal file
View File

@@ -0,0 +1,160 @@
/*
* (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 "GSMCommon.h"
#include <atomic>
#include <cassert>
#include <complex>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <thread>
#include <fstream>
#include "ms.h"
extern "C" {
#include "sch.h"
}
#include "threadsched.h"
dummylog ms_trx::dummy_log;
#ifdef DBGXX
const int offsetrange = 200;
const int offset_start = -15;
static int offset_ctr = 0;
#endif
template <>
std::atomic<bool> ms_trx::base::stop_lower_threads_flag(false);
int ms_trx::init_dev_and_streams()
{
int status = 0;
status = init_device(rx_bh(), tx_bh());
if (status < 0) {
std::cerr << "failed to init dev!" << std::endl;
return -1;
}
return status;
}
bh_fn_t ms_trx::rx_bh()
{
return [this](dev_buf_t *rcd) -> int {
if (this->search_for_sch(rcd) == SCH_STATE::FOUND)
this->grab_bursts(rcd);
return 0;
};
}
bh_fn_t ms_trx::tx_bh()
{
return [this](dev_buf_t *rcd) -> int {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
auto y = this;
#pragma GCC diagnostic pop
/* nothing to do here */
return 0;
};
}
void ms_trx::start_lower_ms()
{
if (stop_lower_threads_flag)
return;
auto fn = get_rx_burst_handler_fn(rx_bh());
lower_rx_task = spawn_worker_thread(sched_params::thread_names::RXRUN, fn, this);
usleep(1000);
auto fn2 = get_tx_burst_handler_fn(tx_bh());
lower_tx_task = spawn_worker_thread(sched_params::thread_names::TXRUN, fn2, this);
actually_enable_streams();
}
void ms_trx::set_upper_ready(bool is_ready)
{
upper_is_ready = is_ready;
}
void ms_trx::stop_threads()
{
std::cerr << "killing threads..." << std::endl;
stop_lower_threads_flag = true;
close_device();
std::cerr << "dev closed..." << std::endl;
pthread_join(lower_rx_task, nullptr);
std::cerr << "L rx dead..." << std::endl;
pthread_join(lower_tx_task, nullptr);
std::cerr << "L tx dead..." << std::endl;
}
void ms_trx::submit_burst(blade_sample_type *buffer, int len, GSM::Time target)
{
int64_t now_ts;
GSM::Time now_time;
target.incTN(3); // ul dl offset
int target_fn = target.FN();
int target_tn = target.TN();
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
tosend.decTN(-diff_tn);
// in theory fn equal and tn+3 equal is also a problem...
if (diff_fn < 0 || (diff_fn == 0 && (target_tn-now_time.TN() < 3))) {
std::cerr << "## TX too late?! fn DIFF:" << diff_fn << " tn LOCAL: " << now_time.TN()
<< " tn OTHER: " << target_tn << std::endl;
return;
}
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 0
auto check = now_time + tosend;
unsigned int pad = 4 * 4;
blade_sample_type buf2[len + pad];
std::fill(buf2, buf2 + pad, 0);
memcpy(&buf2[pad], buffer, len * sizeof(blade_sample_type));
assert(target.FN() == check.FN());
assert(target.TN() == check.TN());
submit_burst_ts(buf2, len + pad, send_ts - pad);
#else
submit_burst_ts(buffer, len, send_ts);
#endif
}

View File

@@ -1,5 +1,4 @@
#pragma once
/*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
@@ -27,7 +26,7 @@
#include <cstdint>
#include <mutex>
#include <iostream>
#include <thread>
// #include <thread>
#if defined(BUILDBLADE)
#include "bladerf_specific.h"
@@ -35,37 +34,92 @@
#elif defined(BUILDUHD)
#include "uhd_specific.h"
#define BASET uhd_hw<ms_trx>
#elif defined(BUILDIPC)
#include "ipc_specific.h"
#define BASET ipc_hw<ms_trx>
#else
#error wat? no device..
#endif
#include "Complex.h"
#include "GSMCommon.h"
#include "itrq.h"
#include "threadpool.h"
#include "threadsched.h"
const unsigned int ONE_TS_BURST_LEN = (3 + 58 + 26 + 58 + 3 + 8.25) * 4 /*sps*/;
const unsigned int NUM_RXQ_FRAMES = 1; // rx thread <-> upper rx queue
const unsigned int SCH_LEN_SPS = (ONE_TS_BURST_LEN * 8 /*ts*/ * 12 /*frames*/);
template <typename T> 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,11 +129,11 @@ struct one_burst {
GSM::Time gsmts;
union {
blade_sample_type burst[ONE_TS_BURST_LEN];
unsigned char sch_bits[148];
char sch_bits[148];
};
};
using rx_queue_t = spsc_cond<8 * NUM_RXQ_FRAMES, one_burst, true, true>;
using rx_queue_t = spsc_cond_timeout<4, one_burst, true, false>;
enum class SCH_STATE { SEARCHING, FOUND };
@@ -165,7 +219,21 @@ class time_keeper {
using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>;
struct ms_trx : public BASET {
// used to globally initialize the sched/hw information
struct sched_hw_info {
int hw_cpus;
sched_params::target hw_target;
sched_hw_info()
{
hw_cpus = std::thread::hardware_concurrency();
hw_target = hw_cpus > 4 ? sched_params::target::ODROID : sched_params::target::PI4;
set_sched_target(hw_target);
std::cerr << "scheduling for: " << (hw_cpus > 4 ? "odroid" : "pi4") << std::endl;
}
};
struct ms_trx : public BASET, public sched_hw_info {
using base = BASET;
static dummylog dummy_log;
unsigned int mTSC;
@@ -173,15 +241,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;
pthread_t lower_rx_task;
pthread_t 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;
@@ -193,28 +258,29 @@ struct ms_trx : public BASET {
int64_t first_sch_ts_start = -1;
time_keeper timekeeper;
single_thread_pool worker_thread; // uses base class sched target hw info
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_streams(void *rx_cb, void *tx_cb);
int init_dev_and_streams(void *rx_cb, void *tx_cb);
int init_dev_and_streams();
void stop_threads();
void *rx_cb(ms_trx *t);
void *tx_cb();
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 }, upper_is_ready(false)
{
}

View File

@@ -1,227 +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 <radioInterface.h>
#include "ms_rx_upper.h"
#include "syncthing.h"
void upper_trx::driveControl()
{
#ifdef IPCIF
auto m = pop_c();
if (!m)
return;
#else
TRX_C cmd;
socklen_t addr_len = sizeof(ctrlsrc);
int rdln = recvfrom(mCtrlSockets, (void *)cmd.cmd, sizeof(cmd) - 1, 0, &ctrlsrc, &addr_len);
if (rdln < 0 && errno == EAGAIN) {
std::cerr << "fuck, send ctrl?" << std::endl;
exit(0);
}
TRX_C *m = &cmd;
#endif
auto response = (TRX_C *)malloc(sizeof(TRX_C));
response->cmd[0] = '\0';
commandhandler(m->cmd, response->cmd);
#ifdef IPCIF
free(m);
#endif
std::clog << "response is " << response->cmd << std::endl;
#ifdef IPCIF
push_c(response);
#else
int rv = sendto(mCtrlSockets, response, strlen(response->cmd) + 1, 0, &ctrlsrc, sizeof(struct sockaddr_in));
if (rv < 0) {
std::cerr << "fuck, rcv ctrl?" << std::endl;
exit(0);
}
free(response);
#endif
}
void upper_trx::commandhandler(char *buffer, char *response)
{
int MAX_PACKET_LENGTH = TRXC_BUF_SIZE;
char cmdcheck[4];
char command[MAX_PACKET_LENGTH];
sscanf(buffer, "%3s %s", cmdcheck, command);
if (strcmp(cmdcheck, "CMD") != 0) {
LOG(WARNING) << "bogus message on control interface";
return;
}
std::clog << "command is " << buffer << std::endl << std::flush;
if (strcmp(command, "MEASURE") == 0) {
msleep(100);
int freq;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &freq);
sprintf(response, "RSP MEASURE 0 %d -80", freq);
} else if (strcmp(command, "ECHO") == 0) {
msleep(100);
sprintf(response, "RSP ECHO 0");
} else if (strcmp(command, "POWEROFF") == 0) {
set_ta(0);
// turn off transmitter/demod
// set_upper_ready(false);
sprintf(response, "RSP POWEROFF 0");
} else if (strcmp(command, "POWERON") == 0) {
// turn on transmitter/demod
if (!mTxFreq || !mRxFreq)
sprintf(response, "RSP POWERON 1");
else {
sprintf(response, "RSP POWERON 0");
if (!mOn) {
// Prepare for thread start
mPower = -20;
// start_ms();
set_upper_ready(true);
writeClockInterface();
mOn = true;
}
}
} else if (strcmp(command, "SETMAXDLY") == 0) {
//set expected maximum time-of-arrival
int maxDelay;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &maxDelay);
mMaxExpectedDelay = maxDelay; // 1 GSM symbol is approx. 1 km
sprintf(response, "RSP SETMAXDLY 0 %d", maxDelay);
} else if (strcmp(command, "SETRXGAIN") == 0) {
//set expected maximum time-of-arrival
int newGain;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &newGain);
newGain = setRxGain(newGain);
sprintf(response, "RSP SETRXGAIN 0 %d", newGain);
} else if (strcmp(command, "NOISELEV") == 0) {
if (mOn) {
float lev = 0; //mStates[chan].mNoiseLev;
sprintf(response, "RSP NOISELEV 0 %d", (int)round(20.0 * log10(rxFullScale / lev)));
} else {
sprintf(response, "RSP NOISELEV 1 0");
}
} else if (!strcmp(command, "SETPOWER")) {
// set output power in dB
int dbPwr;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbPwr);
if (!mOn)
sprintf(response, "RSP SETPOWER 1 %d", dbPwr);
else {
mPower = dbPwr;
setPowerAttenuation(mPower);
sprintf(response, "RSP SETPOWER 0 %d", dbPwr);
}
} else if (!strcmp(command, "ADJPOWER")) {
// adjust power in dB steps
int dbStep;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbStep);
if (!mOn)
sprintf(response, "RSP ADJPOWER 1 %d", mPower);
else {
mPower += dbStep;
setPowerAttenuation(mPower);
sprintf(response, "RSP ADJPOWER 0 %d", mPower);
}
} else if (strcmp(command, "RXTUNE") == 0) {
// tune receiver
int freqKhz;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &freqKhz);
mRxFreq = freqKhz * 1e3;
if (!tuneRx(mRxFreq)) {
LOG(ALERT) << "RX failed to tune";
sprintf(response, "RSP RXTUNE 1 %d", freqKhz);
} else
sprintf(response, "RSP RXTUNE 0 %d", freqKhz);
} else if (strcmp(command, "TXTUNE") == 0) {
// tune txmtr
int freqKhz;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &freqKhz);
mTxFreq = freqKhz * 1e3;
if (!tuneTx(mTxFreq)) {
LOG(ALERT) << "TX failed to tune";
sprintf(response, "RSP TXTUNE 1 %d", freqKhz);
} else
sprintf(response, "RSP TXTUNE 0 %d", freqKhz);
} else if (!strcmp(command, "SETTSC")) {
// set TSC
unsigned TSC;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC);
if (mOn)
sprintf(response, "RSP SETTSC 1 %d", TSC);
// else if (chan && (TSC != mTSC))
// sprintf(response, "RSP SETTSC 1 %d", TSC);
else {
mTSC = TSC;
//generateMidamble(rx_sps, TSC);
sprintf(response, "RSP SETTSC 0 %d", TSC);
}
} else if (!strcmp(command, "GETBSIC")) {
if (mBSIC < 0)
sprintf(response, "RSP GETBSIC 1");
else
sprintf(response, "RSP GETBSIC 0 %d", mBSIC);
} else if (strcmp(command, "SETSLOT") == 0) {
// set TSC
int corrCode;
int timeslot;
sscanf(buffer, "%3s %s %d %d", cmdcheck, command, &timeslot, &corrCode);
if ((timeslot < 0) || (timeslot > 7)) {
LOG(WARNING) << "bogus message on control interface";
sprintf(response, "RSP SETSLOT 1 %d %d", timeslot, corrCode);
return;
}
sprintf(response, "RSP SETSLOT 0 %d %d", timeslot, corrCode);
} else if (!strcmp(command, "SETRXMASK")) {
int slot;
unsigned long long mask;
sscanf(buffer, "%3s %s %d 0x%llx", cmdcheck, command, &slot, &mask);
if ((slot < 0) || (slot > 7)) {
sprintf(response, "RSP SETRXMASK 1");
} else {
mRxSlotMask[slot] = mask;
sprintf(response, "RSP SETRXMASK 0 %d 0x%llx", slot, mask);
}
} else if (!strcmp(command, "SYNC")) {
// msleep(10);
sprintf(response, "RSP SYNC 0");
mMaxExpectedDelay = 48;
// setRxGain(30);
// msleep(10);
} else if (!strcmp(command, "SETTA")) {
int ta;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &ta);
set_ta(ta);
sprintf(response, "RSP SETTA 0 %d", ta);
} else {
LOG(WARNING) << "bogus command " << command << " on control interface.";
}
//mCtrlSockets[chan]->write(response, strlen(response) + 1);
}

View File

@@ -1,211 +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 "syncthing.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
__attribute__((xray_always_instrument)) __attribute__((noinline)) 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() != 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();
}
__attribute__((xray_always_instrument)) __attribute__((noinline)) static void
handle_it(one_burst &e, signalVector &burst, unsigned int tsc, int scale)
{
memset(burst.begin(), 0, burst.size() * sizeof(std::complex<float>));
auto is_sch = gsm_sch_check_fn(e.gsmts.FN()) && e.gsmts.TN() == 0;
auto is_fcch = gsm_fcch_check_fn(e.gsmts.FN()) && e.gsmts.TN() == 0;
// if (is_sch)
// return;
if (is_fcch)
return;
if (is_sch) {
unsigned 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;
unsigned 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);
;
auto bits = SoftVector(148);
for (int i = 0; i < 148; i++)
(bits)[i] = outbin[i] < 1 ? -1 : 1;
#endif
}
__attribute__((xray_always_instrument)) __attribute__((noinline)) 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);
// 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();
}
}

View File

@@ -27,9 +27,11 @@
#include <iostream>
#include <future>
#include "syncthing.h"
#include "ms.h"
#include "grgsm_vitac/grgsm_vitac.h"
#include "threadpool.h"
extern "C" {
#include "sch.h"
}
@@ -38,29 +40,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
__attribute__((xray_always_instrument)) __attribute__((noinline)) 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], 62, 39);
float_to_sbit(&bits[106], &data[39], 62, 39);
memcpy(&data[0], &bits[3], 39);
memcpy(&data[39], &bits[106], 39);
if (!gsm_sch_decode(info, data)) {
gsm_sch_parse(info, &sch);
@@ -88,20 +90,7 @@ __attribute__((xray_always_instrument)) __attribute__((noinline)) bool ms_trx::d
// 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
// auto sch11 = gsm_sch_check_fn(fn + 11);
// DBGLG() << "next sch: "<< (sch11 ? "11":"10")<<" first ts " << first_sch_buf_rcv_ts << std::endl;
return true;
}
return false;
@@ -115,11 +104,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) {
@@ -128,22 +113,23 @@ 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;
}
static unsigned char sch_demod_bits[148];
static char sch_demod_bits[148];
bool ms_trx::handle_sch_or_nb()
{
one_burst brst;
auto current_gsm_time = timekeeper.gsmtime();
auto is_sch = gsm_sch_check_fn(current_gsm_time.FN()) && current_gsm_time.TN() == 0;
auto is_fcch = gsm_fcch_check_fn(current_gsm_time.FN()) && current_gsm_time.TN() == 0;
#pragma unused(is_fcch)
const auto current_gsm_time = timekeeper.gsmtime();
const auto is_sch = gsm_sch_check_ts(current_gsm_time.TN(), current_gsm_time.FN());
//either pass burst to upper layer for demod, OR pass demodded SCH to upper layer so we don't waste time processing it twice
brst.gsmts = current_gsm_time;
@@ -154,20 +140,10 @@ bool ms_trx::handle_sch_or_nb()
handle_sch(false);
memcpy(brst.sch_bits, sch_demod_bits, sizeof(sch_demod_bits));
}
// auto pushok = rxqueue.spsc_push(&brst);
#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
// #ifdef PRINT_Q_OVERFLOW
// if (!pushok)
// std::cout << "F" << std::endl;
// #endif
while (upper_is_ready && !rxqueue.spsc_push(&brst))
;
if (do_auto_gain)
maybe_update_gain(brst);
@@ -181,36 +157,40 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
auto current_gsm_time = timekeeper.gsmtime();
const auto buf_len = is_first_sch_acq ? SCH_LEN_SPS : ONE_TS_BURST_LEN;
const auto which_in_buffer = is_first_sch_acq ? first_sch_buf : burst_copy_buffer;
memset((void *)&sch_acq_buffer[0], 0, sizeof(sch_acq_buffer));
#if 1
const auto which_out_buffer = is_first_sch_acq ? sch_acq_buffer : &sch_acq_buffer[40 * 2];
const auto ss = reinterpret_cast<std::complex<float> *>(which_out_buffer);
std::complex<float> channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
float max_corr = 0;
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) {
convert_and_scale<float, int16_t>(which_out_buffer, which_in_buffer, buf_len * 2,
1.f / float(rxFullScale));
float max_corr = 0;
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]) < 1 ? -1 : 1;
}
auto sch_decode_success = decode_sch(sch_demod_bits, is_first_sch_acq);
#if 0
auto burst = new signalVector(buf_len, 50);
const auto corr_type = is_first_sch_acq ? sch_detect_type::SCH_DETECT_BUFFER : sch_detect_type::SCH_DETECT_FULL;
struct estim_burst_params ebp;
auto sch_decode_success = decode_sch(bitss.begin(), is_first_sch_acq);
// scale like uhd, +-2k -> +-32k
convert_and_scale(burst->begin(), which_in_buffer, buf_len * 2, SAMPLE_SCALE_FACTOR);
auto rv = detectSCHBurst(*burst, 4, 4, corr_type, &ebp);
int howmuchdelay = ebp.toa * 4;
std::cerr << "ooffs: " << howmuchdelay << " " << std::endl;
std::cerr << "voffs: " << start << " " << sch_decode_success << std::endl;
#endif
if (sch_decode_success) {
const auto ts_offset_symb = 0;
const auto ts_offset_symb = 4;
if (is_first_sch_acq) {
// update ts to first sample in sch buffer, to allow delay calc for current ts
first_sch_ts_start = first_sch_buf_rcv_ts + start - (ts_offset_symb * 4) - 1;
@@ -225,35 +205,132 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << start << " " << current_gsm_time.FN()
<< ":" << current_gsm_time.TN() << std::endl;
}
#else
const auto ts_offset_symb = 4;
auto burst = new signalVector(buf_len, 50);
const auto corr_type = is_first_sch_acq ? sch_detect_type::SCH_DETECT_BUFFER : sch_detect_type::SCH_DETECT_FULL;
struct estim_burst_params ebp;
// scale like uhd, +-2k -> +-32k
convert_and_scale(burst->begin(), which_in_buffer, buf_len * 2, SAMPLE_SCALE_FACTOR);
auto rv = detectSCHBurst(*burst, 4, 4, corr_type, &ebp);
int howmuchdelay = ebp.toa * 4;
if (!rv) {
delete burst;
DBGLG() << "SCH : \x1B[31m detect fail \033[0m NOOOOOOOOOOOOOOOOOO toa:" << ebp.toa << " "
<< current_gsm_time.FN() << ":" << current_gsm_time.TN() << std::endl;
return false;
}
__attribute__((xray_never_instrument)) SCH_STATE ms_trx::search_for_sch(dev_buf_t *rcd)
SoftVector *bits;
if (is_first_sch_acq) {
// can't be legit with a buf size spanning _at least_ one SCH but delay that implies partial sch burst
if (howmuchdelay < 0 || (buf_len - howmuchdelay) < ONE_TS_BURST_LEN) {
delete burst;
return false;
}
struct estim_burst_params ebp2;
// auto sch_chunk = new signalVector(ONE_TS_BURST_LEN, 50);
// auto sch_chunk_start = sch_chunk->begin();
// memcpy(sch_chunk_start, sch_buf_f.data() + howmuchdelay, sizeof(std::complex<float>) * ONE_TS_BURST_LEN);
auto delay = delayVector(burst, NULL, -howmuchdelay);
scaleVector(*delay, (complex)1.0 / ebp.amp);
auto rv2 = detectSCHBurst(*delay, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp2);
DBGLG() << "FIRST SCH : " << (rv2 ? "yes " : " ") << "Timing offset " << ebp2.toa << " symbols"
<< std::endl;
bits = demodAnyBurst(*delay, SCH, 4, &ebp2);
delete delay;
} else {
bits = demodAnyBurst(*burst, SCH, 4, &ebp);
}
delete burst;
// clamp to +-1.5 because +-127 softbits scaled by 64 after -0.5 can be at most +-1.5
clamp_array(bits->begin(), 148, 1.5f);
float_to_sbit(&bits->begin()[0], (signed char *)&sch_demod_bits[0], 62, 148);
// float_to_sbit(&bits->begin()[106], &data[39], 62, 39);
if (decode_sch((char *)sch_demod_bits, is_first_sch_acq)) {
auto current_gsm_time_updated = timekeeper.gsmtime();
if (is_first_sch_acq) {
// update ts to first sample in sch buffer, to allow delay calc for current ts
first_sch_ts_start = first_sch_buf_rcv_ts + howmuchdelay - (ts_offset_symb * 4);
} else {
// continuous sch tracking, only update if off too much
auto diff = [](float x, float y) { return x > y ? x - y : y - x; };
auto d = diff(ebp.toa, ts_offset_symb);
if (abs(d) > 0.3) {
if (ebp.toa < ts_offset_symb)
ebp.toa = d;
else
ebp.toa = -d;
temp_ts_corr_offset += ebp.toa * 4;
DBGLG() << "offs: " << ebp.toa << " " << temp_ts_corr_offset << std::endl;
}
}
auto a = gsm_sch_check_fn(current_gsm_time_updated.FN() - 1);
auto b = gsm_sch_check_fn(current_gsm_time_updated.FN());
auto c = gsm_sch_check_fn(current_gsm_time_updated.FN() + 1);
DBGLG() << "L SCH : Timing offset " << rv << " " << ebp.toa << " " << a << b << c << "fn "
<< current_gsm_time_updated.FN() << ":" << current_gsm_time_updated.TN() << std::endl;
delete bits;
return true;
} else {
DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << ebp.toa << " " << current_gsm_time.FN()
<< ":" << current_gsm_time.TN() << std::endl;
}
delete bits;
#endif
return false;
}
/*
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 (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 ?
@@ -266,19 +343,13 @@ __attribute__((xray_never_instrument)) SCH_STATE ms_trx::search_for_sch(dev_buf_
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 > 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;
}
__attribute__((optnone)) void ms_trx::grab_bursts(dev_buf_t *rcd)
void ms_trx::grab_bursts(dev_buf_t *rcd)
{
// partial burst samples read from the last buffer
static int partial_rdofs = 0;
@@ -292,7 +363,7 @@ __attribute__((optnone)) void ms_trx::grab_bursts(dev_buf_t *rcd)
const auto fracts = next_burst_start % ONE_TS_BURST_LEN;
to_skip = ONE_TS_BURST_LEN - fracts;
for (int i = 0; i < fullts; i++)
for (unsigned int i = 0; i < fullts; i++)
timekeeper.inc_and_update(first_sch_ts_start + i * ONE_TS_BURST_LEN);
if (fracts)
@@ -311,9 +382,8 @@ __attribute__((optnone)) void ms_trx::grab_bursts(dev_buf_t *rcd)
if (partial_rdofs) {
auto first_remaining = ONE_TS_BURST_LEN - partial_rdofs;
// memcpy(burst_copy_buffer, partial_buf, partial_rdofs * sizeof(blade_sample_type));
auto rd = rcd->read_n(burst_copy_buffer + partial_rdofs, 0, first_remaining);
if (rd != first_remaining) {
if (rd != (int)first_remaining) {
partial_rdofs += rd;
return;
}

View File

@@ -1,301 +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 "sigProcLib.h"
#include "syncthing.h"
#include <signalVector.h>
#include <radioVector.h>
#include <radioInterface.h>
#include "grgsm_vitac/grgsm_vitac.h"
#include "ms_rx_upper.h"
extern "C" {
#include <osmocom/core/select.h>
#include "sch.h"
#include "convolve.h"
#include "convert.h"
#include "proto_trxd.h"
void __lsan_do_recoverable_leak_check();
}
namespace trxcon
{
extern "C" {
#include <trxcon/trx_if.h>
}
trx_instance *trxcon_instance; // local handle
static tx_queue_t txq;
} // namespace trxcon
#ifdef LOG
#undef LOG
#define LOG(...) upper_trx::dummy_log()
#endif
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 (1) {
driveControl();
pthread_testcancel();
}
});
msleep(1);
thr_tx = std::thread([this] {
set_name_aff_sched("upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1);
while (1) {
driveTx();
pthread_testcancel();
}
});
// atomic ensures data is not written to q until loop reads
start_ms();
set_name_aff_sched("upper_rx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_RR) - 5);
while (1) {
// set_upper_ready(true);
driveReceiveFIFO();
pthread_testcancel();
osmo_select_main(1);
}
// std::thread([this] {
// set_name_aff_sched("leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10);
// while (1) {
// std::this_thread::sleep_for(std::chrono::seconds{ 5 });
// __lsan_do_recoverable_leak_check();
// }
// }).detach();
}
void upper_trx::start_ms()
{
ms_trx::start();
}
SoftVector *upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset) __attribute__((optnone))
{
float pow, avg = 1.0;
static SoftVector bits(148);
static complex workbuf[40 + 625 + 40];
static signalVector sv(workbuf, 40, 625);
GSM::Time burst_time;
auto ss = reinterpret_cast<std::complex<float> *>(&workbuf[40]);
memset((void *)&workbuf[0], 0, sizeof(workbuf));
// assert(sv.begin() == &workbuf[40]);
one_burst e;
unsigned char outbin[148];
std::stringstream dbgout;
while (!rxqueue.spsc_pop(&e)) {
rxqueue.spsc_prep_pop();
}
burst_time = e.gsmts;
wTime = burst_time;
auto is_sch = (burst_time.TN() == 0 && gsm_sch_check_fn(burst_time.FN()));
auto is_fcch = (burst_time.TN() == 0 && gsm_fcch_check_fn(burst_time.FN()));
if (is_fcch) {
// return trash
// fprintf(stderr, "c %d\n",burst_time.FN());
return &bits;
}
if (is_sch) {
for (int i = 0; i < 148; i++)
(bits)[i] = (!e.sch_bits[i]) < 1 ? -1 : 1;
RSSI = 10;
timingOffset = 0;
// fprintf(stderr, "s %d\n", burst_time.FN());
return &bits;
}
auto ts = trxcon::trxcon_instance->ts_list[burst_time.TN()];
if (ts == NULL || ts->mf_layout == NULL)
return 0;
convert_and_scale<float, int16_t>(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
pow = energyDetect(sv, 20 * rx_sps);
if (pow < -1) {
LOG(ALERT) << "Received empty burst";
return NULL;
}
avg = sqrt(pow);
{
float ncmax, dcmax;
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
std::complex<float> chan_imp_resp2[CHAN_IMP_RESP_LENGTH * d_OSR];
auto normal_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp[0], &ncmax, mTSC);
auto dummy_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp2[0], &dcmax, TS_DUMMY);
auto is_nb = ncmax > dcmax;
// std::cerr << " U " << (is_nb ? "NB" : "DB") << "@ o nb: " << normal_burst_start
// << " o db: " << dummy_burst_start << std::endl;
normal_burst_start = normal_burst_start < 39 ? normal_burst_start : 39;
normal_burst_start = normal_burst_start > -39 ? normal_burst_start : -39;
// fprintf(stderr, "%s %d\n", (is_nb ? "N":"D"), burst_time.FN());
// 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);
for (int i = 0; i < 148; i++)
(bits)[i] = (outbin[i]) < 1 ? -1 : 1;
}
RSSI = (int)floor(20.0 * log10(rxFullScale / avg));
timingOffset = (int)round(0);
return &bits;
}
void upper_trx::driveReceiveFIFO()
{
int RSSI;
int TOA; // in 1/256 of a symbol
GSM::Time burstTime;
if (!mOn)
return;
SoftVector *rxBurst = pullRadioVector(burstTime, RSSI, TOA);
if (rxBurst) {
trxd_from_trx response;
response.ts = burstTime.TN();
response.fn = htonl(burstTime.FN());
response.rssi = RSSI;
response.toa = htons(TOA);
SoftVector::const_iterator burstItr = rxBurst->begin();
if (burstTime.TN() == 0 && gsm_sch_check_fn(burstTime.FN())) {
clamp_array(rxBurst->begin(), 148, 1.5f);
for (unsigned int i = 0; i < gSlotLen; i++) {
auto val = *burstItr++;
auto vval = isnan(val) ? 0 : val;
((int8_t *)response.symbols)[i] = round((vval - 0.5) * 64.0);
}
} else {
// invert and fix to +-127 sbits
for (int i = 0; i < 148; i++)
((int8_t *)response.symbols)[i] = *burstItr++ > 0.0f ? -127 : 127;
}
trxcon::trx_data_rx_handler(trxcon::trxcon_instance, (uint8_t *)&response);
}
}
void upper_trx::driveTx()
{
trxd_to_trx e;
while (!trxcon::txq.spsc_pop(&e)) {
trxcon::txq.spsc_prep_pop();
}
trxd_to_trx *burst = &e;
auto proper_fn = ntohl(burst->fn);
// std::cerr << "got burst!" << proper_fn << ":" << burst->ts
// << " current: " << timekeeper.gsmtime().FN()
// << " dff: " << (int64_t)((int64_t)timekeeper.gsmtime().FN() - (int64_t)proper_fn)
// << std::endl;
auto currTime = GSM::Time(proper_fn, burst->ts);
int RSSI = (int)burst->txlev;
static BitVector newBurst(gSlotLen);
BitVector::iterator itr = newBurst.begin();
auto *bufferItr = burst->symbols;
while (itr < newBurst.end())
*itr++ = *bufferItr++;
auto txburst = modulateBurst(newBurst, 8 + (currTime.TN() % 4 == 0), 4);
scaleVector(*txburst, txFullScale * 0.7 /* * pow(10, -RSSI / 10)*/);
// float -> int16
blade_sample_type burst_buf[txburst->size()];
convert_and_scale<int16_t, float>(burst_buf, txburst->begin(), txburst->size() * 2, 1);
// auto check = signalVector(txburst->size(), 40);
// convert_and_scale<float, int16_t, 1>(check.begin(), burst_buf, txburst->size() * 2);
// estim_burst_params ebp;
// auto d = detectAnyBurst(check, 2, 4, 4, CorrType::RACH, 40, &ebp);
// if(d)
// std::cerr << "RACH D! " << ebp.toa << std::endl;
// else
// std::cerr << "RACH NOOOOOOOOOO D! " << ebp.toa << std::endl;
// memory read --binary --outfile /tmp/mem.bin &burst_buf[0] --count 2500 --force
submit_burst(burst_buf, txburst->size(), currTime);
delete txburst;
}
int trxc_main(int argc, char *argv[])
{
pthread_setname_np(pthread_self(), "main_trxc");
convolve_init();
convert_init();
sigProcLibSetup();
initvita();
int status = 0;
auto trx = new upper_trx();
trx->do_auto_gain = true;
status = trx->init_dev_and_streams(0, 0);
trx->start_threads();
return status;
}
extern "C" {
void init_external_transceiver(struct trx_instance *trx, int argc, char **argv)
{
trxcon::trxcon_instance = (trxcon::trx_instance *)trx;
std::cout << "init?" << std::endl;
trxc_main(argc, argv);
}
void close_external_transceiver(int argc, char **argv)
{
std::cout << "Shutting down transceiver..." << std::endl;
}
void tx_external_transceiver(uint8_t *burst)
{
trxcon::txq.spsc_push((trxd_to_trx *)burst);
}
}

View File

@@ -1,111 +0,0 @@
#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 <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "GSMCommon.h"
#include "radioClock.h"
#include "syncthing.h"
#include "l1if.h"
using tx_queue_t = spsc_cond<8 * 1, trxd_to_trx, true, false>;
class upper_trx : public ms_trx {
int rx_sps, tx_sps;
bool mOn; ///< flag to indicate that transceiver is powered on
double mTxFreq; ///< the transmit frequency
double mRxFreq; ///< the receive frequency
int mPower; ///< the transmit power in dB
unsigned mMaxExpectedDelay; ///< maximum TOA offset in GSM symbols
unsigned long long mRxSlotMask[8]; ///< MS - enabled multiframe slot mask
int mCtrlSockets;
sockaddr_in ctrldest;
sockaddr ctrlsrc;
void openudp(int *mSocketFD, unsigned short localPort, const char *wlocalIP)
{
*mSocketFD = socket(AF_INET, SOCK_DGRAM, 0);
int on = 1;
setsockopt(*mSocketFD, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
struct sockaddr_in address;
size_t length = sizeof(address);
bzero(&address, length);
address.sin_family = AF_INET;
address.sin_addr.s_addr = inet_addr(wlocalIP);
address.sin_port = htons(localPort);
if (bind(*mSocketFD, (struct sockaddr *)&address, length) < 0) {
std::cerr << "bind fail!" << std::endl;
exit(0);
}
}
bool resolveAddress(struct sockaddr_in *address, const char *host, unsigned short port)
{
struct hostent *hp;
int h_errno_local;
struct hostent hostData;
char tmpBuffer[2048];
auto rc = gethostbyname2_r(host, AF_INET, &hostData, tmpBuffer, sizeof(tmpBuffer), &hp, &h_errno_local);
if (hp == NULL || hp->h_addrtype != AF_INET || rc != 0) {
std::cerr << "WARNING -- gethostbyname() failed for " << host << ", "
<< hstrerror(h_errno_local);
exit(0);
return false;
}
address->sin_family = hp->h_addrtype;
assert(sizeof(address->sin_addr) == hp->h_length);
memcpy(&(address->sin_addr), hp->h_addr_list[0], hp->h_length);
address->sin_port = htons(port);
return true;
}
void driveControl();
void driveReceiveFIFO();
void driveTx();
void commandhandler(char *buffer, char *response);
void writeClockInterface(){};
SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
std::thread thr_control, thr_rx, thr_tx;
public:
void start_threads();
void start_ms();
upper_trx() : rx_sps(4), tx_sps(4)
{
auto c_srcport = 6700 + 2 * 0 + 1;
auto c_dstport = 6700 + 2 * 0 + 101;
openudp(&mCtrlSockets, c_srcport, "127.0.0.1");
resolveAddress(&ctrldest, "127.0.0.1", c_dstport);
};
};

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

@@ -0,0 +1,42 @@
#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 "ms.h"
extern "C" {
#include <osmocom/bb/trxcon/phyif.h>
}
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

@@ -0,0 +1,512 @@
/*
* (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 <csignal>
#include "sigProcLib.h"
#include "ms.h"
#include <signalVector.h>
#include <radioVector.h>
#include <radioInterface.h>
#include <grgsm_vitac/grgsm_vitac.h>
// #define TXDEBUG
extern "C" {
#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"
#include "threadsched.h"
extern bool trxc_l1ctl_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
#define LOG(...) upper_trx::dummy_log()
#endif
#define DBGLG(...) upper_trx::dummy_log()
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()
{
DBGLG(...) << "spawning threads.." << std::endl;
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;
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;
return 0;
},
this);
#ifdef LSANDEBUG
std::thread([this] {
set_name_aff_sched(sched_params::thread_names::LEAKCHECK);
while (1) {
std::this_thread::sleep_for(std::chrono::seconds{ 5 });
__lsan_do_recoverable_leak_check();
}
}).detach();
#endif
}
void upper_trx::main_loop()
{
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..
static void static_free(void *wData){};
static void *static_alloc(size_t newSize)
{
return 0;
};
bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
{
float pow, avg = 1.0;
const auto zero_pad_len = 40; // give the VA some runway for misaligned bursts
const auto workbuf_size = zero_pad_len + ONE_TS_BURST_LEN + zero_pad_len;
static complex workbuf[workbuf_size];
static signalVector sv(workbuf, zero_pad_len, ONE_TS_BURST_LEN, static_alloc, static_free);
one_burst e;
auto ss = reinterpret_cast<std::complex<float> *>(&workbuf[zero_pad_len]);
std::fill(workbuf, workbuf + workbuf_size, 0);
// assert(sv.begin() == &workbuf[40]);
while (!rxqueue.spsc_pop(&e)) {
rxqueue.spsc_prep_pop();
}
wTime = e.gsmts;
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_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;
if (is_fcch) {
// return trash
return true;
}
if (is_sch) {
for (int i = 0; i < 148; i++)
(demodded_softbits)[i] = (e.sch_bits[i]);
RSSI = 10;
timingOffset = 0;
return true;
}
#if 1
convert_and_scale(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
pow = energyDetect(sv, 20 * 4 /*sps*/);
if (pow < -1) {
LOG(ALERT) << "Received empty burst";
return false;
}
avg = sqrt(pow);
{
float ncmax;
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
auto normal_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp[0], &ncmax, mTSC);
#ifdef DBGXX
float dcmax;
std::complex<float> chan_imp_resp2[CHAN_IMP_RESP_LENGTH * d_OSR];
auto dummy_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp2[0], &dcmax, TS_DUMMY);
auto is_nb = ncmax > dcmax;
// DBGLG() << " U " << (is_nb ? "NB" : "DB") << "@ o nb: " << normal_burst_start
// << " o db: " << dummy_burst_start << std::endl;
#endif
normal_burst_start = normal_burst_start < 39 ? normal_burst_start : 39;
normal_burst_start = normal_burst_start > -39 ? normal_burst_start : -39;
#ifdef DBGXX
// fprintf(stderr, "%s %d\n", (is_nb ? "N":"D"), burst_time.FN());
// if (is_nb)
#endif
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);
#endif
}
#else
// lower layer sch detection offset, easy to verify by just printing the detected value using both the va+sigproc code.
convert_and_scale(ss + 16, e.burst, ONE_TS_BURST_LEN * 2, 15);
pow = energyDetect(sv, 20 * 4 /*sps*/);
if (pow < -1) {
LOG(ALERT) << "Received empty burst";
return false;
}
avg = sqrt(pow);
/* Detect normal or RACH bursts */
CorrType type = CorrType::TSC;
struct estim_burst_params ebp;
auto rc = detectAnyBurst(sv, mTSC, 3, 4, type, 48, &ebp);
if (rc > 0) {
type = (CorrType)rc;
}
if (rc < 0) {
std::cerr << "UR : \x1B[31m rx fail \033[0m @ toa:" << ebp.toa << " " << e.gsmts.FN() << ":"
<< e.gsmts.TN() << std::endl;
return false;
}
SoftVector *bits = demodAnyBurst(sv, type, 4, &ebp);
SoftVector::const_iterator burstItr = bits->begin();
// invert and fix to +-127 sbits
for (int ii = 0; ii < 148; ii++) {
demodded_softbits[ii] = *burstItr++ > 0.0f ? -127 : 127;
}
delete bits;
#endif
RSSI = (int)floor(20.0 * log10(rxFullScale / avg));
// FIXME: properly handle offset, sch/nb alignment diff? handled by lower anyway...
timingOffset = (int)round(0);
return true;
}
void upper_trx::driveReceiveFIFO()
{
int RSSI;
int TOA; // in 1/256 of a symbol
GSM::Time burstTime;
if (!mOn)
return;
if (pullRadioVector(burstTime, RSSI, TOA)) {
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(g_trxcon, &bi);
}
burstTime.incTN(2);
struct trxcon_phyif_rts_ind rts {
static_cast<uint32_t>(burstTime.FN()), static_cast<uint8_t>(burstTime.TN())
};
trxcon_phyif_handle_rts_ind(g_trxcon, &rts);
}
void upper_trx::driveTx()
{
internal_q_tx_buf e;
static BitVector newBurst(sizeof(e.buf));
while (!txq.spsc_pop(&e)) {
txq.spsc_prep_pop();
}
// ensure our tx cb is tickled and can exit
if (g_exit_flag) {
submit_burst_ts(0, 1337, 1);
return;
}
internal_q_tx_buf *burst = &e;
#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
auto currTime = GSM::Time(burst->r.fn, burst->r.tn);
int RSSI = (int)burst->r.pwr;
BitVector::iterator itr = newBurst.begin();
auto *bufferItr = burst->buf;
while (itr < newBurst.end())
*itr++ = *bufferItr++;
auto txburst = modulateBurst(newBurst, 8 + (currTime.TN() % 4 == 0), 4);
scaleVector(*txburst, txFullScale * pow(10, -RSSI / 10));
// float -> int16
blade_sample_type burst_buf[txburst->size()];
convert_and_scale(burst_buf, txburst->begin(), txburst->size() * 2, 1);
#ifdef TXDEBUG2
auto check = signalVector(txburst->size(), 40);
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)
DBGLG() << "RACH D! " << ebp.toa << std::endl;
else
DBGLG() << "RACH NOOOOOOOOOO D! " << ebp.toa << std::endl;
// memory read --binary --outfile /tmp/mem.bin &burst_buf[0] --count 2500 --force
#endif
submit_burst(burst_buf, txburst->size(), currTime);
delete txburst;
}
#ifdef TXDEBUG
static const char *cmd2str(trxcon_phyif_cmd_type c)
{
switch (c) {
case TRXCON_PHYIF_CMDT_RESET:
return "TRXCON_PHYIF_CMDT_RESET";
case TRXCON_PHYIF_CMDT_POWERON:
return "TRXCON_PHYIF_CMDT_POWERON";
case TRXCON_PHYIF_CMDT_POWEROFF:
return "TRXCON_PHYIF_CMDT_POWEROFF";
case TRXCON_PHYIF_CMDT_MEASURE:
return "TRXCON_PHYIF_CMDT_MEASURE";
case TRXCON_PHYIF_CMDT_SETFREQ_H0:
return "TRXCON_PHYIF_CMDT_SETFREQ_H0";
case TRXCON_PHYIF_CMDT_SETFREQ_H1:
return "TRXCON_PHYIF_CMDT_SETFREQ_H1";
case TRXCON_PHYIF_CMDT_SETSLOT:
return "TRXCON_PHYIF_CMDT_SETSLOT";
case TRXCON_PHYIF_CMDT_SETTA:
return "TRXCON_PHYIF_CMDT_SETTA";
default:
return "UNKNOWN COMMAND!";
}
}
static void print_cmd(trxcon_phyif_cmd_type c)
{
DBGLG() << "handling " << cmd2str(c) << std::endl;
}
#endif
bool upper_trx::driveControl()
{
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)
return false;
#ifdef TXDEBUG
print_cmd(cmd.type);
#endif
switch (cmd.type) {
case TRXCON_PHYIF_CMDT_RESET:
set_ta(0);
break;
case TRXCON_PHYIF_CMDT_POWERON:
if (!mOn) {
mOn = true;
start_lower_ms();
}
break;
case TRXCON_PHYIF_CMDT_POWEROFF:
break;
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(gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 0) * 1000 * 100);
tuneTx(gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 1) * 1000 * 100);
cmdq_from_phy.spsc_push(&r);
break;
case TRXCON_PHYIF_CMDT_SETFREQ_H0:
tuneRx(gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 0) * 1000 * 100);
tuneTx(gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 1) * 1000 * 100);
break;
case TRXCON_PHYIF_CMDT_SETFREQ_H1:
break;
case TRXCON_PHYIF_CMDT_SETSLOT:
break;
case TRXCON_PHYIF_CMDT_SETTA:
set_ta(cmd.param.setta.ta);
break;
}
return false;
}
void sighandler(int sigset)
{
// we might get a sigpipe in case the l1ctl ud socket disconnects because mobile quits
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_phyif_cmd cmd = {};
internal_q_tx_buf b = {};
txq.spsc_push(&b);
cmdq_to_phy.spsc_push(&cmd);
msleep(200);
return;
}
}
int main(int argc, char *argv[])
{
auto tall_trxcon_ctx = talloc_init("trxcon context");
signal(SIGPIPE, sighandler);
signal(SIGINT, sighandler);
msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
trxc_log_init(tall_trxcon_ctx);
/* 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();
sigProcLibSetup();
initvita();
int status = 0;
auto trx = new upper_trx();
trx->do_auto_gain = true;
status = trx->init_dev_and_streams();
if (status < 0) {
std::cerr << "Error initializing hardware, quitting.." << std::endl;
return -1;
}
set_name_aff_sched(sched_params::thread_names::MAIN);
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

@@ -19,7 +19,30 @@
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <netdb.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include "syncthing.h"
#include "GSMCommon.h"
#include "ms.h"
void rcv_bursts_test(rx_queue_t *q, unsigned int *tsc, int scale);
class upper_trx : public ms_trx {
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();
upper_trx() : mOn(false){};
};

View File

@@ -1,3 +1,25 @@
/*
* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
* (C) 2016 by Tom Tsou <tom.tsou@ettus.com>
* (C) 2017 by Harald Welte <laforge@gnumonks.org>
* (C) 2022 by 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> / Eric Wild <ewild@sysmocom.de>
*
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*/
#include <complex.h>
#include <stdio.h>
#include <math.h>
@@ -12,6 +34,9 @@
#include "sch.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
/* GSM 04.08, 9.1.30 Synchronization channel information */
struct sch_packed_info {
ubit_t t1_hi[2];
@@ -105,6 +130,14 @@ int gsm_sch_check_fn(int fn)
return 0;
}
int gsm_fcch_check_ts(int ts, int fn) {
return ts == 0 && gsm_fcch_check_fn(fn);
}
int gsm_sch_check_ts(int ts, int fn) {
return ts == 0 && gsm_sch_check_fn(fn);
}
/* SCH (T1, T2, T3p) to full FN value */
int gsm_sch_to_fn(struct sch_info *sch)
{
@@ -152,7 +185,7 @@ int gsm_sch_parse(const uint8_t *info, struct sch_info *desc)
}
/* From osmo-bts */
__attribute__((xray_always_instrument)) __attribute__((noinline)) int gsm_sch_decode(uint8_t *info, sbit_t *data)
int gsm_sch_decode(uint8_t *info, sbit_t *data)
{
int rc;
ubit_t uncoded[GSM_SCH_UNCODED_LEN];
@@ -292,3 +325,5 @@ static __attribute__((constructor)) void init()
}
}
#pragma GCC diagnostic pop

48
Transceiver52M/ms/sch.h Normal file
View File

@@ -0,0 +1,48 @@
#pragma once
/*
* (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
* (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
* (C) 2016 by Tom Tsou <tom.tsou@ettus.com>
* (C) 2017 by Harald Welte <laforge@gnumonks.org>
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> / Eric Wild <ewild@sysmocom.de>
*
* All Rights Reserved
*
* SPDX-License-Identifier: GPL-2.0+
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 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 General Public License for more details.
*/
#include <osmocom/core/bits.h>
struct sch_info {
int bsic;
int t1;
int t2;
int t3p;
};
#define GSM_SCH_INFO_LEN 25
#define GSM_SCH_UNCODED_LEN 35
#define GSM_SCH_CODED_LEN 78
int gsm_sch_decode(uint8_t *sb_info, sbit_t *burst);
int gsm_sch_parse(const uint8_t *sb_info, struct sch_info *desc);
int gsm_sch_to_fn(struct sch_info *sch);
int gsm_sch_check_fn(int fn);
int gsm_fcch_check_fn(int fn);
int gsm_fcch_check_ts(int ts, int fn);
int gsm_sch_check_ts(int ts, int fn);
double gsm_fcch_offset(float *burst, int len);
int float_to_sbit(const float *in, sbit_t *out, float scale, int len);

View File

@@ -1,337 +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 "GSMCommon.h"
#include <atomic>
#include <cassert>
#include <complex>
#include <iostream>
#include <cstdlib>
#include <cstdio>
#include <thread>
#include <fstream>
#include "sigProcLib.h"
#include "syncthing.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;
const int offset_start = -15;
int offsetrange = 200;
static int offset_ctr = 0;
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;
// 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;
unsigned int pad = 4 * 25;
blade_sample_type buf2[burst->size() + pad];
memset(buf2, 0, pad * sizeof(blade_sample_type));
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);
// auto this_offset = offset_start + (offset_ctr++ % offsetrange);
// std::cerr << "-- O " << this_offset << std::endl;
// send_ts = now_ts + ((target.FN() * 8 + (int)target.TN()) - (now_time.FN() * 8 + (int)now_time.TN())) * ONE_TS_BURST_LEN - timing_advance;
t->submit_burst_ts(buf2, burst->size() + pad, send_ts - pad);
// 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;
}
}
#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(0, 0);
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
int ms_trx::init_streams(void *rx_cb, void *tx_cb)
{
return 0;
}
int ms_trx::init_dev_and_streams(void *rx_cb, void *tx_cb)
{
int status = 0;
status = base::init_device(rx_bh(), tx_bh());
if (status < 0) {
std::cerr << "failed to init dev!" << std::endl;
return -1;
}
return status;
}
bh_fn_t ms_trx::rx_bh()
{
return [this](dev_buf_t *rcd) -> int {
if (this->search_for_sch(rcd) == SCH_STATE::FOUND)
this->grab_bursts(rcd);
return 0;
};
}
bh_fn_t ms_trx::tx_bh()
{
return [this](dev_buf_t *rcd) -> int {
#pragma unused(rcd)
auto y = this;
#pragma unused(y)
/* nothing to do here */
return 0;
};
}
void ms_trx::start()
{
auto fn = get_rx_burst_handler_fn(rx_bh());
rx_task = std::thread(fn);
usleep(1000);
auto fn2 = get_tx_burst_handler_fn(tx_bh());
tx_task = std::thread(fn2);
}
void ms_trx::set_upper_ready(bool is_ready)
{
upper_is_ready = is_ready;
}
void ms_trx::stop_threads()
{
std::cerr << "killing threads...\r\n" << std::endl;
rx_task.join();
}
void ms_trx::submit_burst(blade_sample_type *buffer, int len, GSM::Time target)
{
int64_t now_ts;
GSM::Time now_time;
target.incTN(3); // ul dl offset
int target_fn = target.FN();
int target_tn = target.TN();
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
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;
// 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;
#if 1
unsigned int pad = 4 * 4;
blade_sample_type buf2[len + pad];
memset(buf2, 0, pad * sizeof(blade_sample_type));
memcpy(&buf2[pad], buffer, len * sizeof(blade_sample_type));
assert(target.FN() == check.FN());
assert(target.TN() == check.TN());
submit_burst_ts(buf2, len + pad, send_ts - pad);
#else
submit_burst_ts(buffer, len, send_ts);
#endif
}

View File

@@ -0,0 +1,95 @@
#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 <atomic>
#include <vector>
#include <future>
#include <mutex>
#include <queue>
#include "threadsched.h"
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;
pthread_t 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 = spawn_worker_thread(
sched_params::thread_names::SCH_SEARCH,
[](void *args) -> void * {
using thist = decltype(this);
thist t = reinterpret_cast<thist>(args);
t->thread_loop();
return 0;
},
this);
}
~single_thread_pool()
{
stop();
}
private:
void stop()
{
{
std::unique_lock<std::mutex> l(m);
wq.clear();
stop_flag = true;
cv.notify_one();
}
pthread_join(worker_thread, nullptr);
}
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

@@ -0,0 +1,104 @@
/*
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <cerrno>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <thread>
extern "C" {
#include <pthread.h>
}
#include "threadsched.h"
sched_params::target scheduling_target;
void set_sched_target(sched_params::target t)
{
scheduling_target = t;
}
void set_name_aff_sched(std::thread::native_handle_type h, const char *name, int cpunum, int schedtype, int prio)
{
pthread_setname_np(h, name);
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpunum, &cpuset);
if (pthread_setaffinity_np(h, sizeof(cpuset), &cpuset) < 0) {
std::cerr << name << " affinity: errreur! " << std::strerror(errno);
return exit(0);
}
sched_param sch_params;
sch_params.sched_priority = prio;
if (pthread_setschedparam(h, schedtype, &sch_params) < 0) {
std::cerr << name << " sched: errreur! " << std::strerror(errno);
return exit(0);
}
}
static pthread_t do_spawn_thr(const char *name, int cpunum, int schedtype, int prio, worker_func_sig fun, void *arg)
{
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
sched_param sch_params;
sch_params.sched_priority = prio;
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpunum, &cpuset);
auto a = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
a |= pthread_attr_setschedpolicy(&attr, schedtype);
a |= pthread_attr_setschedparam(&attr, &sch_params);
a |= pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if (a)
std::cerr << "thread arg rc:" << a << std::endl;
pthread_create(&thread, &attr, fun, arg);
pthread_setname_np(thread, name);
pthread_attr_destroy(&attr);
return thread;
}
void set_name_aff_sched(std::thread::native_handle_type h, sched_params::thread_names name)
{
auto tgt = schdp[scheduling_target][name];
// std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << std::endl;
set_name_aff_sched(h, tgt.name, tgt.core, tgt.schedtype, tgt.prio);
}
void set_name_aff_sched(sched_params::thread_names name)
{
set_name_aff_sched(pthread_self(), name);
}
pthread_t spawn_worker_thread(sched_params::thread_names name, worker_func_sig fun, void *arg)
{
auto tgt = schdp[scheduling_target][name];
// std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << " prio:" << tgt.prio << std::endl;
return do_spawn_thr(tgt.name, tgt.core, tgt.schedtype, tgt.prio, fun, arg);
}

View File

@@ -0,0 +1,68 @@
#pragma once
/*
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
extern "C" {
#include <pthread.h>
#include <sched.h>
}
static struct sched_params {
enum thread_names { U_CTL = 0, U_RX, U_TX, SCH_SEARCH, MAIN, LEAKCHECK, RXRUN, TXRUN, _THRD_NAME_COUNT };
enum target { ODROID = 0, PI4 };
const char *name;
int core;
int schedtype;
int prio;
} schdp[][sched_params::_THRD_NAME_COUNT]{
{
{ "upper_ctrl", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) },
{ "upper_rx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
{ "upper_tx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 1 },
{ "sch_search", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
{ "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
{ "leakcheck", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
{ "rxrun", 4, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
{ "txrun", 5, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
},
{
{ "upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) },
{ "upper_rx", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
{ "upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
{ "sch_search", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
{ "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
{ "leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
{ "rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
{ "txrun", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
},
};
void set_sched_target(sched_params::target t);
using worker_func_sig = void *(*)(void *);
void set_name_aff_sched(sched_params::thread_names name);
pthread_t spawn_worker_thread(sched_params::thread_names name, worker_func_sig fun, void *arg);

View File

@@ -1,5 +1,4 @@
#pragma once
/*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
@@ -66,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;
@@ -77,26 +77,38 @@ template <typename T> struct uhd_hw {
const unsigned int rxFullScale, txFullScale;
const int rxtxdelay;
float rxgain, txgain;
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)
{
}
void close_device()
{
}
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;
};
@@ -129,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";
@@ -136,7 +149,8 @@ template <typename T> struct uhd_hw {
auto const freq = 931.4e6; // 936.8e6
auto bw = 0.5e6;
auto const channel = 0;
std::string args = {};
// aligned to blade: 1020 samples per transfer
std::string args = { "recv_frame_size=4092,send_frame_size=4092" };
dev = uhd::usrp::multi_usrp::make(args);
std::cout << "Using Device: " << dev->get_pp_string() << std::endl;
@@ -152,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);
@@ -168,27 +192,26 @@ 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;
void *ret = nullptr;
static int to_skip = 0;
uhd::rx_metadata_t md;
auto num_rx_samps = rx_stream->recv(pkt_ptrs.front(), rx_spp, md, 3.0, true);
auto num_rx_samps = rx_stream->recv(pkt_ptrs.front(), rx_spp, md, 1.0, true);
if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) {
std::cerr << boost::format("Timeout while streaming") << std::endl;
exit(0);
}
if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW) {
std::cerr << boost::format("Got an overflow indication. Please consider the following:\n"
" Your write medium must sustain a rate of %fMB/s.\n"
" Dropped samples will not be written to the file.\n"
" Please modify this example for your purposes.\n"
" This message will not appear again.\n") %
1.f;
std::cerr << boost::format("Got an overflow indication\n") << std::endl;
exit(0);
;
}
if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) {
std::cerr << str(boost::format("Receiver error: %s") % md.strerror());
@@ -208,24 +231,29 @@ template <typename T> struct uhd_hw {
auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
{
auto fn = [this, burst_handler] {
// C cb -> ghetto closure capture, which is fine, the args never change.
static auto rx_burst_cap_this = this;
static auto rx_burst_cap_bh = burst_handler;
auto fn = [](void *args) -> void * {
pthread_setname_np(pthread_self(), "rxrun");
uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
stream_cmd.stream_now = true;
stream_cmd.time_spec = uhd::time_spec_t();
rx_stream->issue_stream_cmd(stream_cmd);
rx_burst_cap_this->rx_stream->issue_stream_cmd(stream_cmd);
while (1) {
rx_cb(burst_handler);
while (!rx_burst_cap_this->stop_lower_threads_flag) {
rx_burst_cap_this->rx_cb(rx_burst_cap_bh);
}
return 0;
};
return fn;
}
auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
{
auto fn = [] {
auto fn = [](void *args) -> void * {
// dummy
return 0;
};
return fn;
}
@@ -238,37 +266,14 @@ template <typename T> struct uhd_hw {
m.time_spec = m.time_spec.from_ticks(ts + rxtxdelay, rxticks); // uhd specific b210 delay!
std::vector<void *> ptrs(1, buffer);
tx_stream->send(ptrs, len, m);
tx_stream->send(ptrs, len, m, 1.0);
#ifdef DBGXX
uhd::async_metadata_t async_md;
bool tx_ack = false;
while (!tx_ack && tx_stream->recv_async_msg(async_md)) {
tx_ack = (async_md.event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK);
}
std::cout << (tx_ack ? "yay" : "nay") << " " << async_md.time_spec.to_ticks(rxticks) << std::endl;
}
void set_name_aff_sched(const char *name, int cpunum, int schedtype, int prio)
{
pthread_setname_np(pthread_self(), name);
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpunum, &cpuset);
auto rv = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
if (rv < 0) {
std::cerr << name << " affinity: errreur! " << std::strerror(errno);
return exit(0);
}
sched_param sch_params;
sch_params.sched_priority = prio;
rv = pthread_setschedparam(pthread_self(), schedtype, &sch_params);
if (rv < 0) {
std::cerr << name << " sched: errreur! " << std::strerror(errno);
return exit(0);
}
#endif
}
};

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,
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,11 +665,11 @@ 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);
g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_TRX, NULL);
g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL);
if (!g_ctrlh) {
LOG(ERROR) << "Failed to create CTRL interface.\n";
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

@@ -1,27 +0,0 @@
#ifndef _SCH_H_
#define _SCH_H_
#include <osmocom/core/bits.h>
struct sch_info {
int bsic;
int t1;
int t2;
int t3p;
};
#define GSM_SCH_INFO_LEN 25
#define GSM_SCH_UNCODED_LEN 35
#define GSM_SCH_CODED_LEN 78
int gsm_sch_decode(uint8_t *sb_info, sbit_t *burst);
int gsm_sch_parse(const uint8_t *sb_info, struct sch_info *desc);
int gsm_sch_to_fn(struct sch_info *sch);
int gsm_sch_check_fn(int fn);
int gsm_fcch_check_fn(int fn);
double gsm_fcch_offset(float *burst, int len);
int float_to_sbit(const float *in, sbit_t *out, float scale, int len);
#endif /* _SCH_H_ */

View File

@@ -87,19 +87,17 @@ static Resampler *dnsampler = NULL;
* perform 16-byte memory alignment required by many SSE instructions.
*/
struct CorrelationSequence {
CorrelationSequence() : sequence(NULL), buffer(NULL), toa(0.0), history(nullptr)
CorrelationSequence() : sequence(NULL), buffer(NULL), toa(0.0)
{
}
~CorrelationSequence()
{
delete sequence;
delete[] history;
}
signalVector *sequence;
void *buffer;
complex *history;
float toa;
complex gain;
};
@@ -325,7 +323,6 @@ static signalVector *convolve(const signalVector *x, const signalVector *h,
append = true;
break;
case CUSTOM:
// FIXME: x->getstart?
if (start < h->size() - 1) {
head = h->size() - start;
@@ -1100,7 +1097,6 @@ signalVector *delayVector(const signalVector *in, signalVector *out, float delay
return out;
}
__attribute__((xray_never_instrument))
static complex interpolatePoint(const signalVector &inSig, float ix)
{
int start = (int) (floor(ix) - 10);
@@ -1494,7 +1490,7 @@ bool generateSCHSequence(int sps)
data = (complex *) convolve_h_alloc(seq1->size());
_seq1 = new signalVector(data, 0, seq1->size(), convolve_h_alloc, free);
_seq1->setAligned(true);
memcpy(_seq1->begin(), seq1->begin(), seq1->size() * sizeof(complex));
seq1->copyTo(*_seq1);
autocorr = convolve(seq0, _seq1, autocorr, NO_DELAY);
if (!autocorr) {
@@ -1506,7 +1502,6 @@ bool generateSCHSequence(int sps)
gSCHSequence->sequence = _seq1;
gSCHSequence->buffer = data;
gSCHSequence->gain = peakDetect(*autocorr, &toa, NULL);
gSCHSequence->history = new complex[_seq1->size()];
/* For 1 sps only
* (Half of correlation length - 1) + midpoint of pulse shaping filer
@@ -1621,7 +1616,7 @@ static float computeCI(const signalVector *burst, const CorrelationSequence *syn
if(ps < 0) // might be -22 for toa 40 with N=64, if off by a lot during sch ms sync
return 0;
if (ps + N > burst->size())
if (ps + N > (int)burst->size())
return 0;
/* Estimate Signal power */
@@ -1666,7 +1661,7 @@ static int detectBurst(const signalVector &burst,
corr_in = &burst;
break;
case 4:
dec = downsampleBurst(burst, len * 4, len);
dec = downsampleBurst(burst);
/* Running at the downsampled rate at this point: */
corr_in = dec;
sps = 1;
@@ -1748,11 +1743,11 @@ static int detectGeneralBurst(const signalVector &rxBurst, float thresh, int sps
// Detect potential clipping
// We still may be able to demod the burst, so we'll give it a try
// and only report clipping if we can't demod.
// float maxAmpl = maxAmplitude(rxBurst);
// if (maxAmpl > CLIP_THRESH) {
// LOG(INFO) << "max burst amplitude: " << maxAmpl << " is above the clipping threshold: " << CLIP_THRESH << std::endl;
// clipping = true;
// }
float maxAmpl = maxAmplitude(rxBurst);
if (maxAmpl > CLIP_THRESH) {
LOG(INFO) << "max burst amplitude: " << maxAmpl << " is above the clipping threshold: " << CLIP_THRESH << std::endl;
clipping = true;
}
start = target - head - 1;
len = head + tail;
@@ -1813,7 +1808,6 @@ int detectSCHBurst(signalVector &burst,
sch_detect_type state, struct estim_burst_params *ebp)
{
int rc, start, target, head, tail, len;
float _toa;
complex _amp;
CorrelationSequence *sync;
@@ -1844,14 +1838,9 @@ int detectSCHBurst(signalVector &burst,
sync = gSCHSequence;
signalVector corr(len);
signalVector _burst(burst, sync->sequence->size(), 5);
memcpy(_burst.begin() - sync->sequence->size(), sync->history, sync->sequence->size() * sizeof(complex));
memcpy(sync->history, &burst.begin()[burst.size() - sync->sequence->size()],
sync->sequence->size() * sizeof(complex));
rc = detectBurst(_burst, corr, sync, thresh, sps, start, len, ebp);
signalVector *dec = downsampleBurst(burst, len * 4, len);
rc = detectBurst(*dec, corr, sync, thresh, 1, start, len, ebp);
delete dec;
if (rc < 0) {
return -1;

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(
@@ -143,6 +143,11 @@ AC_ARG_WITH(bladerf, [
[enable bladeRF])
])
AC_ARG_WITH(mstrx, [
AS_HELP_STRING([--with-mstrx],
[enable MS TRX])
])
AC_ARG_WITH(singledb, [
AS_HELP_STRING([--with-singledb],
[enable single daughterboard use on USRP1])
@@ -190,12 +195,39 @@ AS_IF([test "x$with_uhd" = "xyes"],[
[PKG_CHECK_MODULES(UHD, uhd >= 003.005)]
)]
)
# OS#5608: libuhd < 4.2.0 includes boost/thread/thread.hpp in its logging
# code and therefore requires linking against boost_thread. It's missing in
# uhd.pc, so work around it here.
# https://github.com/EttusResearch/uhd/commit/04a83b6e76beef970854da69ba882d717669b49c
PKG_CHECK_MODULES(UHD, uhd < 004.002,
[LIBS="$LIBS -lboost_thread"],
[]
)
])
AS_IF([test "x$with_bladerf" = "xyes"], [
PKG_CHECK_MODULES(BLADE, libbladeRF >= 2.0)
])
AC_MSG_CHECKING([whether to enable building MS TRX])
AS_IF([test "x$with_mstrx" = "xyes"], [
AC_CONFIG_SUBDIRS([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,
# autoconf/automake still requires the directory to be present
# and thus the submodule to be fetched (even if MS TRX is not needed).
# Work this around by pointing it to an empty dir.
AC_SUBST(LIBTRXCON_DIR, "osmocom-bb")
AC_MSG_RESULT([no])
])
AS_IF([test "x$with_singledb" = "xyes"], [
AC_DEFINE(SINGLEDB, 1, Define to 1 for single daughterboard)
])
@@ -252,6 +284,7 @@ AM_CONDITIONAL(DEVICE_IPC, [test "x$with_ipc" = "xyes"])
AM_CONDITIONAL(DEVICE_BLADE, [test "x$with_bladerf" = "xyes"])
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
AM_CONDITIONAL(ENABLE_MS_TRX, [test "x$with_mstrx" = "xyes"])
PKG_CHECK_MODULES(LIBUSB, libusb-1.0)
PKG_CHECK_MODULES(FFTWF, fftw3f)
@@ -319,10 +352,10 @@ AC_MSG_RESULT([CFLAGS="$CFLAGS"])
AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"])
AC_MSG_RESULT([LDFLAGS="$LDFLAGS"])
dnl Output files
AC_CONFIG_FILES([\
Makefile \
trxcon/Makefile \
CommonLibs/Makefile \
GSM/Makefile \
Transceiver52M/Makefile \
@@ -336,18 +369,17 @@ AC_CONFIG_FILES([\
Transceiver52M/device/usrp1/Makefile \
Transceiver52M/device/lms/Makefile \
Transceiver52M/device/ipc/Makefile \
Transceiver52M/device/ipc2/Makefile \
Transceiver52M/device/bladerf/Makefile \
tests/Makefile \
tests/CommonLibs/Makefile \
tests/Transceiver52M/Makefile \
utils/Makefile \
utils/va-test/Makefile \
doc/Makefile \
doc/examples/Makefile \
contrib/Makefile \
contrib/systemd/Makefile \
doc/manuals/Makefile \
contrib/osmo-trx.spec \
])
AC_OUTPUT(
doc/manuals/Makefile
contrib/osmo-trx.spec)
AC_OUTPUT

View File

@@ -85,7 +85,7 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib"
export PATH="$inst/bin:$PATH"
CONFIG="--enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms --with-ipc $INSTR"
CONFIG="--enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms --with-ipc --with-mstrx $INSTR"
# Additional configure options and depends
if [ "$WITH_MANUALS" = "1" ]; then
@@ -101,13 +101,17 @@ echo
set -x
cd "$base"
git submodule status
autoreconf --install --force
./configure $CONFIG
$MAKE $PARALLEL_MAKE
$MAKE check \
|| cat-testlogs.sh
if arch | grep -v -q arm; then
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck \
|| cat-testlogs.sh
fi
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
make -C "$base/doc/manuals" publish

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,11 +1,20 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (IPC Backend)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Restart=always
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
ExecStart=/usr/bin/osmo-trx-ipc -C /etc/osmocom/osmo-trx-ipc.cfg
RestartSec=2
# CPU scheduling policy:
CPUSchedulingPolicy=rr
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
CPUSchedulingPriority=21
# See sched(7) for further details on real-time policies and priorities
[Install]
WantedBy=multi-user.target

View File

@@ -1,11 +1,20 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (LimeSuite backend)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Restart=always
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
ExecStart=/usr/bin/osmo-trx-lms -C /etc/osmocom/osmo-trx-lms.cfg
RestartSec=2
# CPU scheduling policy:
CPUSchedulingPolicy=rr
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
CPUSchedulingPriority=21
# See sched(7) for further details on real-time policies and priorities
[Install]
WantedBy=multi-user.target

View File

@@ -1,11 +1,21 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (UHD Backend)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Restart=always
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
Environment=HOME=%h
ExecStart=/usr/bin/osmo-trx-uhd -C /etc/osmocom/osmo-trx-uhd.cfg
RestartSec=2
# CPU scheduling policy:
CPUSchedulingPolicy=rr
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
CPUSchedulingPriority=21
# See sched(7) for further details on real-time policies and priorities
[Install]
WantedBy=multi-user.target

View File

@@ -1,11 +1,20 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (libusrp backend)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
Restart=always
StateDirectory=osmocom
WorkingDirectory=%S/osmocom
ExecStart=/usr/bin/osmo-trx-usrp1 -C /etc/osmocom/osmo-trx-usrp1.cfg
RestartSec=2
# CPU scheduling policy:
CPUSchedulingPolicy=rr
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
CPUSchedulingPriority=21
# See sched(7) for further details on real-time policies and priorities
[Install]
WantedBy=multi-user.target

122
debian/changelog vendored
View File

@@ -1,3 +1,125 @@
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 ]
* treewide: remove FSF address
[ Vadim Yanitskiy ]
* tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
[ Harald Welte ]
* update git URLs (git -> https; gitea)
-- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 29 Jun 2022 09:32:56 +0200
osmo-trx (1.4.0) unstable; urgency=medium
[ Pau Espin Pedrol ]

2
debian/compat vendored
View File

@@ -1 +1 @@
9
10

30
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,11 +14,12 @@ Build-Depends: debhelper (>= 9),
libtalloc-dev,
libusrp-dev,
liblimesuite-dev,
libosmocore-dev (>= 1.6.0),
osmo-gsm-manuals-dev
libbladerf-dev,
libosmocore-dev (>= 1.9.0),
osmo-gsm-manuals-dev (>= 1.5.0)
Standards-Version: 3.9.6
Vcs-Browser: http://cgit.osmocom.org/osmo-trx
Vcs-Git: git://git.osmocom.org/osmo-trx
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
Homepage: https://projects.osmocom.org/projects/osmotrx
Package: osmo-trx
@@ -110,6 +111,25 @@ Description: SDR transceiver that implements Layer 1 of a GSM BTS (generic IPC)
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)
Package: osmo-trx-ms-blade
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}
Description: MS side transceiver (bladeRF)
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications:
.
TS 05.01 "Physical layer on the radio path"
TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
TS 05.04 "Modulation"
TS 05.10 "Radio subsystem synchronization"
.
In this context, BTS is "Base transceiver station". It's the stations that
connect mobile phones to the mobile network.
.
3GPP is the "3rd Generation Partnership Project" which is the collaboration
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)
Package: osmo-trx-doc
Architecture: all
Section: doc

2
debian/copyright vendored
View File

@@ -1,6 +1,6 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: OsmoTRX
Source: http://cgit.osmocom.org/osmo-trx/
Source: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
Files-Excluded: Transceiver52M/device/usrp1/std_inband.rbf
Files: *

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

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

11
debian/rules vendored
View File

@@ -9,7 +9,16 @@ override_dh_shlibdeps:
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
override_dh_auto_configure:
dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-ipc --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
dh_auto_configure -- \
--enable-manuals \
--with-systemdsystemunitdir=/lib/systemd/system \
--with-bladerf \
--with-ipc \
--with-lms \
--with-mstrx \
--with-uhd \
--with-usrp1 \
$(NULL)
override_dh_strip:
dh_strip --dbg-package=osmo-trx-dbg

View File

@@ -1,3 +1,15 @@
# all config examples must be listed here unconditionally, so that
# all of them end up in the release tarball (see OS#6349)
EXTRA_DIST = \
osmo-trx-uhd/osmo-trx-limesdr.cfg \
osmo-trx-uhd/osmo-trx-usrp_b200.cfg \
osmo-trx-uhd/osmo-trx-uhd.cfg \
osmo-trx-uhd/osmo-trx-umtrx.cfg \
osmo-trx-lms/osmo-trx-limesdr.cfg \
osmo-trx-lms/osmo-trx-lms.cfg \
osmo-trx-ipc/osmo-trx-ipc.cfg \
$(NULL)
OSMOCONF_FILES =
osmoconfdir = $(sysconfdir)/osmocom
@@ -19,7 +31,6 @@ OSMOCONF_FILES += osmo-trx-ipc/osmo-trx-ipc.cfg
endif
osmoconf_DATA = $(OSMOCONF_FILES)
EXTRA_DIST = $(OSMOCONF_FILES)
CFG_FILES = find $(srcdir) -type f -name '*.cfg*' | sed -e 's,^$(srcdir),,'

View File

@@ -1,9 +1,11 @@
log stderr
logging filter all 1
logging color 1
logging print category-hex 0
logging print category 1
logging timestamp 1
logging print file basename
logging timestamp 0
logging print file basename last
logging print level 1
logging level set-all notice
!
line vty

Some files were not shown because too many files have changed in this diff Show More