Compare commits

..

8 Commits

Author SHA1 Message Date
Eric
2ecd9f698f trcon<->l1 data if without sockets
direct call of UL rx receive data handler + sched check if ts is active
tx dir call stub for for tx thread queue.

Change-Id: I5911004db58742cf39b968fcf87bc1243f7a374a
2022-10-31 15:01:26 +01:00
Eric
70bd9415a2 trxcon/upper loop thread merge hack
Change-Id: Ia42f9df3050d7e6cc558f4d60e08955a0fa70b8a
2022-10-31 14:02:30 +01:00
Eric
2c12b30ace new ms
Change-Id: I7c5abe57182e7ef508cac4068c0b41f905d39fd6
2022-10-31 14:02:11 +01:00
Eric
607141bf34 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-07-19 21:23:06 +02:00
Eric Wild
901f689086 add linked trxcon copy
Change-Id: Ic746c26e527f4505188729d6e5df47435daac96f
2022-07-19 21:22:34 +02:00
Eric
966af04ff1 rename noisevector class -> avgvector
The vectors feature is averaging, and not adding noise.

Change-Id: I05def8ab9ea7a2cece8db09c36c303e13ef40927
2022-07-19 21:22:34 +02:00
Eric
d5cafc2cc0 xray ignores
tiny functions, do not want.

Change-Id: Ie55458f31d16e76e84855ed2c634a9dd9a5e139b
2022-07-19 21:22:34 +02:00
Eric
b8ef806c25 ignore vscode dirs
Change-Id: Iad9fd20924b7cfc6dbbfb708aa9c692a3cab574c
2022-07-19 21:04:20 +02:00
153 changed files with 16345 additions and 5363 deletions

View File

@@ -1,5 +0,0 @@
--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 AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true AlwaysBreakTemplateDeclarations: false
BinPackArguments: true BinPackArguments: true
BinPackParameters: true BinPackParameters: true
BraceWrapping: BraceWrapping:
@@ -515,7 +515,7 @@ SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false SpacesInCStyleCastParentheses: false
SpacesInParentheses: false SpacesInParentheses: false
SpacesInSquareBrackets: false SpacesInSquareBrackets: false
Standard: Cpp11 Standard: Cpp03
TabWidth: 8 TabWidth: 8
UseTab: Always UseTab: Always
... ...

1
.github/FUNDING.yml vendored
View File

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

4
.gitignore vendored
View File

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

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[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 include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
noinst_LTLIBRARIES = libcommon.la noinst_LTLIBRARIES = libcommon.la
@@ -37,12 +37,7 @@ libcommon_la_SOURCES = \
trx_rate_ctr.cpp \ trx_rate_ctr.cpp \
trx_vty.c \ trx_vty.c \
debug.c debug.c
libcommon_la_LIBADD = \ libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_LIBS) \
-lpthread \
$(NULL)
noinst_HEADERS = \ noinst_HEADERS = \
BitVector.h \ BitVector.h \

View File

@@ -57,15 +57,4 @@ struct trx_cfg {
unsigned int stack_size; unsigned int stack_size;
unsigned int num_chans; unsigned int num_chans;
struct trx_chan chans[TRX_CHAN_MAX]; 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,8 @@ static void threshold_timer_update_intv() {
return; return;
if (llist_empty(&threshold_list)) { if (llist_empty(&threshold_list)) {
osmo_timer_del(&threshold_timer); if (osmo_timer_pending(&threshold_timer))
osmo_timer_del(&threshold_timer);
return; return;
} }

View File

@@ -285,79 +285,6 @@ DEFUN_ATTR(cfg_ul_fn_offset, cfg_ul_fn_offset_cmd,
return CMD_SUCCESS; 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, DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
"swap-channels (disable|enable)", "swap-channels (disable|enable)",
"Swap primary and secondary channels of the PHY (if any)\n" "Swap primary and secondary channels of the PHY (if any)\n"
@@ -539,6 +466,7 @@ DEFUN_ATTR(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,
int rc; int rc;
struct ctr_threshold ctr; struct ctr_threshold ctr;
struct trx_ctx *trx = trx_from_vty(vty);
rc = vty_ctr_name_2_id(argv[0]); rc = vty_ctr_name_2_id(argv[0]);
if (rc < 0) { if (rc < 0) {
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s", vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
@@ -570,6 +498,7 @@ DEFUN_ATTR(cfg_no_ctr_error_threshold, cfg_no_ctr_error_threshold_cmd,
int rc; int rc;
struct ctr_threshold ctr; struct ctr_threshold ctr;
struct trx_ctx *trx = trx_from_vty(vty);
rc = vty_ctr_name_2_id(argv[0]); rc = vty_ctr_name_2_id(argv[0]);
if (rc < 0) { if (rc < 0) {
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s", vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
@@ -680,7 +609,7 @@ static int config_write_trx(struct vty *vty)
vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE); vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
if (trx->cfg.base_port != DEFAULT_TRX_PORT) if (trx->cfg.base_port != DEFAULT_TRX_PORT)
vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE); vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE);
if (strlen(trx->cfg.dev_args)) if (trx->cfg.dev_args)
vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE); vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE);
if (trx->cfg.tx_sps != DEFAULT_TX_SPS) if (trx->cfg.tx_sps != DEFAULT_TX_SPS)
vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE); vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
@@ -711,16 +640,6 @@ static int config_write_trx(struct vty *vty)
vty_out(vty, " stack-size %u%s", trx->cfg.stack_size, VTY_NEWLINE); vty_out(vty, " stack-size %u%s", trx->cfg.stack_size, VTY_NEWLINE);
if (trx->cfg.ul_fn_offset != 0) if (trx->cfg.ul_fn_offset != 0)
vty_out(vty, " ul-fn-offset %d%s", trx->cfg.ul_fn_offset, VTY_NEWLINE); 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, " "); trx_rate_ctr_threshold_write_config(vty, " ");
for (i = 0; i < trx->cfg.num_chans; i++) { for (i = 0; i < trx->cfg.num_chans; i++) {
@@ -843,7 +762,6 @@ struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx)
trx->cfg.rx_sps = DEFAULT_RX_SPS; trx->cfg.rx_sps = DEFAULT_RX_SPS;
trx->cfg.filler = FILLER_ZERO; trx->cfg.filler = FILLER_ZERO;
trx->cfg.rssi_offset = 0.0f; trx->cfg.rssi_offset = 0.0f;
trx->cfg.dev_args = talloc_strdup(trx, "");
return trx; return trx;
} }
@@ -886,11 +804,6 @@ int trx_vty_init(struct trx_ctx* trx)
install_element(TRX_NODE, &cfg_chan_cmd); install_element(TRX_NODE, &cfg_chan_cmd);
install_element(TRX_NODE, &cfg_ul_fn_offset_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_node(&chan_node, dummy_config_write);
install_element(CHAN_NODE, &cfg_chan_rx_path_cmd); install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
install_element(CHAN_NODE, &cfg_chan_tx_path_cmd); install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);

View File

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

View File

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

View File

@@ -4,17 +4,17 @@ About OsmoTRX
OsmoTRX is a software-defined radio transceiver that implements the Layer 1 OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications: physical layer of a BTS comprising the following 3GPP specifications:
* TS 05.01 *Physical layer on the radio path* * TS 05.01 "Physical layer on the radio path"
* TS 05.02 *Multiplexing and Multiple Access on the Radio Path* * TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
* TS 05.04 *Modulation* * TS 05.04 "Modulation"
* TS 05.10 *Radio subsystem synchronization* * TS 05.10 "Radio subsystem synchronization"
OsmoTRX is originally based on the transceiver code from the OsmoTRX is originally based on the transceiver code from the
[OpenBTS](https://osmocom.org/projects/osmobts/wiki/OpenBTS) project, but setup [OpenBTS](https://osmocom.org/projects/osmobts/wiki/OpenBTS) project, but setup
to operate independently with the purpose of using with non-OpenBTS software and to operate independently with the purpose of using with non-OpenBTS software and
projects, specifically within the Osmocom stack. Used together with projects, specifically within the Osmocom stack. Used together with
[OsmoBTS](https://osmocom.org/projects/osmobts/wiki) you can get a pretty [OsmoBTS](https://osmocom.org/projects/osmobts/wiki) you can get a pretty
standard GSM/GPRS/EGPRS BTS with Abis interface as per the relevant 3GPP specifications. standard GSM BTS with Abis interface as per the relevant 3GPP specifications.
Homepage Homepage
-------- --------
@@ -27,9 +27,9 @@ GIT Repository
You can clone from the official osmo-trx.git repository using You can clone from the official osmo-trx.git repository using
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-trx git clone git://git.osmocom.org/osmo-trx.git
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-trx> There is a cgit interface at <https://git.osmocom.org/osmo-trx/>
Documentation Documentation
------------- -------------
@@ -39,13 +39,6 @@ also available online for each of the sub-libraries at User Manual for OsmoTRX
can be generated during the build process, and is also available online at can be generated during the build process, and is also available online at
<https://ftp.osmocom.org/docs/latest/osmotrx-usermanual.pdf>. <https://ftp.osmocom.org/docs/latest/osmotrx-usermanual.pdf>.
Forum
-----
We welcome any osmo-trx related discussions in the
[Cellular Network Infrastructure -> 2 RAN (GERAN)](https://discourse.osmocom.org/c/cni/geran)
section of the osmocom discourse (web based Forum).
Mailing List Mailing List
------------ ------------
@@ -57,20 +50,13 @@ Please observe the [Osmocom Mailing List
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules) Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
when posting. when posting.
Issue Tracker
-------------
We use the [issue tracker of the osmo-trx project on osmocom.org](https://osmocom.org/projects/osmotrx/issues) for
tracking the state of bug reports and feature requests. Feel free to submit any issues you may find, or help
us out by resolving existing issues.
Contributing Contributing
------------ ------------
Our coding standards are described at Our coding standards are described at
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards> <https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
We use a Gerrit based patch submission/review process for managing contributions. We us a gerrit based patch submission/review process for managing contributions.
Please see <https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit> Please see <https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit>
for more details for more details

View File

@@ -1,9 +0,0 @@
# When cleaning up this file: bump API version in corresponding Makefile.am and rename corresponding debian/lib*.install
# according to https://osmocom.org/projects/cellular-infrastructure/wiki/Make_a_new_release
# In short: https://www.gnu.org/software/libtool/manual/html_node/Updating-version-info.html#Updating-version-info
# LIBVERSION=c:r:a
# If the library source code has changed at all since the last update, then increment revision: c:r + 1:a.
# If any interfaces have been added, removed, or changed since the last update: c + 1:0:a.
# If any interfaces have been added since the last public release: c:r:a + 1.
# If any interfaces have been removed or changed since the last public release: c:r:0.
#library what description / commit summary line

View File

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

View File

@@ -24,12 +24,13 @@ include $(top_srcdir)/Makefile.common
SUBDIRS = arch device SUBDIRS = arch device
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device/common AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device/common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) 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) AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) -mcpu=cortex-a72 -mfloat-abi=hard -mfpu=neon-fp-armv8
noinst_LTLIBRARIES = libtransceiver_common.la noinst_LTLIBRARIES = libtransceiver_common.la
COMMON_SOURCES = \ COMMON_SOURCES = \
l1if.cpp \
radioInterface.cpp \ radioInterface.cpp \
radioVector.cpp \ radioVector.cpp \
radioClock.cpp \ radioClock.cpp \
@@ -41,6 +42,7 @@ COMMON_SOURCES = \
Channelizer.cpp \ Channelizer.cpp \
Synthesis.cpp \ Synthesis.cpp \
proto_trxd.c \ proto_trxd.c \
sch.c \
grgsm_vitac/grgsm_vitac.cpp \ grgsm_vitac/grgsm_vitac.cpp \
grgsm_vitac/viterbi_detector.cc grgsm_vitac/viterbi_detector.cc
@@ -63,7 +65,9 @@ noinst_HEADERS = \
ChannelizerBase.h \ ChannelizerBase.h \
Channelizer.h \ Channelizer.h \
Synthesis.h \ Synthesis.h \
proto_trxd.h proto_trxd.h \
grgsm_vitac/viterbi_detector.h \
grgsm_vitac/constants.h
COMMON_LDADD = \ COMMON_LDADD = \
libtransceiver_common.la \ libtransceiver_common.la \
@@ -72,50 +76,10 @@ COMMON_LDADD = \
$(COMMON_LA) \ $(COMMON_LA) \
$(FFTWF_LIBS) \ $(FFTWF_LIBS) \
$(LIBOSMOCORE_LIBS) \ $(LIBOSMOCORE_LIBS) \
$(LIBOSMOCODING_LIBS) \
$(LIBOSMOCTRL_LIBS) \ $(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_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 = bin_PROGRAMS =
if DEVICE_UHD if DEVICE_UHD
@@ -127,16 +91,23 @@ osmo_trx_uhd_LDADD = \
$(UHD_LIBS) $(UHD_LIBS)
osmo_trx_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) osmo_trx_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
#if ENABLE_MS_TRX bin_PROGRAMS += osmo-trx-ms-uhd
#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 = \
#osmo_trx_ms_uhd_LDADD = \ $(builddir)/device/bladerf/libdevice.la \
# $(builddir)/device/uhd/libdevice.la \ $(COMMON_LDADD) \
# $(COMMON_LDADD) \ $(UHD_LIBS) \
# $(UHD_LIBS) \ $(TRXCON_LA)
# $(TRXCON_LDADD) osmo_trx_ms_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DBUILDUHD
#osmo_trx_ms_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DBUILDUHD
#endif 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 if DEVICE_USRP1
@@ -168,16 +139,23 @@ osmo_trx_blade_LDADD = \
$(BLADE_LIBS) $(BLADE_LIBS)
osmo_trx_blade_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS) osmo_trx_blade_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
if ENABLE_MS_TRX
bin_PROGRAMS += osmo-trx-ms-blade bin_PROGRAMS += osmo-trx-ms-blade
osmo_trx_ms_blade_SOURCES = $(MS_LOWER_SRC) $(MS_UPPER_SRC) 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_LDADD = \ osmo_trx_ms_blade_LDADD = \
$(builddir)/device/bladerf/libdevice.la \ $(builddir)/device/bladerf/libdevice.la \
$(COMMON_LDADD) \ $(COMMON_LDADD) \
$(BLADE_LIBS) \ $(BLADE_LIBS) \
$(TRXCON_LDADD) $(TRXCON_LA)
osmo_trx_ms_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DBUILDBLADE osmo_trx_ms_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DBUILDBLADE
endif
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 if DEVICE_IPC
@@ -187,4 +165,32 @@ osmo_trx_ipc_LDADD = \
$(builddir)/device/ipc/libdevice.la \ $(builddir)/device/ipc/libdevice.la \
$(COMMON_LDADD) $(COMMON_LDADD)
osmo_trx_ipc_CPPFLAGS = $(AM_CPPFLAGS) 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 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

@@ -32,7 +32,7 @@ extern "C" {
#define M_PI 3.14159265358979323846264338327f #define M_PI 3.14159265358979323846264338327f
#endif #endif
#define MAX_OUTPUT_LEN 4096*4 #define MAX_OUTPUT_LEN 4096 * 4
using namespace std; using namespace std;

View File

@@ -29,7 +29,6 @@
#include <fstream> #include <fstream>
#include "Transceiver.h" #include "Transceiver.h"
#include <Logger.h> #include <Logger.h>
#include <grgsm_vitac/grgsm_vitac.h>
extern "C" { extern "C" {
#include "osmo_signal.h" #include "osmo_signal.h"
@@ -209,10 +208,8 @@ bool Transceiver::init()
return false; return false;
} }
initvita();
mDataSockets.resize(mChans, -1); mDataSockets.resize(mChans, -1);
/* Filler table retransmissions - support only on channel 0 */ /* Filler table retransmissions - support only on channel 0 */
if (cfg->filler == FILLER_DUMMY) if (cfg->filler == FILLER_DUMMY)
@@ -581,7 +578,7 @@ CorrType Transceiver::expectedCorrType(GSM::Time currTime,
case XIII: { case XIII: {
int mod52 = burstFN % 52; int mod52 = burstFN % 52;
if ((mod52 == 12) || (mod52 == 38)) if ((mod52 == 12) || (mod52 == 38))
return RACH; /* RACH is always 8-bit on PTCCH/U */ return cfg->ext_rach ? EXT_RACH : RACH;
else if ((mod52 == 25) || (mod52 == 51)) else if ((mod52 == 25) || (mod52 == 51))
return IDLE; return IDLE;
else /* Enable 8-PSK burst detection if EDGE is enabled */ else /* Enable 8-PSK burst detection if EDGE is enabled */
@@ -617,44 +614,6 @@ double Transceiver::rssiOffset(size_t chan)
return mRadioInterface->rssiOffset(chan) + cfg->rssi_offset; 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 * Pull bursts from the FIFO and handle according to the slot
* and burst correlation type. Equalzation is currently disabled. * and burst correlation type. Equalzation is currently disabled.
@@ -675,9 +634,6 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
TransceiverState *state = &mStates[chan]; TransceiverState *state = &mStates[chan];
bool ctr_changed = false; bool ctr_changed = false;
double rssi_offset; 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 */ /* Blocking FIFO read */
radioVector *radio_burst = mReceiveFIFO[chan]->read(); radioVector *radio_burst = mReceiveFIFO[chan]->read();
@@ -757,15 +713,8 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
max_toa = (type == RACH || type == EXT_RACH) ? max_toa = (type == RACH || type == EXT_RACH) ?
mMaxExpectedDelayAB : mMaxExpectedDelayNB; 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 */ /* Detect normal or RACH bursts */
rc = detectAnyBurst(*shvec_ptr, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp); rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
if (rc <= 0) { if (rc <= 0) {
if (rc == -SIGERR_CLIP) { if (rc == -SIGERR_CLIP) {
LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst"; LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst";
@@ -779,13 +728,7 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
goto ret_idle; goto ret_idle;
} }
if (cfg->use_va) { rxBurst = demodAnyBurst(*burst, (CorrType) rc, cfg->rx_sps, &ebp);
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->toa = ebp.toa;
bi->tsc = ebp.tsc; bi->tsc = ebp.tsc;
bi->ci = ebp.ci; bi->ci = ebp.ci;
@@ -959,18 +902,19 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss); sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
} }
} else if (match_cmd(command, "SETMAXDLY", &params)) { } else if (match_cmd(command, "SETMAXDLY", &params)) {
//set expected maximum time-of-arrival for Access Bursts //set expected maximum time-of-arrival
int maxDelay; int maxDelay;
sscanf(params, "%d", &maxDelay); sscanf(params, "%d", &maxDelay);
mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay); sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
} else if (match_cmd(command, "SETMAXDLYNB", &params)) { } else if (match_cmd(command, "SETMAXDLYNB", &params)) {
//set expected maximum time-of-arrival for Normal Bursts //set expected maximum time-of-arrival
int maxDelay; int maxDelay;
sscanf(params, "%d", &maxDelay); sscanf(params, "%d", &maxDelay);
mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay); sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
} else if (match_cmd(command, "SETRXGAIN", &params)) { } else if (match_cmd(command, "SETRXGAIN", &params)) {
//set expected maximum time-of-arrival
int newGain; int newGain;
sscanf(params, "%d", &newGain); sscanf(params, "%d", &newGain);
newGain = mRadioInterface->setRxGain(newGain, chan); newGain = mRadioInterface->setRxGain(newGain, chan);

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -37,27 +37,77 @@ extern "C" {
#include <osmocom/vty/cpu_sched_vty.h> #include <osmocom/vty/cpu_sched_vty.h>
} }
#define SAMPLE_BUF_SZ (1 << 20) #define USRP_TX_AMPL 0.3
#define UMTRX_TX_AMPL 0.7
#define LIMESDR_TX_AMPL 0.3
#define SAMPLE_BUF_SZ (1 << 20)
#define B2XX_TIMING_4_4SPS 6.18462e-5 /*
* 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
#define CHKRET() \ /*
{ \ * UmTRX specific settings
if (status != 0) \ */
LOGC(DDEV, ERROR) << bladerf_strerror(status); \ #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() { \
if(status !=0) \
fprintf(stderr, "%s:%s:%d %s\n", __FILE__,__FUNCTION__, __LINE__, bladerf_strerror(status)); \
} }
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" } }, * 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 power_map_t dev_band_nom_power_param_map{ static const std::map<dev_key, dev_desc> dev_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, 1, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_1SPS, "B200 1 SPS" } },
{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_900), { 89.75, 13.3, -7.5 } }, { std::make_tuple(blade_dev_type::BLADE2, 4, 1), { 1, 26e6, GSMRATE, B2XX_TIMING_4SPS, "B200 4/1 Tx/Rx SPS" } },
{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_1800), { 89.75, 7.5, -11.0 } }, { std::make_tuple(blade_dev_type::BLADE2, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_1900), { 89.75, 7.7, -11.0 } },
}; };
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 {
{ 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 /* 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 * between gain and real output power, so we simply adjust the measured offset
*/ */
@@ -70,16 +120,21 @@ 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); return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
} }
blade_device::blade_device(InterfaceType iface, const struct trx_cfg *cfg) blade_device::blade_device(size_t tx_sps, size_t rx_sps,
: RadioDevice(iface, cfg), band_manager(dev_band_nom_power_param_map, dev_param_map), dev(nullptr), InterfaceType iface, size_t chan_num, double lo_offset,
rx_gain_min(0.0), rx_gain_max(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), drop_cnt(0), const std::vector<std::string>& tx_paths,
prev_ts(0), ts_initial(0), ts_offset(0), async_event_thrd(NULL) 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() blade_device::~blade_device()
{ {
if (dev) { if(dev) {
bladerf_enable_module(dev, BLADERF_CHANNEL_RX(0), false); bladerf_enable_module(dev, BLADERF_CHANNEL_RX(0), false);
bladerf_enable_module(dev, BLADERF_CHANNEL_TX(0), false); bladerf_enable_module(dev, BLADERF_CHANNEL_TX(0), false);
} }
@@ -90,41 +145,82 @@ blade_device::~blade_device()
delete rx_buffers[i]; 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() void blade_device::init_gains()
{ {
double tx_gain_min, tx_gain_max; double tx_gain_min, tx_gain_max;
int status; int status;
const struct bladerf_range *r;
const struct bladerf_range* r;
bladerf_get_gain_range(dev, BLADERF_RX, &r); bladerf_get_gain_range(dev, BLADERF_RX, &r);
rx_gain_min = r->min; rx_gain_min = r->min;
rx_gain_max = r->max; rx_gain_max = r->max;
LOGC(DDEV, INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]"; LOGC(DDEV, INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
for (size_t i = 0; i < rx_gains.size(); i++) { for (size_t i = 0; i < rx_gains.size(); i++) {
double gain = (rx_gain_min + rx_gain_max) / 2; double gain = (rx_gain_min + rx_gain_max) / 2;
status = bladerf_set_gain_mode(dev, BLADERF_CHANNEL_RX(i), BLADERF_GAIN_MGC); status = bladerf_set_gain_mode(dev, BLADERF_CHANNEL_RX(i), BLADERF_GAIN_MGC );
CHKRET() CHKRET()
bladerf_gain_mode m; bladerf_gain_mode m;
bladerf_get_gain_mode(dev, BLADERF_CHANNEL_RX(i), &m); bladerf_get_gain_mode(dev, BLADERF_CHANNEL_RX(i), &m);
LOGC(DDEV, INFO) << (m == BLADERF_GAIN_MANUAL ? "gain manual" : "gain AUTO"); LOGC(DDEV, INFO) << (m == BLADERF_GAIN_MANUAL ? "gain manual" : "gain AUTO") ;
status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0); status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0);//gain);
CHKRET() CHKRET()
int actual_gain; int actual_gain;
status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain); status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain);
CHKRET() CHKRET()
LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain << " scale " LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain << " scale " << r->scale << " actual " << actual_gain;
<< r->scale << " actual " << actual_gain;
rx_gains[i] = actual_gain; rx_gains[i] = actual_gain;
status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0); status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0);//gain);
CHKRET() CHKRET()
status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain); status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain);
CHKRET() CHKRET()
LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain << " scale " LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain << " scale " << r->scale << " actual " << actual_gain;
<< r->scale << " actual " << actual_gain;
rx_gains[i] = actual_gain; rx_gains[i] = actual_gain;
} }
@@ -136,35 +232,40 @@ void blade_device::init_gains()
for (size_t i = 0; i < tx_gains.size(); i++) { for (size_t i = 0; i < tx_gains.size(); i++) {
double gain = (tx_gain_min + tx_gain_max) / 2; double gain = (tx_gain_min + tx_gain_max) / 2;
status = bladerf_set_gain(dev, BLADERF_CHANNEL_TX(i), 30); status = bladerf_set_gain(dev, BLADERF_CHANNEL_TX(i), 30);//gain);
CHKRET() CHKRET()
int actual_gain; int actual_gain;
status = bladerf_get_gain(dev, BLADERF_CHANNEL_TX(i), &actual_gain); status = bladerf_get_gain(dev, BLADERF_CHANNEL_TX(i), &actual_gain);
CHKRET() CHKRET()
LOGC(DDEV, INFO) << "Default setting Tx gain for channel " << i << " to " << gain << " scale " LOGC(DDEV, INFO) << "Default setting Tx gain for channel " << i << " to " << gain << " scale " << r->scale << " actual " << actual_gain;
<< r->scale << " actual " << actual_gain;
tx_gains[i] = actual_gain; tx_gains[i] = actual_gain;
} }
return; return;
} }
void blade_device::set_rates() void blade_device::set_rates()
{ {
struct bladerf_rational_rate rate = { 0, static_cast<uint64_t>((1625e3 * 4)), 6 }, actual; //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); auto status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_RX(0), &rate, &actual);
CHKRET() CHKRET()
status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_TX(0), &rate, &actual); status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_TX(0), &rate, &actual);
CHKRET() CHKRET()
tx_rate = rx_rate = (double)rate.num / (double)rate.den;
tx_rate = rx_rate = (double)rate.num/(double)rate.den;
LOGC(DDEV, INFO) << "Rates set to" << tx_rate << " / " << rx_rate; 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; // FIXME: actual blade offset, should equal b2xx 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;
} }
double blade_device::setRxGain(double db, size_t chan) double blade_device::setRxGain(double db, size_t chan)
@@ -174,7 +275,7 @@ double blade_device::setRxGain(double db, size_t chan)
return 0.0f; return 0.0f;
} }
bladerf_set_gain(dev, BLADERF_CHANNEL_RX(chan), 30); //db); bladerf_set_gain(dev, BLADERF_CHANNEL_RX(chan), 30);//db);
int actual_gain; int actual_gain;
bladerf_get_gain(dev, BLADERF_CHANNEL_RX(chan), &actual_gain); bladerf_get_gain(dev, BLADERF_CHANNEL_RX(chan), &actual_gain);
@@ -210,8 +311,7 @@ double blade_device::rssiOffset(size_t chan)
return rssiOffset; return rssiOffset;
} }
double blade_device::setPowerAttenuation(int atten, size_t chan) double blade_device::setPowerAttenuation(int atten, size_t chan) {
{
double tx_power, db; double tx_power, db;
dev_band_desc desc; dev_band_desc desc;
@@ -224,20 +324,19 @@ double blade_device::setPowerAttenuation(int atten, size_t chan)
tx_power = desc.nom_out_tx_power - atten; tx_power = desc.nom_out_tx_power - atten;
db = TxPower2TxGain(desc, tx_power); db = TxPower2TxGain(desc, tx_power);
bladerf_set_gain(dev, BLADERF_CHANNEL_TX(chan), 30); bladerf_set_gain(dev, BLADERF_CHANNEL_TX(chan), 30);//db);
int actual_gain; int actual_gain;
bladerf_get_gain(dev, BLADERF_CHANNEL_RX(chan), &actual_gain); bladerf_get_gain(dev, BLADERF_CHANNEL_RX(chan), &actual_gain);
tx_gains[chan] = actual_gain; tx_gains[chan] = actual_gain;
LOGC(DDEV, INFO) LOGC(DDEV, INFO) << "Set TX gain to " << tx_gains[chan] << "dB, ~"
<< "Set TX gain to " << tx_gains[chan] << "dB, ~" << TxGain2TxPower(desc, tx_gains[chan]) << " dBm " << TxGain2TxPower(desc, tx_gains[chan]) << " dBm "
<< "(asked for " << db << " dB, ~" << tx_power << " dBm)"; << "(asked for " << db << " dB, ~" << tx_power << " dBm)";
return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]); 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; dev_band_desc desc;
if (chan >= tx_gains.size()) { if (chan >= tx_gains.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
@@ -256,42 +355,45 @@ int blade_device::getNominalTxPower(size_t chan)
return desc.nom_out_tx_power; return desc.nom_out_tx_power;
} }
int blade_device::open()
int blade_device::open(const std::string &args, int ref, bool swap_channels)
{ {
bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_VERBOSE); bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_VERBOSE);
bladerf_set_usb_reset_on_open(true); bladerf_set_usb_reset_on_open(true);
auto success = bladerf_open(&dev, cfg->dev_args); auto success = bladerf_open(&dev, args.c_str());
if (success != 0) { if(success != 0) {
struct bladerf_devinfo *info; struct bladerf_devinfo* info;
auto num_devs = bladerf_get_device_list(&info); auto num_devs = bladerf_get_device_list(&info);
LOGC(DDEV, ALERT) << "No bladerf devices found with identifier '" << cfg->dev_args << "'"; LOGC(DDEV, ALERT) << "No bladerf devices found with identifier '" << args << "'";
if (num_devs) { if(num_devs) {
for (int i = 0; i < num_devs; i++) for(int i=0; i < num_devs; i++)
LOGC(DDEV, ALERT) << "Found device:" << info[i].product << " serial " << info[i].serial; LOGC(DDEV, ALERT) << "Found device:" << info[i].product << " serial " << info[i].serial;
} }
return -1; return -1;
} }
if (strcmp("bladerf2", bladerf_get_board_name(dev))) { if(strcmp("bladerf2", bladerf_get_board_name(dev))) {
LOGC(DDEV, ALERT) << "Only BladeRF2 supported! found:" << bladerf_get_board_name(dev); LOGC(DDEV, ALERT) << "Only BladeRF2 supported! found:" << bladerf_get_board_name(dev) ;
return -1; return -1;
} }
dev_type = blade_dev_type::BLADE2; dev_type = blade_dev_type::BLADE2;
tx_window = TX_WINDOW_FIXED; tx_window = TX_WINDOW_FIXED;
update_band_dev(dev_key(dev_type, tx_sps, rx_sps));
struct bladerf_devinfo info; struct bladerf_devinfo info;
bladerf_get_devinfo(dev, &info); bladerf_get_devinfo(dev, &info);
// Use the first found device
LOGC(DDEV, INFO) << "Using discovered bladerf device " << info.serial; LOGC(DDEV, INFO) << "Using discovered bladerf device " << info.serial;
tx_freqs.resize(chans); tx_freqs.resize(chans);
rx_freqs.resize(chans); rx_freqs.resize(chans);
tx_gains.resize(chans); tx_gains.resize(chans);
rx_gains.resize(chans); rx_gains.resize(chans);
rx_buffers.resize(chans); rx_buffers.resize(chans);
switch (cfg->clock_ref) { switch (ref) {
case REF_INTERNAL: case REF_INTERNAL:
case REF_EXTERNAL: case REF_EXTERNAL:
break; break;
@@ -300,65 +402,74 @@ int blade_device::open()
return -1; return -1;
} }
if (cfg->clock_ref == REF_EXTERNAL) { if(ref == REF_EXTERNAL) {
bool is_locked; bool is_locked;
int status = bladerf_set_pll_enable(dev, true); int status = bladerf_set_pll_enable(dev, true);
CHKRET() CHKRET()
status = bladerf_set_pll_refclk(dev, 10000000); status = bladerf_set_pll_refclk(dev, 10000000);
CHKRET() CHKRET()
for (int i = 0; i < 20; i++) { for(int i=0; i < 20; i++) {
usleep(50 * 1000); usleep(50*1000);
status = bladerf_get_pll_lock_state(dev, &is_locked); status = bladerf_get_pll_lock_state(dev, &is_locked);
CHKRET() CHKRET()
if (is_locked) if(is_locked)
break; break;
} }
if (!is_locked) { if(!is_locked) {
LOGC(DDEV, ALERT) << "unable to lock refclk!"; LOGC(DDEV, ALERT) << "unable to lock refclk!";
return -1; return -1;
} }
} }
LOGC(DDEV, INFO) LOGC(DDEV, INFO) << "Selected clock source is " << ((ref == REF_INTERNAL) ? "internal" : "external 10Mhz");
<< "Selected clock source is " << ((cfg->clock_ref == REF_INTERNAL) ? "internal" : "external 10Mhz");
set_rates(); set_rates();
/* /*
1ts = 3/5200s 1ts = 3/5200s
1024*2 = small gap(~180us) every 9.23ms = every 16 ts? -> every 2 frames 1024*2 = small gap(~180us) every 9.23ms = every 16 ts? -> every 2 frames
1024*1 = large gap(~627us) every 9.23ms = every 16 ts? -> every 2 frames 1024*1 = large gap(~627us) every 9.23ms = every 16 ts? -> every 2 frames
rif convertbuffer = 625*4 = 2500 -> 4 ts rif convertbuffer = 625*4 = 2500 -> 4 ts
rif rxtxbuf = 4 * segment(625*4) = 10000 -> 16 ts rif rxtxbuf = 4 * segment(625*4) = 10000 -> 16 ts
*/ */
const unsigned int num_buffers = 256; const unsigned int num_buffers = 256;
const unsigned int buffer_size = 1024 * 4; /* Must be a multiple of 1024 */ const unsigned int buffer_size = 1024*4; /* Must be a multiple of 1024 */
const unsigned int num_transfers = 32; const unsigned int num_transfers = 32;
const unsigned int timeout_ms = 3500; const unsigned int timeout_ms = 3500;
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,
timeout_ms);
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,
timeout_ms);
/* Number of samples per over-the-wire packet */ /* Number of samples per over-the-wire packet */
tx_spp = rx_spp = buffer_size; tx_spp = rx_spp = buffer_size;
// Create receive buffer
size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t); size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
for (size_t i = 0; i < rx_buffers.size(); i++) for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i] = new smpl_buf(buf_len); 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)); pkt_bufs = std::vector<std::vector<short> >(chans, std::vector<short>(2 * rx_spp));
for (size_t i = 0; i < pkt_bufs.size(); i++) for (size_t i = 0; i < pkt_bufs.size(); i++)
pkt_ptrs.push_back(&pkt_bufs[i].front()); pkt_ptrs.push_back(&pkt_bufs[i].front());
// Initialize and shadow gain values
init_gains(); init_gains();
return NORMAL; return NORMAL;
} }
bool blade_device::restart() bool blade_device::restart()
{ {
/* Allow 100 ms delay to align multi-channel streams */ /* Allow 100 ms delay to align multi-channel streams */
@@ -387,9 +498,11 @@ bool blade_device::start()
return false; return false;
} }
// Start streaming
if (!restart()) if (!restart())
return false; return false;
started = true; started = true;
return true; return true;
} }
@@ -403,13 +516,14 @@ bool blade_device::stop()
for (size_t i = 0; i < rx_buffers.size(); i++) for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i]->reset(); rx_buffers[i]->reset();
band_reset(); band_ass_curr_sess = false;
started = false; started = false;
return true; 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; ssize_t rc;
uint64_t ts; uint64_t ts;
@@ -436,25 +550,33 @@ int blade_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun
return 0; return 0;
} }
struct bladerf_metadata meta = {}; struct bladerf_metadata meta = {};
meta.timestamp = ts; 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) { while (rx_buffers[0]->avail_smpls(timestamp) < len) {
thread_enable_cancel(false); thread_enable_cancel(false);
int status = bladerf_sync_rx(dev, pkt_ptrs[0], len, &meta, 200U); int status = bladerf_sync_rx(dev, pkt_ptrs[0], len, &meta, 200U);
thread_enable_cancel(true); thread_enable_cancel(true);
if (status != 0) if(status != 0)
LOGC(DDEV, ERROR) << "RX broken: " << bladerf_strerror(status); std::cerr << "RX fucked: " << bladerf_strerror(status);
if (meta.flags & BLADERF_META_STATUS_OVERRUN) if(meta.flags & BLADERF_META_STATUS_OVERRUN )
LOGC(DDEV, ERROR) << "RX borken, OVERRUN: " << bladerf_strerror(status); std::cerr << "RX fucked OVER: " << bladerf_strerror(status);
size_t num_smpls = meta.actual_count; size_t num_smpls = meta.actual_count;
; ; ts = meta.timestamp;
ts = meta.timestamp;
for (size_t i = 0; i < rx_buffers.size(); i++) { 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 // Continue on local overrun, exit on other errors
if ((rc < 0)) { if ((rc < 0)) {
@@ -465,9 +587,10 @@ int blade_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun
} }
} }
meta = {}; meta = {};
meta.timestamp = ts + num_smpls; meta.timestamp = ts+num_smpls;
} }
// We have enough samples
for (size_t i = 0; i < rx_buffers.size(); i++) { for (size_t i = 0; i < rx_buffers.size(); i++) {
rc = rx_buffers[i]->read(bufs[i], len, timestamp); rc = rx_buffers[i]->read(bufs[i], len, timestamp);
if ((rc < 0) || (rc != len)) { if ((rc < 0) || (rc != len)) {
@@ -480,23 +603,27 @@ int blade_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun
return len; 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; *underrun = false;
static bool first_tx = true; static bool first_tx = true;
struct bladerf_metadata meta = {}; struct bladerf_metadata meta = {};
if (first_tx) { if(first_tx) {
meta.timestamp = timestamp; meta.timestamp = timestamp;
meta.flags = BLADERF_META_FLAG_TX_BURST_START; meta.flags = BLADERF_META_FLAG_TX_BURST_START;
first_tx = false; first_tx = false;
} }
thread_enable_cancel(false); thread_enable_cancel(false);
int status = bladerf_sync_tx(dev, (const void *)bufs[0], len, &meta, 200U); 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); thread_enable_cancel(true);
if (status != 0) if(status != 0)
LOGC(DDEV, ERROR) << "TX broken: " << bladerf_strerror(status); std::cerr << "TX fucked: " << bladerf_strerror(status);
// LOGCHAN(0, DDEV, INFO) << "tx " << timestamp << " " << len << " t+l: "<< timestamp+len << std::endl;
return len; return len;
} }
@@ -508,31 +635,47 @@ bool blade_device::updateAlignment(TIMESTAMP timestamp)
bool blade_device::set_freq(double freq, size_t chan, bool tx) bool blade_device::set_freq(double freq, size_t chan, bool tx)
{ {
if (tx) { if (tx) {
bladerf_set_frequency(dev, BLADERF_CHANNEL_TX(chan), freq); bladerf_set_frequency(dev, BLADERF_CHANNEL_TX(chan), freq);
bladerf_frequency f; bladerf_frequency f;
bladerf_get_frequency(dev, BLADERF_CHANNEL_TX(chan), &f); bladerf_get_frequency(dev,BLADERF_CHANNEL_TX(chan), &f);
tx_freqs[chan] = f; tx_freqs[chan] = f;
} else { } else {
bladerf_set_frequency(dev, BLADERF_CHANNEL_RX(chan), freq); bladerf_set_frequency(dev, BLADERF_CHANNEL_RX(chan), freq);
bladerf_frequency f; bladerf_frequency f;
bladerf_get_frequency(dev, BLADERF_CHANNEL_RX(chan), &f); bladerf_get_frequency(dev,BLADERF_CHANNEL_RX(chan), &f);
rx_freqs[chan] = f; rx_freqs[chan] = f;
} }
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << (tx ? "TX" : "RX") << "): " << std::endl; LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << (tx ? "TX" : "RX") << "): " << std::endl;
return true; return true;
} }
bool blade_device::setTxFreq(double wFreq, size_t chan) bool blade_device::setTxFreq(double wFreq, size_t chan)
{ {
uint16_t req_arfcn;
enum gsm_band req_band;
if (chan >= tx_freqs.size()) { if (chan >= tx_freqs.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false; return false;
} }
ScopedLock lock(tune_lock); ScopedLock lock(tune_lock);
if (!update_band_from_freq(wFreq, chan, true)) 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; return false;
if (!set_freq(wFreq, chan, true)) if (!set_freq(wFreq, chan, true))
@@ -543,13 +686,27 @@ bool blade_device::setTxFreq(double wFreq, size_t chan)
bool blade_device::setRxFreq(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()) { if (chan >= rx_freqs.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false; return false;
} }
ScopedLock lock(tune_lock); ScopedLock lock(tune_lock);
if (!update_band_from_freq(wFreq, chan, false)) 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 false;
return set_freq(wFreq, chan, false); return set_freq(wFreq, chan, false);
@@ -580,9 +737,12 @@ bool blade_device::requiresRadioAlign()
return false; return false;
} }
GSM::Time blade_device::minLatency() GSM::Time blade_device::minLatency() {
{ /* Empirical data from a handful of
return GSM::Time(6, 7); 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. */
return GSM::Time(6,7);
} }
TIMESTAMP blade_device::initialWriteTimestamp() TIMESTAMP blade_device::initialWriteTimestamp()
@@ -597,15 +757,21 @@ TIMESTAMP blade_device::initialReadTimestamp()
double blade_device::fullScaleInputValue() double blade_device::fullScaleInputValue()
{ {
return (double)2047; return (double) 2047;
} }
double blade_device::fullScaleOutputValue() double blade_device::fullScaleOutputValue()
{ {
return (double)2047; return (double) 2047;
} }
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
#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)
{ {
return new blade_device(type, cfg); return new blade_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
} }
#endif

View File

@@ -22,12 +22,10 @@
#pragma once #pragma once
#include <map>
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
#endif #endif
#include "bandmanager.h"
#include "radioDevice.h" #include "radioDevice.h"
#include "smpl_buf.h" #include "smpl_buf.h"
@@ -35,13 +33,18 @@ extern "C" {
#include <osmocom/gsm/gsm_utils.h> #include <osmocom/gsm/gsm_utils.h>
} }
enum class blade_dev_type { BLADE1, BLADE2 }; #include <bladerf.h>
enum class blade_dev_type {
BLADE1,
BLADE2
};
struct dev_band_desc { struct dev_band_desc {
/* Maximum UHD Tx Gain which can be set/used without distorting the /* Maximum UHD Tx Gain which can be set/used without distorting the
output signal, and the resulting real output power measured when that output signal, and the resulting real output power measured when that
gain is used. Correct measured values only provided for B210 so far. */ gain is used. Correct measured values only provided for B210 so far. */
double nom_uhd_tx_gain; /* dB */ double nom_uhd_tx_gain; /* dB */
double nom_out_tx_power; /* dBm */ double nom_out_tx_power; /* dBm */
/* Factor used to infer base real RSSI offset on the Rx path based on current /* Factor used to infer base real RSSI offset on the Rx path based on current
configured RxGain. The resulting rssiOffset is added to the per burst configured RxGain. The resulting rssiOffset is added to the per burst
@@ -52,37 +55,25 @@ struct dev_band_desc {
double rxgain2rssioffset_rel; /* dB */ double rxgain2rssioffset_rel; /* dB */
}; };
/* Device parameter descriptor */ class blade_device : public RadioDevice {
struct dev_desc { public:
unsigned channels; blade_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
double mcr; size_t chan_num, double offset,
double rate; const std::vector<std::string>& tx_paths,
double offset; const std::vector<std::string>& rx_paths);
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(InterfaceType iface, const struct trx_cfg *cfg);
~blade_device(); ~blade_device();
int open(); int open(const std::string &args, int ref, bool swap_channels);
bool start(); bool start();
bool stop(); bool stop();
bool restart(); bool restart();
enum TxWindowType getWindowType() enum TxWindowType getWindowType() { return tx_window; }
{
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); bool updateAlignment(TIMESTAMP timestamp);
@@ -97,14 +88,8 @@ class blade_device : public RadioDevice, public band_manager<power_map_t, dev_ma
double setRxGain(double db, size_t chan); double setRxGain(double db, size_t chan);
double getRxGain(size_t chan); double getRxGain(size_t chan);
double maxRxGain(void) double maxRxGain(void) { return rx_gain_max; }
{ double minRxGain(void) { return rx_gain_min; }
return rx_gain_max;
}
double minRxGain(void)
{
return rx_gain_min;
}
double rssiOffset(size_t chan); double rssiOffset(size_t chan);
double setPowerAttenuation(int atten, size_t chan); double setPowerAttenuation(int atten, size_t chan);
@@ -116,31 +101,16 @@ class blade_device : public RadioDevice, public band_manager<power_map_t, dev_ma
double getRxFreq(size_t chan); double getRxFreq(size_t chan);
double getRxFreq(); double getRxFreq();
bool setRxAntenna(const std::string &ant, size_t chan) bool setRxAntenna(const std::string &ant, size_t chan) { return {};};
{ std::string getRxAntenna(size_t chan) { return {};};
return {}; bool setTxAntenna(const std::string &ant, size_t chan) { return {};};
}; std::string getTxAntenna(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(); bool requiresRadioAlign();
GSM::Time minLatency(); GSM::Time minLatency();
inline double getSampleRate() inline double getSampleRate() { return tx_rate; }
{
return tx_rate;
}
/** Receive and process asynchronous message /** Receive and process asynchronous message
@return true if message received or false on timeout or error @return true if message received or false on timeout or error
@@ -154,9 +124,9 @@ class blade_device : public RadioDevice, public band_manager<power_map_t, dev_ma
ERROR_UNHANDLED = -4, ERROR_UNHANDLED = -4,
}; };
protected: protected:
struct bladerf *dev; struct bladerf* dev;
void *usrp_dev; void* usrp_dev;
enum TxWindowType tx_window; enum TxWindowType tx_window;
enum blade_dev_type dev_type; enum blade_dev_type dev_type;
@@ -167,6 +137,9 @@ class blade_device : public RadioDevice, public band_manager<power_map_t, dev_ma
std::vector<double> tx_gains, rx_gains; std::vector<double> tx_gains, rx_gains;
std::vector<double> tx_freqs, rx_freqs; 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; size_t tx_spp, rx_spp;
bool started; bool started;
@@ -189,6 +162,9 @@ class blade_device : public RadioDevice, public band_manager<power_map_t, dev_ma
bool flush_recv(size_t num_pkts); bool flush_recv(size_t num_pkts);
bool set_freq(double freq, 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; Thread *async_event_thrd;
Mutex tune_lock; Mutex tune_lock;

View File

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

View File

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

View File

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

View File

@@ -115,11 +115,12 @@ class IPCDevice : public RadioDevice {
int ipc_chan_sock_write(osmo_fd *bfd); int ipc_chan_sock_write(osmo_fd *bfd);
/** Object constructor */ /** Object constructor */
IPCDevice(InterfaceType iface, const struct trx_cfg *cfg); 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);
virtual ~IPCDevice() override; virtual ~IPCDevice() override;
/** Instantiate the IPC */ /** Instantiate the IPC */
virtual int open() override; virtual int open(const std::string &args, int ref, bool swap_channels) override;
/** Start the IPC */ /** Start the IPC */
virtual bool start() override; virtual bool start() override;
@@ -198,7 +199,7 @@ class IPCDevice : public RadioDevice {
virtual double minRxGain(void) override; virtual double minRxGain(void) override;
/* FIXME: return rx_gains[chan] ? receive factor from IPC Driver? */ /* FIXME: return rx_gains[chan] ? receive factor from IPC Driver? */
double rssiOffset(size_t chan) override { return 0.0f; }; double rssiOffset(size_t chan) { return 0.0f; };
double setPowerAttenuation(int atten, size_t chan) override; double setPowerAttenuation(int atten, size_t chan) override;
double getPowerAttenuation(size_t chan = 0) override; double getPowerAttenuation(size_t chan = 0) override;

View File

@@ -1,8 +1,9 @@
include $(top_srcdir)/Makefile.common include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(UHD_CFLAGS) AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(UHD_CFLAGS) AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
AM_LDFLAGS = -lpthread -lrt
noinst_HEADERS = IPCDevice.h shm.h ipc_shm.h ipc_chan.h ipc_sock.h noinst_HEADERS = IPCDevice.h shm.h ipc_shm.h ipc_chan.h ipc_sock.h
@@ -13,22 +14,30 @@ endif
noinst_LTLIBRARIES = libdevice.la noinst_LTLIBRARIES = libdevice.la
libdevice_la_SOURCES = IPCDevice.cpp shm.c ipc_shm.c ipc_chan.c ipc_sock.c libdevice_la_SOURCES = IPCDevice.cpp shm.c ipc_shm.c ipc_chan.c ipc_sock.c
libdevice_la_CPPFLAGS = $(AM_CPPFLAGS) -DIPCMAGIC libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
libdevice_la_LIBADD = \ libdevice_la_CXXFLAGS = $(AM_CXXFLAGS) -DIPCMAGIC
$(top_builddir)/Transceiver52M/device/common/libdevice_common.la \
-lpthread \ #work around distclean issue on older autotools vers:
-lrt \ #a direct build of ../uhd/UHDDevice.cpp tries to clean
$(NULL) #../uhd/.dep/UHDDevice.Plo twice and fails
uhddev_ipc.cpp:
echo "#include \"../uhd/UHDDevice.cpp\"" >$@
CLEANFILES= uhddev_ipc.cpp
BUILT_SOURCES = uhddev_ipc.cpp
if DEVICE_UHD if DEVICE_UHD
bin_PROGRAMS = ipc-driver-test bin_PROGRAMS = ipc-driver-test
#ipc_driver_test_SHORTNAME = drvt #ipc_driver_test_SHORTNAME = drvt
ipc_driver_test_SOURCES = ipc-driver-test.c uhdwrap.cpp ../uhd/UHDDevice.cpp 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_LDADD = \ ipc_driver_test_LDADD = \
libdevice.la \ shm.lo \
$(COMMON_LA) \ $(top_builddir)/Transceiver52M/device/common/libdevice_common.la \
$(COMMON_LA)
$(LIBOSMOCORE_LIBS) \ $(LIBOSMOCORE_LIBS) \
$(UHD_LIBS) \
$(NULL) $(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 endif

View File

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

View File

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

View File

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

View File

@@ -0,0 +1,314 @@
/*
* 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

@@ -0,0 +1,186 @@
/*
* 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

@@ -0,0 +1,14 @@
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

@@ -0,0 +1,161 @@
/*
* (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

@@ -0,0 +1,219 @@
/*
* (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

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

View File

@@ -0,0 +1,255 @@
/*
* 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

@@ -0,0 +1,83 @@
/*
* 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,16 +52,39 @@ extern "C" {
#define LMS_DEV_SDR_MINI_PREFIX_NAME "LimeSDR-Mini" #define LMS_DEV_SDR_MINI_PREFIX_NAME "LimeSDR-Mini"
#define LMS_DEV_NET_MICRO_PREFIX_NAME "LimeNET-Micro" #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_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_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_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" } }, { LMS_DEV_UNKNOWN, { true, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, "UNKNOWN" } },
}; };
static const power_map_t dev_band_nom_power_param_map { 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 {
{ 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_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_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) */ { 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) */
@@ -99,8 +122,8 @@ static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
enum lms_dev_type dev_type = it->first; enum lms_dev_type dev_type = it->first;
struct dev_desc desc = it->second; struct dev_desc desc = it->second;
if (strncmp(device_info->deviceName, desc.desc_str.c_str(), desc.desc_str.length()) == 0) { if (strncmp(device_info->deviceName, desc.name_prefix.c_str(), desc.name_prefix.length()) == 0) {
LOGC(DDEV, INFO) << "Device identified as " << desc.desc_str; LOGC(DDEV, INFO) << "Device identified as " << desc.name_prefix;
return dev_type; return dev_type;
} }
it++; it++;
@@ -108,10 +131,12 @@ static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
return LMS_DEV_UNKNOWN; return LMS_DEV_UNKNOWN;
} }
LMSDevice::LMSDevice(InterfaceType iface, const struct trx_cfg *cfg) LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
: RadioDevice(iface, cfg), const std::vector<std::string>& tx_paths,
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), const std::vector<std::string>& rx_paths):
started(false), m_dev_type(LMS_DEV_UNKNOWN) 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)
{ {
LOGC(DDEV, INFO) << "creating LMS device..."; LOGC(DDEV, INFO) << "creating LMS device...";
@@ -198,7 +223,48 @@ int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::str
return -1; return -1;
} }
int LMSDevice::open() 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)
{ {
lms_info_str_t* info_list; lms_info_str_t* info_list;
lms_range_t range_sr; lms_range_t range_sr;
@@ -226,9 +292,9 @@ int LMSDevice::open()
for (i = 0; i < n; i++) for (i = 0; i < n; i++)
LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i]; LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i];
dev_id = info_list_find(info_list, n, cfg->dev_args); dev_id = info_list_find(info_list, n, args);
if (dev_id == -1) { if (dev_id == -1) {
LOGC(DDEV, ERROR) << "No LMS device found with address '" << cfg->dev_args << "'"; LOGC(DDEV, ERROR) << "No LMS device found with address '" << args << "'";
delete[] info_list; delete[] info_list;
return -1; return -1;
} }
@@ -245,15 +311,14 @@ int LMSDevice::open()
m_dev_type = parse_dev_type(m_lms_dev); m_dev_type = parse_dev_type(m_lms_dev);
dev_desc = dev_param_map.at(m_dev_type); dev_desc = dev_param_map.at(m_dev_type);
update_band_dev(m_dev_type);
if ((cfg->clock_ref != REF_EXTERNAL) && (cfg->clock_ref != REF_INTERNAL)) { if ((ref != REF_EXTERNAL) && (ref != REF_INTERNAL)){
LOGC(DDEV, ERROR) << "Invalid reference type"; LOGC(DDEV, ERROR) << "Invalid reference type";
goto out_close; goto out_close;
} }
/* if reference clock is external, setup must happen _before_ calling LMS_Init */ /* if reference clock is external, setup must happen _before_ calling LMS_Init */
if (cfg->clock_ref == REF_EXTERNAL) { if (ref == REF_EXTERNAL) {
LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz"; LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz";
/* FIXME: Assume an external 10 MHz reference clock. make /* FIXME: Assume an external 10 MHz reference clock. make
external reference frequency configurable */ external reference frequency configurable */
@@ -268,7 +333,7 @@ int LMSDevice::open()
} }
/* if reference clock is internal, setup must happen _after_ calling LMS_Init */ /* if reference clock is internal, setup must happen _after_ calling LMS_Init */
if (cfg->clock_ref == REF_INTERNAL) { if (ref == REF_INTERNAL) {
LOGC(DDEV, INFO) << "Setting Internal clock reference"; LOGC(DDEV, INFO) << "Setting Internal clock reference";
/* Internal freq param is not used */ /* Internal freq param is not used */
if (!do_clock_src_freq(REF_INTERNAL, 0)) if (!do_clock_src_freq(REF_INTERNAL, 0))
@@ -404,7 +469,7 @@ bool LMSDevice::stop()
LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]); LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]);
} }
band_reset(); band_ass_curr_sess = false;
started = false; started = false;
return true; return true;
@@ -421,8 +486,8 @@ bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
break; break;
case REF_INTERNAL: case REF_INTERNAL:
if (!dev_desc.clock_src_int_usable) { if (!dev_desc.clock_src_int_usable) {
LOGC(DDEV, ERROR) LOGC(DDEV, ERROR) << "Device type " << dev_desc.name_prefix
<< "Device type " << dev_desc.desc_str << " doesn't support internal reference clock"; << " doesn't support internal reference clock";
return false; return false;
} }
/* According to lms using LMS_CLOCK_EXTREF with a /* According to lms using LMS_CLOCK_EXTREF with a
@@ -440,8 +505,8 @@ bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
if (LMS_SetClockFreq(m_lms_dev, lms_clk_id, freq) < 0) if (LMS_SetClockFreq(m_lms_dev, lms_clk_id, freq) < 0)
return false; return false;
} else { } else {
LOGC(DDEV, INFO) LOGC(DDEV, INFO) << "Device type " << dev_desc.name_prefix
<< "Device type " << dev_desc.desc_str << " doesn't support switching clock source through SW"; << " doesn't support switching clock source through SW";
} }
return true; return true;
@@ -934,6 +999,9 @@ bool LMSDevice::updateAlignment(TIMESTAMP timestamp)
bool LMSDevice::setTxFreq(double wFreq, size_t chan) bool LMSDevice::setTxFreq(double wFreq, size_t chan)
{ {
uint16_t req_arfcn;
enum gsm_band req_band;
if (chan >= chans) { if (chan >= chans) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false; return false;
@@ -941,7 +1009,18 @@ bool LMSDevice::setTxFreq(double wFreq, size_t chan)
LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz"; LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz";
if (!update_band_from_freq(wFreq, chan, true)) 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; return false;
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) { if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
@@ -954,9 +1033,23 @@ bool LMSDevice::setTxFreq(double wFreq, size_t chan)
bool LMSDevice::setRxFreq(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"; LOGCHAN(chan, DDEV, NOTICE) << "Setting Rx Freq to " << wFreq << " Hz";
if (!update_band_from_freq(wFreq, chan, false)) 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 false;
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) { if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) {
@@ -967,15 +1060,18 @@ bool LMSDevice::setRxFreq(double wFreq, size_t chan)
return true; return true;
} }
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg) 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 (cfg->tx_sps != cfg->rx_sps) { if (tx_sps != rx_sps) {
LOGC(DDEV, ERROR) << "LMS requires tx_sps == rx_sps"; LOGC(DDEV, ERROR) << "LMS Requires tx_sps == rx_sps";
return NULL; return NULL;
} }
if (cfg->offset != 0.0) { if (lo_offset != 0.0) {
LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset"; LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset";
return NULL; return NULL;
} }
return new LMSDevice(type, cfg); return new LMSDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
} }

View File

@@ -18,13 +18,11 @@
#ifndef _LMS_DEVICE_H_ #ifndef _LMS_DEVICE_H_
#define _LMS_DEVICE_H_ #define _LMS_DEVICE_H_
#include <map>
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include "config.h" #include "config.h"
#endif #endif
#include "radioDevice.h" #include "radioDevice.h"
#include "bandmanager.h"
#include "smpl_buf.h" #include "smpl_buf.h"
#include <sys/time.h> #include <sys/time.h>
@@ -71,35 +69,8 @@ struct dev_band_desc {
double rxgain2rssioffset_rel; /* dB */ 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 */ /** A class to handle a LimeSuite supported device */
class LMSDevice:public RadioDevice, public band_manager<power_map_t, dev_map_t> { class LMSDevice:public RadioDevice {
private: private:
lms_device_t *m_lms_dev; lms_device_t *m_lms_dev;
@@ -116,6 +87,9 @@ private:
TIMESTAMP ts_initial, ts_offset; TIMESTAMP ts_initial, ts_offset;
std::vector<double> tx_gains, rx_gains; 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; enum lms_dev_type m_dev_type;
@@ -127,25 +101,29 @@ private:
void update_stream_stats_rx(size_t chan, bool *overrun); void update_stream_stats_rx(size_t chan, bool *overrun);
void update_stream_stats_tx(size_t chan, bool *underrun); void update_stream_stats_tx(size_t chan, bool *underrun);
bool do_clock_src_freq(enum ReferenceType ref, double freq); 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: public:
/** Object constructor */ /** Object constructor */
LMSDevice(InterfaceType iface, const struct trx_cfg *cfg); LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
~LMSDevice(); const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
~LMSDevice();
/** Instantiate the LMS */ /** Instantiate the LMS */
int open(); int open(const std::string &args, int ref, bool swap_channels);
/** Start the LMS */ /** Start the LMS */
bool start(); bool start();
/** Stop the LMS */ /** Stop the LMS */
bool stop(); bool stop();
enum TxWindowType getWindowType() enum TxWindowType getWindowType() {
{ return TX_WINDOW_LMS1;
return TX_WINDOW_LMS1; }
}
/** /**
Read samples from the LMS. Read samples from the LMS.

View File

@@ -91,7 +91,19 @@ extern "C" {
* USRP1 with timestamps is not supported by UHD. * USRP1 with timestamps is not supported by UHD.
*/ */
static const dev_map_t dev_param_map { /* 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 {
{ std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } }, { 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, 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" } }, { std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } },
@@ -117,7 +129,9 @@ static const dev_map_t 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" } }, { std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
}; };
static const power_map_t dev_band_nom_power_param_map { 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 {
{ std::make_tuple(B200, GSM_BAND_850), { 89.75, 13.3, -7.5 } }, { 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_900), { 89.75, 13.3, -7.5 } },
{ std::make_tuple(B200, GSM_BAND_1800), { 89.75, 7.5, -11.0 } }, { std::make_tuple(B200, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
@@ -206,10 +220,15 @@ 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); return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
} }
uhd_device::uhd_device(InterfaceType iface, const struct trx_cfg *cfg) uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
: RadioDevice(iface, cfg), band_manager(dev_band_nom_power_param_map, dev_param_map), rx_gain_min(0.0), InterfaceType iface, size_t chan_num, double lo_offset,
rx_gain_max(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), drop_cnt(0), prev_ts(0, 0), const std::vector<std::string>& tx_paths,
ts_initial(0), ts_offset(0), async_event_thrd(NULL) 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)
{ {
} }
@@ -221,6 +240,47 @@ uhd_device::~uhd_device()
delete rx_buffers[i]; 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() void uhd_device::init_gains()
{ {
double tx_gain_min, tx_gain_max; double tx_gain_min, tx_gain_max;
@@ -285,7 +345,7 @@ void uhd_device::set_rates()
rx_rate = usrp_dev->get_rx_rate(); rx_rate = usrp_dev->get_rx_rate();
ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate); ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
LOGC(DDEV, INFO) << "Rates configured for " << desc.desc_str; LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
} }
double uhd_device::setRxGain(double db, size_t chan) double uhd_device::setRxGain(double db, size_t chan)
@@ -295,9 +355,6 @@ double uhd_device::setRxGain(double db, size_t chan)
return 0.0f; return 0.0f;
} }
if (cfg->overrides.ul_gain_override)
return rx_gains[chan];
usrp_dev->set_rx_gain(db, chan); usrp_dev->set_rx_gain(db, chan);
rx_gains[chan] = usrp_dev->get_rx_gain(chan); rx_gains[chan] = usrp_dev->get_rx_gain(chan);
@@ -340,9 +397,6 @@ double uhd_device::setPowerAttenuation(int atten, size_t chan) {
return 0.0f; return 0.0f;
} }
if (cfg->overrides.dl_gain_override)
return atten; // ensures caller does not apply digital attenuation
get_dev_band_desc(desc); get_dev_band_desc(desc);
tx_power = desc.nom_out_tx_power - atten; tx_power = desc.nom_out_tx_power - atten;
db = TxPower2TxGain(desc, tx_power); db = TxPower2TxGain(desc, tx_power);
@@ -494,7 +548,7 @@ void uhd_device::set_channels(bool swap)
} }
} }
int uhd_device::open() int uhd_device::open(const std::string &args, int ref, bool swap_channels)
{ {
const char *refstr; const char *refstr;
int clock_lock_attempts = 15; int clock_lock_attempts = 15;
@@ -510,10 +564,10 @@ int uhd_device::open()
#endif #endif
// Find UHD devices // Find UHD devices
uhd::device_addr_t addr(cfg->dev_args); uhd::device_addr_t addr(args);
uhd::device_addrs_t dev_addrs = uhd::device::find(addr); uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
if (dev_addrs.size() == 0) { if (dev_addrs.size() == 0) {
LOGC(DDEV, ALERT) << "No UHD devices found with address '" << cfg->dev_args << "'"; LOGC(DDEV, ALERT) << "No UHD devices found with address '" << args << "'";
return -1; return -1;
} }
@@ -522,7 +576,7 @@ int uhd_device::open()
try { try {
usrp_dev = uhd::usrp::multi_usrp::make(addr); usrp_dev = uhd::usrp::multi_usrp::make(addr);
} catch(uhd::key_error::exception &e) { } catch(uhd::key_error::exception &e) {
LOGC(DDEV, ALERT) << "UHD make failed, device " << cfg->dev_args << ", exception:\n" << e.what(); LOGC(DDEV, ALERT) << "UHD make failed, device " << args << ", exception:\n" << e.what();
return -1; return -1;
} }
@@ -530,16 +584,14 @@ int uhd_device::open()
if (!parse_dev_type()) if (!parse_dev_type())
return -1; return -1;
update_band_dev(dev_key(dev_type, tx_sps, rx_sps));
if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) { if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater"; LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater";
return -1; return -1;
} }
try { try {
set_channels(cfg->swap_channels); set_channels(swap_channels);
} catch (const std::exception &e) { } catch (const std::exception &e) {
LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what(); LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
return -1; return -1;
} }
@@ -555,7 +607,7 @@ int uhd_device::open()
rx_gains.resize(chans); rx_gains.resize(chans);
rx_buffers.resize(chans); rx_buffers.resize(chans);
switch (cfg->clock_ref) { switch (ref) {
case REF_INTERNAL: case REF_INTERNAL:
refstr = "internal"; refstr = "internal";
break; break;
@@ -634,32 +686,6 @@ int uhd_device::open()
// Print configuration // Print configuration
LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string(); 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) if (iface == MULTI_ARFCN)
return MULTI_ARFCN; return MULTI_ARFCN;
@@ -770,7 +796,7 @@ bool uhd_device::stop()
for (size_t i = 0; i < rx_buffers.size(); i++) for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i]->reset(); rx_buffers[i]->reset();
band_reset(); band_ass_curr_sess = false;
started = false; started = false;
return true; return true;
@@ -1010,22 +1036,17 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
{ {
std::vector<double> freqs; std::vector<double> freqs;
uhd::tune_result_t tres; 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); uhd::tune_request_t treq = select_freq(freq, chan, tx);
std::string str_dir;
if (tx) { if (tx) {
tres = usrp_dev->set_tx_freq(treq, chan); tres = usrp_dev->set_tx_freq(treq, chan);
tx_freqs[chan] = usrp_dev->get_tx_freq(chan); tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
str_dir = "Tx";
} else { } else {
tres = usrp_dev->set_rx_freq(treq, chan); tres = usrp_dev->set_rx_freq(treq, chan);
rx_freqs[chan] = usrp_dev->get_rx_freq(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; LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
@@ -1055,20 +1076,59 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
bool uhd_device::setTxFreq(double wFreq, size_t chan) bool uhd_device::setTxFreq(double wFreq, size_t chan)
{ {
uint16_t req_arfcn;
enum gsm_band req_band;
if (chan >= tx_freqs.size()) { if (chan >= tx_freqs.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false; return false;
} }
ScopedLock lock(tune_lock);
return set_freq(wFreq, chan, true); 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;
} }
bool uhd_device::setRxFreq(double wFreq, size_t chan) bool uhd_device::setRxFreq(double wFreq, size_t chan)
{ {
uint16_t req_arfcn;
enum gsm_band req_band;
if (chan >= rx_freqs.size()) { if (chan >= rx_freqs.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false; 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); return set_freq(wFreq, chan, false);
} }
@@ -1318,8 +1378,11 @@ std::string uhd_device::str_code(uhd::async_metadata_t metadata)
} }
#ifndef IPCMAGIC #ifndef IPCMAGIC
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg) 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)
{ {
return new uhd_device(type, cfg); return new uhd_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
} }
#endif #endif

View File

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

View File

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

View File

@@ -104,21 +104,20 @@ private:
public: public:
/** Object constructor */ /** Object constructor */
USRPDevice(InterfaceType iface, const struct trx_cfg *cfg); 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);
/** Instantiate the USRP */ /** Instantiate the USRP */
int open(); int open(const std::string &, int, bool);
/** Start the USRP */ /** Start the USRP */
bool start(); bool start();
/** Stop the USRP */ /** Stop the USRP */
bool stop(); bool stop();
enum TxWindowType getWindowType() enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
{
return TX_WINDOW_USRP1;
}
/** /**
Read samples from the USRP. Read samples from the USRP.

View File

@@ -1,149 +1,121 @@
#pragma once #pragma once
/* -*- c++ -*- */
/* #include <complex>
* @file
* @author (C) 2009-2017 by Piotr Krysik <ptrkrysik@gmail.com> #define gr_complex std::complex<float>
* @section LICENSE
*
* Gr-gsm is free software; you can redistribute it and/or modify #define GSM_SYMBOL_RATE (1625000.0/6.0) //symbols per second
* it under the terms of the GNU General Public License as published by #define GSM_SYMBOL_PERIOD (1.0/GSM_SYMBOL_RATE) //seconds per symbol
* the Free Software Foundation; either version 3, or (at your option)
* any later version. //Burst timing
* #define TAIL_BITS 3
* Gr-gsm is distributed in the hope that it will be useful, #define GUARD_BITS 8
* but WITHOUT ANY WARRANTY; without even the implied warranty of #define GUARD_FRACTIONAL 0.25 //fractional part of guard period
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #define GUARD_PERIOD GUARD_BITS + GUARD_FRACTIONAL
* GNU General Public License for more details. #define DATA_BITS 57 //size of 1 data block in normal burst
* #define STEALING_BIT 1
* You should have received a copy of the GNU General Public License #define N_TRAIN_BITS 26
* along with gr-gsm; see the file COPYING. If not, write to #define N_SYNC_BITS 64
* the Free Software Foundation, Inc., 51 Franklin Street, #define USEFUL_BITS 142 //(2*(DATA_BITS+STEALING_BIT) + N_TRAIN_BITS )
* Boston, MA 02110-1301, USA. #define FCCH_BITS USEFUL_BITS
*/ #define BURST_SIZE (USEFUL_BITS+2*TAIL_BITS)
#define ACCESS_BURST_SIZE 88
#include <complex> #define PROCESSED_CHUNK BURST_SIZE+2*GUARD_PERIOD
#define gr_complex std::complex<float> #define SCH_DATA_LEN 39
#define TS_BITS (TAIL_BITS+USEFUL_BITS+TAIL_BITS+GUARD_BITS) //a full TS (156 bits)
#define TS_PER_FRAME 8
#define GSM_SYMBOL_RATE (1625000.0/6.0) //symbols per second #define FRAME_BITS (TS_PER_FRAME * TS_BITS + 2) // 156.25 * 8
#define GSM_SYMBOL_PERIOD (1.0/GSM_SYMBOL_RATE) //seconds per symbol #define FCCH_POS TAIL_BITS
#define SYNC_POS (TAIL_BITS + 39)
//Burst timing #define TRAIN_POS ( TAIL_BITS + (DATA_BITS+STEALING_BIT) + 5) //first 5 bits of a training sequence
#define TAIL_BITS 3 //aren't used for channel impulse response estimation
#define GUARD_BITS 8 #define TRAIN_BEGINNING 5
#define GUARD_FRACTIONAL 0.25 //fractional part of guard period #define SAFETY_MARGIN 6 //
#define GUARD_PERIOD GUARD_BITS + GUARD_FRACTIONAL
#define DATA_BITS 57 //size of 1 data block in normal burst #define FCCH_HITS_NEEDED (USEFUL_BITS - 4)
#define STEALING_BIT 1 #define FCCH_MAX_MISSES 1
#define N_TRAIN_BITS 26 #define FCCH_MAX_FREQ_OFFSET 100
#define N_SYNC_BITS 64
#define N_ACCESS_BITS 41 #define CHAN_IMP_RESP_LENGTH 5
#define USEFUL_BITS 142 //(2*(DATA_BITS+STEALING_BIT) + N_TRAIN_BITS )
#define FCCH_BITS USEFUL_BITS #define MAX_SCH_ERRORS 10 //maximum number of subsequent sch errors after which gsm receiver goes to find_next_fcch state
#define BURST_SIZE (USEFUL_BITS+2*TAIL_BITS)
#define ACCESS_BURST_SIZE 88 typedef enum { empty, fcch_burst, sch_burst, normal_burst, rach_burst, dummy, dummy_or_normal, normal_or_noise } burst_type;
#define PROCESSED_CHUNK BURST_SIZE+2*GUARD_PERIOD typedef enum { unknown, multiframe_26, multiframe_51 } multiframe_type;
#define SCH_DATA_LEN 39 static const unsigned char SYNC_BITS[] = {
#define TS_BITS (TAIL_BITS+USEFUL_BITS+TAIL_BITS+GUARD_BITS) //a full TS (156 bits) 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0,
#define TS_PER_FRAME 8 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
#define FRAME_BITS (TS_PER_FRAME * TS_BITS + 2) // 156.25 * 8 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1,
#define FCCH_POS TAIL_BITS 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1
#define SYNC_POS (TAIL_BITS + 39) };
#define TRAIN_POS ( TAIL_BITS + (DATA_BITS+STEALING_BIT) + 5) //first 5 bits of a training sequence
//aren't used for channel impulse response estimation const unsigned FCCH_FRAMES[] = { 0, 10, 20, 30, 40 };
#define TRAIN_BEGINNING 5 const unsigned SCH_FRAMES[] = { 1, 11, 21, 31, 41 };
#define SAFETY_MARGIN 6 //
const unsigned BCCH_FRAMES[] = { 2, 3, 4, 5 }; //!!the receiver shouldn't care about logical
#define FCCH_HITS_NEEDED (USEFUL_BITS - 4) //!!channels so this will be removed from this header
#define FCCH_MAX_MISSES 1 const unsigned TEST_CCH_FRAMES[] = { 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 36, 37, 38, 39, 42, 43, 44, 45, 46, 47, 48, 49 };
#define FCCH_MAX_FREQ_OFFSET 100 const unsigned TRAFFIC_CHANNEL_F[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 };
const unsigned TEST51[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 };
#define CHAN_IMP_RESP_LENGTH 5
#define MAX_SCH_ERRORS 10 //maximum number of subsequent sch errors after which gsm receiver goes to find_next_fcch state #define TSC0 0
#define TSC1 1
typedef enum { empty, fcch_burst, sch_burst, normal_burst, rach_burst, dummy, dummy_or_normal, normal_or_noise } burst_type; #define TSC2 2
typedef enum { unknown, multiframe_26, multiframe_51 } multiframe_type; #define TSC3 3
#define TSC4 4
static const unsigned char SYNC_BITS[] = { #define TSC5 5
1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, #define TSC6 6
0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, #define TSC7 7
0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, #define TS_DUMMY 8
0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1
}; #define TRAIN_SEQ_NUM 9
static const unsigned char ACCESS_BITS [] = { #define TIMESLOT0 0
0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, #define TIMESLOT1 1
0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, #define TIMESLOT2 2
0, 1, 1, 1, 1, 0, 0, 0 #define TIMESLOT3 3
}; #define TIMESLOT4 4
#define TIMESLOT5 5
const unsigned FCCH_FRAMES[] = { 0, 10, 20, 30, 40 }; #define TIMESLOT6 6
const unsigned SCH_FRAMES[] = { 1, 11, 21, 31, 41 }; #define TIMESLOT7 7
const unsigned BCCH_FRAMES[] = { 2, 3, 4, 5 }; //!!the receiver shouldn't care about logical
//!!channels so this will be removed from this header static const unsigned char train_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS] = {
const unsigned TEST_CCH_FRAMES[] = { 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 36, 37, 38, 39, 42, 43, 44, 45, 46, 47, 48, 49 }; {0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1},
const unsigned TRAFFIC_CHANNEL_F[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 }; {0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1},
const unsigned TEST51[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 }; {0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0},
{0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0},
{0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1},
#define TSC0 0 {0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0},
#define TSC1 1 {1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1},
#define TSC2 2 {1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0},
#define TSC3 3 {0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1} // DUMMY
#define TSC4 4 };
#define TSC5 5
#define TSC6 6
#define TSC7 7 //Dummy burst 0xFB 76 0A 4E 09 10 1F 1C 5C 5C 57 4A 33 39 E9 F1 2F A8
#define TS_DUMMY 8 static const unsigned char dummy_burst[] = {
0, 0, 0,
#define TRAIN_SEQ_NUM 9 1, 1, 1, 1, 1, 0, 1, 1, 0, 1,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0,
#define TIMESLOT0 0 1, 0, 1, 0, 0, 1, 0, 0, 1, 1,
#define TIMESLOT1 1 1, 0, 0, 0, 0, 0, 1, 0, 0, 1,
#define TIMESLOT2 2 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
#define TIMESLOT3 3 0, 1, 1, 1, 1, 1, 0, 0,
#define TIMESLOT4 4
#define TIMESLOT5 5 0, 1, 1, 1, 0, 0, 0, 1, 0, 1,
#define TIMESLOT6 6 1, 1, 0, 0, 0, 1, 0, 1, 1, 1,
#define TIMESLOT7 7 0, 0, 0, 1, 0, 1,
0, 1, 1, 1, 0, 1, 0, 0, 1, 0,
static const unsigned char train_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS] = { 1, 0, 0, 0, 1, 1, 0, 0, 1, 1,
{0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1}, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1,
{0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1}, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1,
{0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0}, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1,
{0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0}, 1, 1, 1, 0, 1, 0, 1, 0,
{0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1}, 0, 0, 0
{0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0}, };
{1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1},
{1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0},
{0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1} // DUMMY
};
//Dummy burst 0xFB 76 0A 4E 09 10 1F 1C 5C 5C 57 4A 33 39 E9 F1 2F A8
static const unsigned char dummy_burst[] = {
0, 0, 0,
1, 1, 1, 1, 1, 0, 1, 1, 0, 1,
1, 1, 0, 1, 1, 0, 0, 0, 0, 0,
1, 0, 1, 0, 0, 1, 0, 0, 1, 1,
1, 0, 0, 0, 0, 0, 1, 0, 0, 1,
0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
0, 1, 1, 1, 1, 1, 0, 0,
0, 1, 1, 1, 0, 0, 0, 1, 0, 1,
1, 1, 0, 0, 0, 1, 0, 1, 1, 1,
0, 0, 0, 1, 0, 1,
0, 1, 1, 1, 0, 1, 0, 0, 1, 0,
1, 0, 0, 0, 1, 1, 0, 0, 1, 1,
0, 0, 1, 1, 1, 0, 0, 1, 1, 1,
1, 0, 1, 0, 0, 1, 1, 1, 1, 1,
0, 0, 0, 1, 0, 0, 1, 0, 1, 1,
1, 1, 1, 0, 1, 0, 1, 0,
0, 0, 0
};

View File

@@ -1,305 +1,338 @@
/* -*- c++ -*- */ /* -*- c++ -*- */
/* /*
* @file * @file
* @author (C) 2009-2017 by Piotr Krysik <ptrkrysik@gmail.com> * @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
* @section LICENSE *
* * Gr-gsm is free software; you can redistribute it and/or modify
* 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
* it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3, or (at your option)
* the Free Software Foundation; either version 3, or (at your option) * any later version.
* any later version. *
* * Gr-gsm is distributed in the hope that it will be useful,
* Gr-gsm is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of
* but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details.
* GNU General Public License for more details. *
* * You should have received a copy of the GNU General Public License
* You should have received a copy of the GNU General Public License * along with gr-gsm; see the file COPYING. If not, write to
* along with gr-gsm; see the file COPYING. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street,
* the Free Software Foundation, Inc., 51 Franklin Street, * Boston, MA 02110-1301, USA.
* Boston, MA 02110-1301, USA. */
*/
#include "constants.h"
#include "constants.h"
#ifdef HAVE_CONFIG_H
#ifdef HAVE_CONFIG_H #include "config.h"
#include "config.h" #endif
#endif #include <complex>
#include <complex>
#include <algorithm>
#include <algorithm> #include <string.h>
#include <string.h> #include <iostream>
#include <iostream> #include <numeric>
#include <numeric> #include <vector>
#include <vector> #include <fstream>
#include <fstream>
#include "viterbi_detector.h"
#include "viterbi_detector.h" #include "grgsm_vitac.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_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
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;
const int d_chan_imp_length = CHAN_IMP_RESP_LENGTH;
void initvita() {
void initvita()
{ /**
/** * Prepare SCH sequence bits
* Prepare SCH sequence bits *
* * (TS_BITS + 2 * GUARD_PERIOD)
* (TS_BITS + 2 * GUARD_PERIOD) * Burst and two guard periods
* Burst and two guard periods * (one guard period is an arbitrary overlap)
* (one guard period is an arbitrary overlap) */
*/ gmsk_mapper(SYNC_BITS, N_SYNC_BITS,
gmsk_mapper(SYNC_BITS, N_SYNC_BITS, d_sch_training_seq, gr_complex(0.0, -1.0)); d_sch_training_seq, gr_complex(0.0, -1.0));
for (auto &i : d_sch_training_seq) for (auto &i : d_sch_training_seq)
i = conj(i); i = conj(i);
/* ab */ /* Prepare bits of training sequences */
gmsk_mapper(ACCESS_BITS, N_ACCESS_BITS, d_acc_training_seq, gr_complex(0.0, -1.0)); for (int i = 0; i < TRAIN_SEQ_NUM; i++) {
for (auto &i : d_acc_training_seq) /**
i = conj(i); * If first bit of the sequence is 0
* => first symbol is 1, else -1
/* Prepare bits of training sequences */ */
for (int i = 0; i < TRAIN_SEQ_NUM; i++) { gr_complex startpoint = train_seq[i][0] == 0 ?
/** gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
* If first bit of the sequence is 0 gmsk_mapper(train_seq[i], N_TRAIN_BITS,
* => first symbol is 1, else -1 d_norm_training_seq[i], startpoint);
*/ for (auto &i : d_norm_training_seq[i])
gr_complex startpoint = train_seq[i][0] == 0 ? gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0); i = conj(i);
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
template <unsigned int burst_size> detect_burst(const gr_complex* input,
NO_UBSAN static void detect_burst_generic(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, gr_complex* chan_imp_resp, int burst_start,
char *output_binary, int ss) unsigned char* output_binary)
{ {
std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR); std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
unsigned int stop_states[2] = { 4, 12 }; 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]; gr_complex rhh[CHAN_IMP_RESP_LENGTH];
float output[burst_size]; float output[BURST_SIZE];
int start_state = ss; int start_state = 3;
autocorrelation(chan_imp_resp, &rhh_temp[0], d_chan_imp_length * d_OSR); // if(burst_start < 0 ||burst_start > 10)
for (int ii = 0; ii < d_chan_imp_length; ii++) // fprintf(stderr, "bo %d\n", burst_start);
rhh[ii] = conj(rhh_temp[ii * d_OSR]);
// burst_start = burst_start >= 0 ? burst_start : 0;
mafi(&input[burst_start], burst_size, chan_imp_resp, d_chan_imp_length * d_OSR, filtered_burst);
autocorrelation(chan_imp_resp, &rhh_temp[0], d_chan_imp_length * d_OSR);
viterbi_detector(filtered_burst, burst_size, rhh, start_state, stop_states, 2, output); for (int ii = 0; ii < d_chan_imp_length; ii++)
rhh[ii] = conj(rhh_temp[ii * d_OSR]);
for (unsigned int i = 0; i < burst_size; i++)
output_binary[i] = output[i] > 0 ? -127 : 127; // pre flip bits! mafi(&input[burst_start], BURST_SIZE, chan_imp_resp,
} d_chan_imp_length * d_OSR, filtered_burst);
NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary, viterbi_detector(filtered_burst, BURST_SIZE, rhh,
int ss) start_state, stop_states, 2, output);
{
return detect_burst_generic<BURST_SIZE>(input, chan_imp_resp, burst_start, output_binary, ss); for (int i = 0; i < BURST_SIZE; i++)
} output_binary[i] = output[i] > 0;
NO_UBSAN void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary, }
int ss)
{ int process_vita_burst(gr_complex* input, int tsc, unsigned char* output_binary) {
return detect_burst_generic<8 + 41 + 36 + 3>(input, chan_imp_resp, burst_start, output_binary, ss); 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;
NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
{ dummy_burst_start = get_norm_chan_imp_resp(input,
return detect_burst_nb(input, chan_imp_resp, burst_start, output_binary, 3); &channel_imp_resp[0], &dummy_corr_max, TS_DUMMY);
} normal_burst_start = get_norm_chan_imp_resp(input,
NO_UBSAN void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary) &channel_imp_resp[0], &normal_corr_max, tsc);
{
return detect_burst_ab(input, chan_imp_resp, burst_start, output_binary, 3); if (normal_corr_max > dummy_corr_max) {
} /* Perform MLSE detection */
detect_burst(input, &channel_imp_resp[0],
void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output, gr_complex start_point) normal_burst_start, output_binary);
{
gr_complex j = gr_complex(0.0, 1.0); return 0;
gmsk_output[0] = start_point;
} else {
int previous_symbol = 2 * input[0] - 1; memcpy(output_binary, dummy_burst, 148);
int current_symbol; //std::cerr << std::endl << "#NOPE#" << dd.fpath << std::endl << std::endl;
int encoded_symbol; return -1;
}
for (int i = 1; i < nitems; i++) { }
/* Change bits representation to NRZ */
current_symbol = 2 * input[i] - 1; 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];
/* Differentially encode */
encoded_symbol = current_symbol * previous_symbol; /* Get channel impulse response */
int d_c0_burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]);
/* And do GMSK mapping */ // *offset = d_c0_burst_start;
gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0) * gmsk_output[i - 1]; /* Perform MLSE detection */
detect_burst(input, &channel_imp_resp[0],
previous_symbol = current_symbol; d_c0_burst_start, output_binary);
}
} return 0;
}
gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_complex *input)
{ void
gr_complex result(0.0, 0.0); gmsk_mapper(const unsigned char* input,
int nitems, gr_complex* gmsk_output, gr_complex start_point)
for (int ii = 0; ii < length; ii++) {
result += sequence[ii] * input[ii * d_OSR]; gr_complex j = gr_complex(0.0, 1.0);
gmsk_output[0] = start_point;
return conj(result) / gr_complex(length, 0);
} int previous_symbol = 2 * input[0] - 1;
int current_symbol;
/* Computes autocorrelation for positive arguments */ int encoded_symbol;
inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems)
{ for (int i = 1; i < nitems; i++) {
for (int k = nitems - 1; k >= 0; k--) { /* Change bits representation to NRZ */
out[k] = gr_complex(0, 0); current_symbol = 2 * input[i] - 1;
for (int i = k; i < nitems; i++)
out[k] += input[i] * conj(input[i - k]); /* Differentially encode */
} encoded_symbol = current_symbol * previous_symbol;
}
/* And do GMSK mapping */
inline void mafi(const gr_complex *input, int nitems, gr_complex *filter, int filter_length, gr_complex *output) gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0)
{ * gmsk_output[i - 1];
for (int n = 0; n < nitems; n++) {
int a = n * d_OSR; previous_symbol = current_symbol;
output[n] = 0; }
}
for (int ii = 0; ii < filter_length; ii++) {
if ((a + ii) >= nitems * d_OSR) gr_complex
break; correlate_sequence(const gr_complex* sequence,
int length, const gr_complex* input)
output[n] += input[a + ii] * filter[ii]; {
} gr_complex result(0.0, 0.0);
}
} for (int ii = 0; ii < length; ii++)
result += sequence[ii] * input[ii * d_OSR];
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) return conj(result) / gr_complex(length, 0);
{ }
const int num_search_windows = search_stop_pos - search_start_pos;
const int power_search_window_len = d_chan_imp_length * d_OSR; /* Computes autocorrelation for positive arguments */
std::vector<float> window_energy_buffer; inline void
std::vector<float> power_buffer; autocorrelation(const gr_complex* input,
std::vector<gr_complex> correlation_buffer; gr_complex* out, int nitems)
{
power_buffer.reserve(num_search_windows); for (int k = nitems - 1; k >= 0; k--) {
correlation_buffer.reserve(num_search_windows); out[k] = gr_complex(0, 0);
window_energy_buffer.reserve(num_search_windows); for (int i = k; i < nitems; i++)
out[k] += input[i] * conj(input[i - k]);
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)); inline void
} mafi(const gr_complex* input, int nitems,
gr_complex* filter, int filter_length, gr_complex* output)
/* Compute window energies */ {
float windowSum = 0; for (int n = 0; n < nitems; n++) {
int a = n * d_OSR;
// first window output[n] = 0;
for (int i = 0; i < power_search_window_len; i++) {
windowSum += power_buffer[i]; for (int ii = 0; ii < filter_length; ii++) {
} if ((a + ii) >= nitems * d_OSR)
window_energy_buffer.push_back(windowSum); break;
// slide windows output[n] += input[a + ii] * filter[ii];
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); }
}
int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int search_center, int search_start_pos,
int strongest_window_nr = std::max_element(window_energy_buffer.begin(), window_energy_buffer.end()) - int search_stop_pos, gr_complex *tseq, int tseqlen, float *corr_max)
window_energy_buffer.begin(); {
std::vector<gr_complex> correlation_buffer;
float max_correlation = 0; std::vector<float> window_energy_buffer;
for (int ii = 0; ii < power_search_window_len; ii++) { std::vector<float> power_buffer;
gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
if (abs(correlation) > max_correlation) for (int ii = search_start_pos; ii < search_stop_pos; ii++) {
max_correlation = abs(correlation); gr_complex correlation = correlate_sequence(tseq, tseqlen, &input[ii]);
chan_imp_resp[ii] = correlation; correlation_buffer.push_back(correlation);
} power_buffer.push_back(std::pow(abs(correlation), 2));
}
*corr_max = max_correlation;
int strongest_corr_nr = max_element(power_buffer.begin(), power_buffer.end()) - power_buffer.begin();
/**
* Compute first sample position, which corresponds /* Compute window energies */
* to the first sample of the impulse response 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..
return search_start_pos + strongest_window_nr; 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) {
8 ext tail bits std::vector<float>::iterator iter_ii = iter;
41 sync seq bool loop_end = false;
36 encrypted bits float energy = 0;
3 tail bits
68.25 extended tail bits (!) int len = d_chan_imp_length * d_OSR;
for (int ii = 0; ii < len; ii++, iter_ii++) {
center at 8+5 (actually known tb -> known isi, start at 8?) FIXME if (iter_ii == power_buffer.end()) {
*/ loop_end = true;
int get_access_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int max_delay) break;
{ }
const int search_center = 8 + 5;
const int search_start_pos = (search_center - 5) * d_OSR + 1; energy += (*iter_ii);
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); if (loop_end)
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) - break;
search_center * d_OSR;
} window_energy_buffer.push_back(energy);
iter++;
/* }
3 + 57 + 1 + 26 + 1 + 57 + 3 + 8.25 /* Calculate the strongest window number */
int strongest_window_nr = window_energy_start_offset +
search center = 3 + 57 + 1 + 5 (due to tsc 5+16+5 split) max_element(window_energy_buffer.begin(), window_energy_buffer.end()) -
this is +-5 samples around (+5 beginning) of truncated t16 tsc window_energy_buffer.begin();
*/ // auto window_search_start = window_energy_buffer.begin() + strongest_corr_nr - 5* d_OSR;
int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int bcc) // 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;
const int search_center = TRAIN_POS;
const int search_start_pos = (search_center - 5) * d_OSR + 1; // /* Calculate the strongest window number */
const int search_stop_pos = (search_center + 5 + d_chan_imp_length) * d_OSR; // int strongest_window_nr = max_element(window_search_start, window_search_end /* - d_chan_imp_length * d_OSR*/) - window_energy_buffer.begin();
const auto tseq = &d_norm_training_seq[bcc][TRAIN_BEGINNING];
const auto tseqlen = N_TRAIN_BITS - (2 * TRAIN_BEGINNING); // if (strongest_window_nr < 0)
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) - // strongest_window_nr = 0;
search_center * d_OSR;
} float max_correlation = 0;
for (int ii = 0; ii < d_chan_imp_length * d_OSR; ii++) {
/* gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
if (abs(correlation) > max_correlation)
3 tail | 39 data | 64 tsc | 39 data | 3 tail | 8.25 guard max_correlation = abs(correlation);
start 3+39 - 10 chan_imp_resp[ii] = correlation;
end 3+39 + SYNC_SEARCH_RANGE }
*/ *corr_max = max_correlation;
int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp)
{ /**
const int search_center = SYNC_POS + TRAIN_BEGINNING; * Compute first sample position, which corresponds
const int search_start_pos = (search_center - 10) * d_OSR; * to the first sample of the impulse response
const int search_stop_pos = (search_center + SYNC_SEARCH_RANGE) * d_OSR; */
const auto tseq = &d_sch_training_seq[TRAIN_BEGINNING]; return search_start_pos + strongest_window_nr - search_center * d_OSR;
const auto tseqlen = N_SYNC_BITS - (2 * TRAIN_BEGINNING); }
// 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_start_pos, search_stop_pos, tseq, tseqlen, &corr_max) - 3 + 57 + 1 + 26 + 1 + 57 + 3 + 8.25
search_center * d_OSR;
; search center = 3 + 57 + 1 + 5 (due to tsc 5+16+5 split)
} this is +-5 samples around (+5 beginning) of truncated t16 tsc
int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max) */
{ int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int bcc)
const auto tseqlen = N_SYNC_BITS - (2 * TRAIN_BEGINNING); {
const int search_center = SYNC_POS + TRAIN_BEGINNING; const int search_center = TRAIN_POS;
const int search_start_pos = 0; const int search_start_pos = (search_center - 5) * d_OSR + 1;
// FIXME: proper end offset const int search_stop_pos = (search_center + 5 + d_chan_imp_length) * d_OSR;
const int search_stop_pos = len - (N_SYNC_BITS * 8); const auto tseq = &d_norm_training_seq[bcc][TRAIN_BEGINNING];
auto tseq = &d_sch_training_seq[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,
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) - corr_max);
search_center * d_OSR; }
/*
3 tail | 39 data | 64 tsc | 39 data | 3 tail | 8.25 guard
start 3+39 - 10
end 3+39 + SYNC_SEARCH_RANGE
*/
int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp)
{
const int search_center = SYNC_POS + TRAIN_BEGINNING;
const int search_start_pos = (search_center - 10) * d_OSR;
const int search_stop_pos = (search_center + SYNC_SEARCH_RANGE) * d_OSR;
const auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
const auto tseqlen = N_SYNC_BITS - (2 * TRAIN_BEGINNING);
// 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);
}
int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max)
{
const auto tseqlen = N_SYNC_BITS - (2 * TRAIN_BEGINNING);
const int search_center = SYNC_POS + TRAIN_BEGINNING;
const int search_start_pos = 0;
// FIXME: proper end offset
const int search_stop_pos = len - (N_SYNC_BITS*8);
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);
} }

View File

@@ -1,60 +1,17 @@
#pragma once #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 <vector>
#include "constants.h" #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 defined(__has_attribute)
#if __has_attribute(target_clones) && defined(__x86_64) && true #if __has_attribute(target_clones) && defined(__x86_64) && false
#define MULTI_VER_TARGET_ATTR __attribute__((target_clones("avx", "sse4.2", "sse3", "sse2", "sse", "default"))) #define MULTI_VER_TARGET_ATTR __attribute__((target_clones("avx", "sse4.2", "sse3", "sse2", "sse", "default")))
#else #else
#define MULTI_VER_TARGET_ATTR #define MULTI_VER_TARGET_ATTR
#endif #endif
#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 #define SYNC_SEARCH_RANGE 30
const int d_OSR(4); const int d_OSR(4);
@@ -63,11 +20,8 @@ void initvita();
int process_vita_burst(gr_complex *input, int tsc, unsigned char *output_binary); 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); int process_vita_sc_burst(gr_complex *input, int tsc, unsigned char *output_binary, int *offset);
void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary, int ss); MULTI_VER_TARGET_ATTR
void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary, int ss); 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);
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); 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); 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); inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems);
@@ -75,7 +29,6 @@ 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_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_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_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 }; enum class btype { NB, SCH };
struct fdata { struct fdata {

View File

@@ -1,392 +1,392 @@
/* -*- c++ -*- */ /* -*- c++ -*- */
/* /*
* @file * @file
* @author (C) 2009 by Piotr Krysik <ptrkrysik@gmail.com> * @author (C) 2009 by Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE * @section LICENSE
* *
* Gr-gsm is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option) * the Free Software Foundation; either version 3, or (at your option)
* any later version. * any later version.
* *
* Gr-gsm is distributed in the hope that it will be useful, * Gr-gsm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with gr-gsm; see the file COPYING. If not, write to * along with gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, * the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
/* /*
* viterbi_detector: * viterbi_detector:
* This part does the detection of received sequnece. * This part does the detection of received sequnece.
* Employed algorithm is viterbi Maximum Likehood Sequence Estimation. * Employed algorithm is viterbi Maximum Likehood Sequence Estimation.
* At this moment it gives hard decisions on the output, but * At this moment it gives hard decisions on the output, but
* it was designed with soft decisions in mind. * it was designed with soft decisions in mind.
* *
* SYNTAX: void viterbi_detector( * SYNTAX: void viterbi_detector(
* const gr_complex * input, * const gr_complex * input,
* unsigned int samples_num, * unsigned int samples_num,
* gr_complex * rhh, * gr_complex * rhh,
* unsigned int start_state, * unsigned int start_state,
* const unsigned int * stop_states, * const unsigned int * stop_states,
* unsigned int stops_num, * unsigned int stops_num,
* float * output) * float * output)
* *
* INPUT: input: Complex received signal afted matched filtering. * INPUT: input: Complex received signal afted matched filtering.
* samples_num: Number of samples in the input table. * samples_num: Number of samples in the input table.
* rhh: The autocorrelation of the estimated channel * rhh: The autocorrelation of the estimated channel
* impulse response. * impulse response.
* start_state: Number of the start point. In GSM each burst * start_state: Number of the start point. In GSM each burst
* starts with sequence of three bits (0,0,0) which * starts with sequence of three bits (0,0,0) which
* indicates start point of the algorithm. * indicates start point of the algorithm.
* stop_states: Table with numbers of possible stop states. * stop_states: Table with numbers of possible stop states.
* stops_num: Number of possible stop states * stops_num: Number of possible stop states
* *
* *
* OUTPUT: output: Differentially decoded hard output of the algorithm: * OUTPUT: output: Differentially decoded hard output of the algorithm:
* -1 for logical "0" and 1 for logical "1" * -1 for logical "0" and 1 for logical "1"
* *
* SUB_FUNC: none * SUB_FUNC: none
* *
* TEST(S): Tested with real world normal burst. * TEST(S): Tested with real world normal burst.
*/ */
#include "constants.h" #include "constants.h"
#include <cmath> #include <cmath>
#define PATHS_NUM (1 << (CHAN_IMP_RESP_LENGTH-1)) #define PATHS_NUM (1 << (CHAN_IMP_RESP_LENGTH-1))
void viterbi_detector(const gr_complex * input, unsigned int samples_num, gr_complex * rhh, unsigned int start_state, const unsigned int * stop_states, unsigned int stops_num, float * output) void viterbi_detector(const gr_complex * input, unsigned int samples_num, gr_complex * rhh, unsigned int start_state, const unsigned int * stop_states, unsigned int stops_num, float * output)
{ {
float increment[8]; float increment[8];
float path_metrics1[16]; float path_metrics1[16];
float path_metrics2[16]; float path_metrics2[16];
float paths_difference; float paths_difference;
float * new_path_metrics; float * new_path_metrics;
float * old_path_metrics; float * old_path_metrics;
float * tmp; float * tmp;
float trans_table[BURST_SIZE][16]; float trans_table[BURST_SIZE][16];
float pm_candidate1, pm_candidate2; float pm_candidate1, pm_candidate2;
bool real_imag; bool real_imag;
float input_symbol_real, input_symbol_imag; float input_symbol_real, input_symbol_imag;
unsigned int i, sample_nr; unsigned int i, sample_nr;
/* /*
* Setup first path metrics, so only state pointed by start_state is possible. * Setup first path metrics, so only state pointed by start_state is possible.
* Start_state metric is equal to zero, the rest is written with some very low value, * Start_state metric is equal to zero, the rest is written with some very low value,
* which makes them practically impossible to occur. * which makes them practically impossible to occur.
*/ */
for(i=0; i<PATHS_NUM; i++){ for(i=0; i<PATHS_NUM; i++){
path_metrics1[i]=(-10e30); path_metrics1[i]=(-10e30);
} }
path_metrics1[start_state]=0; path_metrics1[start_state]=0;
/* /*
* Compute Increment - a table of values which does not change for subsequent input samples. * Compute Increment - a table of values which does not change for subsequent input samples.
* Increment is table of reference levels for computation of branch metrics: * Increment is table of reference levels for computation of branch metrics:
* branch metric = (+/-)received_sample (+/-) reference_level * branch metric = (+/-)received_sample (+/-) reference_level
*/ */
increment[0] = -rhh[1].imag() -rhh[2].real() -rhh[3].imag() +rhh[4].real(); increment[0] = -rhh[1].imag() -rhh[2].real() -rhh[3].imag() +rhh[4].real();
increment[1] = rhh[1].imag() -rhh[2].real() -rhh[3].imag() +rhh[4].real(); increment[1] = rhh[1].imag() -rhh[2].real() -rhh[3].imag() +rhh[4].real();
increment[2] = -rhh[1].imag() +rhh[2].real() -rhh[3].imag() +rhh[4].real(); increment[2] = -rhh[1].imag() +rhh[2].real() -rhh[3].imag() +rhh[4].real();
increment[3] = rhh[1].imag() +rhh[2].real() -rhh[3].imag() +rhh[4].real(); increment[3] = rhh[1].imag() +rhh[2].real() -rhh[3].imag() +rhh[4].real();
increment[4] = -rhh[1].imag() -rhh[2].real() +rhh[3].imag() +rhh[4].real(); increment[4] = -rhh[1].imag() -rhh[2].real() +rhh[3].imag() +rhh[4].real();
increment[5] = rhh[1].imag() -rhh[2].real() +rhh[3].imag() +rhh[4].real(); increment[5] = rhh[1].imag() -rhh[2].real() +rhh[3].imag() +rhh[4].real();
increment[6] = -rhh[1].imag() +rhh[2].real() +rhh[3].imag() +rhh[4].real(); increment[6] = -rhh[1].imag() +rhh[2].real() +rhh[3].imag() +rhh[4].real();
increment[7] = rhh[1].imag() +rhh[2].real() +rhh[3].imag() +rhh[4].real(); increment[7] = rhh[1].imag() +rhh[2].real() +rhh[3].imag() +rhh[4].real();
/* /*
* Computation of path metrics and decisions (Add-Compare-Select). * Computation of path metrics and decisions (Add-Compare-Select).
* It's composed of two parts: one for odd input samples (imaginary numbers) * It's composed of two parts: one for odd input samples (imaginary numbers)
* and one for even samples (real numbers). * and one for even samples (real numbers).
* Each part is composed of independent (parallelisable) statements like * Each part is composed of independent (parallelisable) statements like
* this one: * this one:
* pm_candidate1 = old_path_metrics[0] -input_symbol_imag +increment[2]; * pm_candidate1 = old_path_metrics[0] -input_symbol_imag +increment[2];
* pm_candidate2 = old_path_metrics[8] -input_symbol_imag -increment[5]; * pm_candidate2 = old_path_metrics[8] -input_symbol_imag -increment[5];
* paths_difference=pm_candidate2-pm_candidate1; * paths_difference=pm_candidate2-pm_candidate1;
* new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; * new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
* trans_table[sample_nr][1] = paths_difference; * trans_table[sample_nr][1] = paths_difference;
* This is very good point for optimisations (SIMD or OpenMP) as it's most time * This is very good point for optimisations (SIMD or OpenMP) as it's most time
* consuming part of this function. * consuming part of this function.
*/ */
sample_nr=0; sample_nr=0;
old_path_metrics=path_metrics1; old_path_metrics=path_metrics1;
new_path_metrics=path_metrics2; new_path_metrics=path_metrics2;
while(sample_nr<samples_num){ while(sample_nr<samples_num){
//Processing imag states //Processing imag states
real_imag=1; real_imag=1;
input_symbol_imag = input[sample_nr].imag(); input_symbol_imag = input[sample_nr].imag();
pm_candidate1 = old_path_metrics[0] +input_symbol_imag -increment[2]; pm_candidate1 = old_path_metrics[0] +input_symbol_imag -increment[2];
pm_candidate2 = old_path_metrics[8] +input_symbol_imag +increment[5]; pm_candidate2 = old_path_metrics[8] +input_symbol_imag +increment[5];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[0]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[0]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][0] = paths_difference; trans_table[sample_nr][0] = paths_difference;
pm_candidate1 = old_path_metrics[0] -input_symbol_imag +increment[2]; pm_candidate1 = old_path_metrics[0] -input_symbol_imag +increment[2];
pm_candidate2 = old_path_metrics[8] -input_symbol_imag -increment[5]; pm_candidate2 = old_path_metrics[8] -input_symbol_imag -increment[5];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][1] = paths_difference; trans_table[sample_nr][1] = paths_difference;
pm_candidate1 = old_path_metrics[1] +input_symbol_imag -increment[3]; pm_candidate1 = old_path_metrics[1] +input_symbol_imag -increment[3];
pm_candidate2 = old_path_metrics[9] +input_symbol_imag +increment[4]; pm_candidate2 = old_path_metrics[9] +input_symbol_imag +increment[4];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[2]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[2]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][2] = paths_difference; trans_table[sample_nr][2] = paths_difference;
pm_candidate1 = old_path_metrics[1] -input_symbol_imag +increment[3]; pm_candidate1 = old_path_metrics[1] -input_symbol_imag +increment[3];
pm_candidate2 = old_path_metrics[9] -input_symbol_imag -increment[4]; pm_candidate2 = old_path_metrics[9] -input_symbol_imag -increment[4];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[3]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[3]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][3] = paths_difference; trans_table[sample_nr][3] = paths_difference;
pm_candidate1 = old_path_metrics[2] +input_symbol_imag -increment[0]; pm_candidate1 = old_path_metrics[2] +input_symbol_imag -increment[0];
pm_candidate2 = old_path_metrics[10] +input_symbol_imag +increment[7]; pm_candidate2 = old_path_metrics[10] +input_symbol_imag +increment[7];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[4]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[4]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][4] = paths_difference; trans_table[sample_nr][4] = paths_difference;
pm_candidate1 = old_path_metrics[2] -input_symbol_imag +increment[0]; pm_candidate1 = old_path_metrics[2] -input_symbol_imag +increment[0];
pm_candidate2 = old_path_metrics[10] -input_symbol_imag -increment[7]; pm_candidate2 = old_path_metrics[10] -input_symbol_imag -increment[7];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[5]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[5]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][5] = paths_difference; trans_table[sample_nr][5] = paths_difference;
pm_candidate1 = old_path_metrics[3] +input_symbol_imag -increment[1]; pm_candidate1 = old_path_metrics[3] +input_symbol_imag -increment[1];
pm_candidate2 = old_path_metrics[11] +input_symbol_imag +increment[6]; pm_candidate2 = old_path_metrics[11] +input_symbol_imag +increment[6];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[6]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[6]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][6] = paths_difference; trans_table[sample_nr][6] = paths_difference;
pm_candidate1 = old_path_metrics[3] -input_symbol_imag +increment[1]; pm_candidate1 = old_path_metrics[3] -input_symbol_imag +increment[1];
pm_candidate2 = old_path_metrics[11] -input_symbol_imag -increment[6]; pm_candidate2 = old_path_metrics[11] -input_symbol_imag -increment[6];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[7]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[7]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][7] = paths_difference; trans_table[sample_nr][7] = paths_difference;
pm_candidate1 = old_path_metrics[4] +input_symbol_imag -increment[6]; pm_candidate1 = old_path_metrics[4] +input_symbol_imag -increment[6];
pm_candidate2 = old_path_metrics[12] +input_symbol_imag +increment[1]; pm_candidate2 = old_path_metrics[12] +input_symbol_imag +increment[1];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[8]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[8]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][8] = paths_difference; trans_table[sample_nr][8] = paths_difference;
pm_candidate1 = old_path_metrics[4] -input_symbol_imag +increment[6]; pm_candidate1 = old_path_metrics[4] -input_symbol_imag +increment[6];
pm_candidate2 = old_path_metrics[12] -input_symbol_imag -increment[1]; pm_candidate2 = old_path_metrics[12] -input_symbol_imag -increment[1];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[9]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[9]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][9] = paths_difference; trans_table[sample_nr][9] = paths_difference;
pm_candidate1 = old_path_metrics[5] +input_symbol_imag -increment[7]; pm_candidate1 = old_path_metrics[5] +input_symbol_imag -increment[7];
pm_candidate2 = old_path_metrics[13] +input_symbol_imag +increment[0]; pm_candidate2 = old_path_metrics[13] +input_symbol_imag +increment[0];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[10]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[10]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][10] = paths_difference; trans_table[sample_nr][10] = paths_difference;
pm_candidate1 = old_path_metrics[5] -input_symbol_imag +increment[7]; pm_candidate1 = old_path_metrics[5] -input_symbol_imag +increment[7];
pm_candidate2 = old_path_metrics[13] -input_symbol_imag -increment[0]; pm_candidate2 = old_path_metrics[13] -input_symbol_imag -increment[0];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[11]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[11]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][11] = paths_difference; trans_table[sample_nr][11] = paths_difference;
pm_candidate1 = old_path_metrics[6] +input_symbol_imag -increment[4]; pm_candidate1 = old_path_metrics[6] +input_symbol_imag -increment[4];
pm_candidate2 = old_path_metrics[14] +input_symbol_imag +increment[3]; pm_candidate2 = old_path_metrics[14] +input_symbol_imag +increment[3];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[12]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[12]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][12] = paths_difference; trans_table[sample_nr][12] = paths_difference;
pm_candidate1 = old_path_metrics[6] -input_symbol_imag +increment[4]; pm_candidate1 = old_path_metrics[6] -input_symbol_imag +increment[4];
pm_candidate2 = old_path_metrics[14] -input_symbol_imag -increment[3]; pm_candidate2 = old_path_metrics[14] -input_symbol_imag -increment[3];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[13]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[13]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][13] = paths_difference; trans_table[sample_nr][13] = paths_difference;
pm_candidate1 = old_path_metrics[7] +input_symbol_imag -increment[5]; pm_candidate1 = old_path_metrics[7] +input_symbol_imag -increment[5];
pm_candidate2 = old_path_metrics[15] +input_symbol_imag +increment[2]; pm_candidate2 = old_path_metrics[15] +input_symbol_imag +increment[2];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[14]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[14]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][14] = paths_difference; trans_table[sample_nr][14] = paths_difference;
pm_candidate1 = old_path_metrics[7] -input_symbol_imag +increment[5]; pm_candidate1 = old_path_metrics[7] -input_symbol_imag +increment[5];
pm_candidate2 = old_path_metrics[15] -input_symbol_imag -increment[2]; pm_candidate2 = old_path_metrics[15] -input_symbol_imag -increment[2];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[15]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[15]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][15] = paths_difference; trans_table[sample_nr][15] = paths_difference;
tmp=old_path_metrics; tmp=old_path_metrics;
old_path_metrics=new_path_metrics; old_path_metrics=new_path_metrics;
new_path_metrics=tmp; new_path_metrics=tmp;
sample_nr++; sample_nr++;
if(sample_nr==samples_num) if(sample_nr==samples_num)
break; break;
//Processing real states //Processing real states
real_imag=0; real_imag=0;
input_symbol_real = input[sample_nr].real(); input_symbol_real = input[sample_nr].real();
pm_candidate1 = old_path_metrics[0] -input_symbol_real -increment[7]; pm_candidate1 = old_path_metrics[0] -input_symbol_real -increment[7];
pm_candidate2 = old_path_metrics[8] -input_symbol_real +increment[0]; pm_candidate2 = old_path_metrics[8] -input_symbol_real +increment[0];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[0]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[0]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][0] = paths_difference; trans_table[sample_nr][0] = paths_difference;
pm_candidate1 = old_path_metrics[0] +input_symbol_real +increment[7]; pm_candidate1 = old_path_metrics[0] +input_symbol_real +increment[7];
pm_candidate2 = old_path_metrics[8] +input_symbol_real -increment[0]; pm_candidate2 = old_path_metrics[8] +input_symbol_real -increment[0];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][1] = paths_difference; trans_table[sample_nr][1] = paths_difference;
pm_candidate1 = old_path_metrics[1] -input_symbol_real -increment[6]; pm_candidate1 = old_path_metrics[1] -input_symbol_real -increment[6];
pm_candidate2 = old_path_metrics[9] -input_symbol_real +increment[1]; pm_candidate2 = old_path_metrics[9] -input_symbol_real +increment[1];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[2]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[2]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][2] = paths_difference; trans_table[sample_nr][2] = paths_difference;
pm_candidate1 = old_path_metrics[1] +input_symbol_real +increment[6]; pm_candidate1 = old_path_metrics[1] +input_symbol_real +increment[6];
pm_candidate2 = old_path_metrics[9] +input_symbol_real -increment[1]; pm_candidate2 = old_path_metrics[9] +input_symbol_real -increment[1];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[3]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[3]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][3] = paths_difference; trans_table[sample_nr][3] = paths_difference;
pm_candidate1 = old_path_metrics[2] -input_symbol_real -increment[5]; pm_candidate1 = old_path_metrics[2] -input_symbol_real -increment[5];
pm_candidate2 = old_path_metrics[10] -input_symbol_real +increment[2]; pm_candidate2 = old_path_metrics[10] -input_symbol_real +increment[2];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[4]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[4]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][4] = paths_difference; trans_table[sample_nr][4] = paths_difference;
pm_candidate1 = old_path_metrics[2] +input_symbol_real +increment[5]; pm_candidate1 = old_path_metrics[2] +input_symbol_real +increment[5];
pm_candidate2 = old_path_metrics[10] +input_symbol_real -increment[2]; pm_candidate2 = old_path_metrics[10] +input_symbol_real -increment[2];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[5]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[5]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][5] = paths_difference; trans_table[sample_nr][5] = paths_difference;
pm_candidate1 = old_path_metrics[3] -input_symbol_real -increment[4]; pm_candidate1 = old_path_metrics[3] -input_symbol_real -increment[4];
pm_candidate2 = old_path_metrics[11] -input_symbol_real +increment[3]; pm_candidate2 = old_path_metrics[11] -input_symbol_real +increment[3];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[6]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[6]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][6] = paths_difference; trans_table[sample_nr][6] = paths_difference;
pm_candidate1 = old_path_metrics[3] +input_symbol_real +increment[4]; pm_candidate1 = old_path_metrics[3] +input_symbol_real +increment[4];
pm_candidate2 = old_path_metrics[11] +input_symbol_real -increment[3]; pm_candidate2 = old_path_metrics[11] +input_symbol_real -increment[3];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[7]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[7]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][7] = paths_difference; trans_table[sample_nr][7] = paths_difference;
pm_candidate1 = old_path_metrics[4] -input_symbol_real -increment[3]; pm_candidate1 = old_path_metrics[4] -input_symbol_real -increment[3];
pm_candidate2 = old_path_metrics[12] -input_symbol_real +increment[4]; pm_candidate2 = old_path_metrics[12] -input_symbol_real +increment[4];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[8]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[8]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][8] = paths_difference; trans_table[sample_nr][8] = paths_difference;
pm_candidate1 = old_path_metrics[4] +input_symbol_real +increment[3]; pm_candidate1 = old_path_metrics[4] +input_symbol_real +increment[3];
pm_candidate2 = old_path_metrics[12] +input_symbol_real -increment[4]; pm_candidate2 = old_path_metrics[12] +input_symbol_real -increment[4];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[9]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[9]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][9] = paths_difference; trans_table[sample_nr][9] = paths_difference;
pm_candidate1 = old_path_metrics[5] -input_symbol_real -increment[2]; pm_candidate1 = old_path_metrics[5] -input_symbol_real -increment[2];
pm_candidate2 = old_path_metrics[13] -input_symbol_real +increment[5]; pm_candidate2 = old_path_metrics[13] -input_symbol_real +increment[5];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[10]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[10]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][10] = paths_difference; trans_table[sample_nr][10] = paths_difference;
pm_candidate1 = old_path_metrics[5] +input_symbol_real +increment[2]; pm_candidate1 = old_path_metrics[5] +input_symbol_real +increment[2];
pm_candidate2 = old_path_metrics[13] +input_symbol_real -increment[5]; pm_candidate2 = old_path_metrics[13] +input_symbol_real -increment[5];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[11]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[11]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][11] = paths_difference; trans_table[sample_nr][11] = paths_difference;
pm_candidate1 = old_path_metrics[6] -input_symbol_real -increment[1]; pm_candidate1 = old_path_metrics[6] -input_symbol_real -increment[1];
pm_candidate2 = old_path_metrics[14] -input_symbol_real +increment[6]; pm_candidate2 = old_path_metrics[14] -input_symbol_real +increment[6];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[12]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[12]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][12] = paths_difference; trans_table[sample_nr][12] = paths_difference;
pm_candidate1 = old_path_metrics[6] +input_symbol_real +increment[1]; pm_candidate1 = old_path_metrics[6] +input_symbol_real +increment[1];
pm_candidate2 = old_path_metrics[14] +input_symbol_real -increment[6]; pm_candidate2 = old_path_metrics[14] +input_symbol_real -increment[6];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[13]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[13]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][13] = paths_difference; trans_table[sample_nr][13] = paths_difference;
pm_candidate1 = old_path_metrics[7] -input_symbol_real -increment[0]; pm_candidate1 = old_path_metrics[7] -input_symbol_real -increment[0];
pm_candidate2 = old_path_metrics[15] -input_symbol_real +increment[7]; pm_candidate2 = old_path_metrics[15] -input_symbol_real +increment[7];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[14]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[14]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][14] = paths_difference; trans_table[sample_nr][14] = paths_difference;
pm_candidate1 = old_path_metrics[7] +input_symbol_real +increment[0]; pm_candidate1 = old_path_metrics[7] +input_symbol_real +increment[0];
pm_candidate2 = old_path_metrics[15] +input_symbol_real -increment[7]; pm_candidate2 = old_path_metrics[15] +input_symbol_real -increment[7];
paths_difference=pm_candidate2-pm_candidate1; paths_difference=pm_candidate2-pm_candidate1;
new_path_metrics[15]=(paths_difference<0) ? pm_candidate1 : pm_candidate2; new_path_metrics[15]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
trans_table[sample_nr][15] = paths_difference; trans_table[sample_nr][15] = paths_difference;
tmp=old_path_metrics; tmp=old_path_metrics;
old_path_metrics=new_path_metrics; old_path_metrics=new_path_metrics;
new_path_metrics=tmp; new_path_metrics=tmp;
sample_nr++; sample_nr++;
} }
/* /*
* Find the best from the stop states by comparing their path metrics. * Find the best from the stop states by comparing their path metrics.
* Not every stop state is always possible, so we are searching in * Not every stop state is always possible, so we are searching in
* a subset of them. * a subset of them.
*/ */
unsigned int best_stop_state; unsigned int best_stop_state;
float stop_state_metric, max_stop_state_metric; float stop_state_metric, max_stop_state_metric;
best_stop_state = stop_states[0]; best_stop_state = stop_states[0];
max_stop_state_metric = old_path_metrics[best_stop_state]; max_stop_state_metric = old_path_metrics[best_stop_state];
for(i=1; i< stops_num; i++){ for(i=1; i< stops_num; i++){
stop_state_metric = old_path_metrics[stop_states[i]]; stop_state_metric = old_path_metrics[stop_states[i]];
if(stop_state_metric > max_stop_state_metric){ if(stop_state_metric > max_stop_state_metric){
max_stop_state_metric = stop_state_metric; max_stop_state_metric = stop_state_metric;
best_stop_state = stop_states[i]; best_stop_state = stop_states[i];
} }
} }
/* /*
* This table was generated with hope that it gives a litle speedup during * This table was generated with hope that it gives a litle speedup during
* traceback stage. * traceback stage.
* Received bit is related to the number of state in the trellis. * Received bit is related to the number of state in the trellis.
* I've numbered states so their parity (number of ones) is related * I've numbered states so their parity (number of ones) is related
* to a received bit. * to a received bit.
*/ */
static const unsigned int parity_table[PATHS_NUM] = { 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, }; static const unsigned int parity_table[PATHS_NUM] = { 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, };
/* /*
* Table of previous states in the trellis diagram. * Table of previous states in the trellis diagram.
* For GMSK modulation every state has two previous states. * For GMSK modulation every state has two previous states.
* Example: * Example:
* previous_state_nr1 = prev_table[current_state_nr][0] * previous_state_nr1 = prev_table[current_state_nr][0]
* previous_state_nr2 = prev_table[current_state_nr][1] * previous_state_nr2 = prev_table[current_state_nr][1]
*/ */
static const unsigned int prev_table[PATHS_NUM][2] = { {0,8}, {0,8}, {1,9}, {1,9}, {2,10}, {2,10}, {3,11}, {3,11}, {4,12}, {4,12}, {5,13}, {5,13}, {6,14}, {6,14}, {7,15}, {7,15}, }; static const unsigned int prev_table[PATHS_NUM][2] = { {0,8}, {0,8}, {1,9}, {1,9}, {2,10}, {2,10}, {3,11}, {3,11}, {4,12}, {4,12}, {5,13}, {5,13}, {6,14}, {6,14}, {7,15}, {7,15}, };
/* /*
* Traceback and differential decoding of received sequence. * Traceback and differential decoding of received sequence.
* Decisions stored in trans_table are used to restore best path in the trellis. * Decisions stored in trans_table are used to restore best path in the trellis.
*/ */
sample_nr=samples_num; sample_nr=samples_num;
unsigned int state_nr=best_stop_state; unsigned int state_nr=best_stop_state;
unsigned int decision; unsigned int decision;
bool out_bit=0; bool out_bit=0;
while(sample_nr>0){ while(sample_nr>0){
sample_nr--; sample_nr--;
decision = (trans_table[sample_nr][state_nr]>0); decision = (trans_table[sample_nr][state_nr]>0);
if(decision != out_bit) if(decision != out_bit)
output[sample_nr]=-trans_table[sample_nr][state_nr]; output[sample_nr]=-trans_table[sample_nr][state_nr];
else else
output[sample_nr]=trans_table[sample_nr][state_nr]; output[sample_nr]=trans_table[sample_nr][state_nr];
out_bit = out_bit ^ real_imag ^ parity_table[state_nr]; out_bit = out_bit ^ real_imag ^ parity_table[state_nr];
state_nr = prev_table[state_nr][decision]; state_nr = prev_table[state_nr][decision];
real_imag = !real_imag; real_imag = !real_imag;
} }
} }

View File

@@ -1,64 +1,64 @@
/* -*- c++ -*- */ /* -*- c++ -*- */
/* /*
* @file * @file
* @author (C) 2009 Piotr Krysik <ptrkrysik@gmail.com> * @author (C) 2009 Piotr Krysik <ptrkrysik@gmail.com>
* @section LICENSE * @section LICENSE
* *
* Gr-gsm is free software; you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3, or (at your option) * the Free Software Foundation; either version 3, or (at your option)
* any later version. * any later version.
* *
* Gr-gsm is distributed in the hope that it will be useful, * Gr-gsm is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with gr-gsm; see the file COPYING. If not, write to * along with gr-gsm; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, * the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA. * Boston, MA 02110-1301, USA.
*/ */
/* /*
* viterbi_detector: * viterbi_detector:
* This part does the detection of received sequnece. * This part does the detection of received sequnece.
* Employed algorithm is viterbi Maximum Likehood Sequence Estimation. * Employed algorithm is viterbi Maximum Likehood Sequence Estimation.
* At this moment it gives hard decisions on the output, but * At this moment it gives hard decisions on the output, but
* it was designed with soft decisions in mind. * it was designed with soft decisions in mind.
* *
* SYNTAX: void viterbi_detector( * SYNTAX: void viterbi_detector(
* const gr_complex * input, * const gr_complex * input,
* unsigned int samples_num, * unsigned int samples_num,
* gr_complex * rhh, * gr_complex * rhh,
* unsigned int start_state, * unsigned int start_state,
* const unsigned int * stop_states, * const unsigned int * stop_states,
* unsigned int stops_num, * unsigned int stops_num,
* float * output) * float * output)
* *
* INPUT: input: Complex received signal afted matched filtering. * INPUT: input: Complex received signal afted matched filtering.
* samples_num: Number of samples in the input table. * samples_num: Number of samples in the input table.
* rhh: The autocorrelation of the estimated channel * rhh: The autocorrelation of the estimated channel
* impulse response. * impulse response.
* start_state: Number of the start point. In GSM each burst * start_state: Number of the start point. In GSM each burst
* starts with sequence of three bits (0,0,0) which * starts with sequence of three bits (0,0,0) which
* indicates start point of the algorithm. * indicates start point of the algorithm.
* stop_states: Table with numbers of possible stop states. * stop_states: Table with numbers of possible stop states.
* stops_num: Number of possible stop states * stops_num: Number of possible stop states
* *
* *
* OUTPUT: output: Differentially decoded hard output of the algorithm: * OUTPUT: output: Differentially decoded hard output of the algorithm:
* -1 for logical "0" and 1 for logical "1" * -1 for logical "0" and 1 for logical "1"
* *
* SUB_FUNC: none * SUB_FUNC: none
* *
* TEST(S): Tested with real world normal burst. * TEST(S): Tested with real world normal burst.
*/ */
#ifndef INCLUDED_VITERBI_DETECTOR_H #ifndef INCLUDED_VITERBI_DETECTOR_H
#define INCLUDED_VITERBI_DETECTOR_H #define INCLUDED_VITERBI_DETECTOR_H
#include "constants.h" #include "constants.h"
void viterbi_detector(const gr_complex * input, unsigned int samples_num, gr_complex * rhh, unsigned int start_state, const unsigned int * stop_states, unsigned int stops_num, float * output); void viterbi_detector(const gr_complex * input, unsigned int samples_num, gr_complex * rhh, unsigned int start_state, const unsigned int * stop_states, unsigned int stops_num, float * output);
#endif /* INCLUDED_VITERBI_DETECTOR_H */ #endif /* INCLUDED_VITERBI_DETECTOR_H */

View File

@@ -27,66 +27,33 @@
#include <sys/eventfd.h> #include <sys/eventfd.h>
#include <unistd.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 namespace spsc_detail
{ {
template <bool block_read, bool block_write> template <bool block_read, bool block_write> class spsc_cond_detail {
class spsc_cond_timeout_detail {
std::condition_variable cond_r, cond_w; std::condition_variable cond_r, cond_w;
std::mutex lr, lw; std::mutex l;
std::atomic_int r_flag, w_flag;
const int timeout_ms = 200;
public: public:
explicit spsc_cond_timeout_detail() : r_flag(0), w_flag(0) explicit spsc_cond_detail()
{
}
~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)
{ {
} }
@@ -96,37 +63,28 @@ class spsc_cond_detail {
ssize_t spsc_check_r() ssize_t spsc_check_r()
{ {
std::unique_lock<std::mutex> lk(lr); std::unique_lock<std::mutex> lk(l);
while (r_flag == 0) cond_r.wait(lk);
cond_r.wait(lk);
r_flag--;
return 1; return 1;
} }
ssize_t spsc_check_w() ssize_t spsc_check_w()
{ {
std::unique_lock<std::mutex> lk(lw); std::unique_lock<std::mutex> lk(l);
while (w_flag == 0) cond_w.wait(lk);
cond_w.wait(lk);
w_flag--;
return 1; return 1;
} }
void spsc_notify_r() void spsc_notify_r()
{ {
std::unique_lock<std::mutex> lk(lr);
r_flag++;
cond_r.notify_one(); cond_r.notify_one();
} }
void spsc_notify_w() void spsc_notify_w()
{ {
std::unique_lock<std::mutex> lk(lw);
w_flag++;
cond_w.notify_one(); cond_w.notify_one();
} }
}; };
// originally designed for select loop integration // originally designed for select loop integration
template <bool block_read, bool block_write> template <bool block_read, bool block_write> class spsc_efd_detail {
class spsc_efd_detail {
int efd_r, efd_w; /* eventfds used to block/notify readers/writers */ int efd_r, efd_w; /* eventfds used to block/notify readers/writers */
public: public:
@@ -193,7 +151,7 @@ class spsc : public T<block_read, block_write> {
/*! Adds element to the queue by copying the data. /*! Adds element to the queue by copying the data.
* \param[in] elem input buffer, must match the originally configured queue buffer size!. * \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 */ * \returns true if queue was not full and element was successfully pushed */
bool spsc_push(const ELEM *elem) bool spsc_push(ELEM *elem)
{ {
size_t cur_wp, cur_rp; size_t cur_wp, cur_rp;
cur_wp = writeptr.load(std::memory_order_relaxed); cur_wp = writeptr.load(std::memory_order_relaxed);
@@ -243,7 +201,4 @@ class spsc : public T<block_read, block_write> {
template <unsigned int SZ, typename ELEM, bool block_read, bool block_write> template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
class spsc_evfd : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_efd_detail> {}; 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> template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
class spsc_cond : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_detail> {}; class spsc_cond : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_detail> {};
template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
class spsc_cond_timeout
: public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_timeout_detail> {};

124
Transceiver52M/l1if.cpp Normal file
View File

@@ -0,0 +1,124 @@
#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;}
}

74
Transceiver52M/l1if.h Normal file
View File

@@ -0,0 +1,74 @@
#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,4 +1,5 @@
#pragma once #pragma once
/* /*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved * All Rights Reserved
@@ -38,18 +39,17 @@ const size_t BLADE_NUM_BUFFERS = 32 * 1;
const size_t NUM_TRANSFERS = 16 * 2; const size_t NUM_TRANSFERS = 16 * 2;
const int SAMPLE_SCALE_FACTOR = 15; // actually 16 but sigproc complains about clipping.. const int SAMPLE_SCALE_FACTOR = 15; // actually 16 but sigproc complains about clipping..
// see https://en.cppreference.com/w/cpp/language/parameter_pack "Brace-enclosed initializers" example template <typename Arg, typename... Args> void doPrint(std::ostream &out, Arg &&arg, Args &&...args)
template <typename Arg, typename... Args>
void expand_args(std::ostream &out, Arg &&arg, Args &&...args)
{ {
out << '(' << std::forward<Arg>(arg); out << '(' << std::forward<Arg>(arg);
(void)(int[]){ 0, (void((out << "," << std::forward<Args>(args))), 0)... }; using expander = int[];
(void)expander{ 0, (void(out << ',' << std::forward<Args>(args)), 0)... };
out << ')' << std::endl; out << ')' << std::endl;
} }
template <class R, class... Args> template <class R, class... Args> using RvalFunc = R (*)(Args...);
using RvalFunc = R (*)(Args...);
// specialisation for funcs which return a value
template <class R, class... Args> template <class R, class... Args>
R exec_and_check(RvalFunc<R, Args...> func, const char *fname, const char *finame, const char *funcname, int line, R exec_and_check(RvalFunc<R, Args...> func, const char *fname, const char *finame, const char *funcname, int line,
Args... args) Args... args)
@@ -58,7 +58,7 @@ R exec_and_check(RvalFunc<R, Args...> func, const char *fname, const char *finam
if (rval != 0) { if (rval != 0) {
std::cerr << ((rval >= 0) ? "OK:" : bladerf_strerror(rval)) << ':' << finame << ':' << line << ':' std::cerr << ((rval >= 0) ? "OK:" : bladerf_strerror(rval)) << ':' << finame << ':' << line << ':'
<< funcname << ':' << fname; << funcname << ':' << fname;
expand_args(std::cerr, args...); doPrint(std::cerr, args...);
} }
return rval; return rval;
} }
@@ -69,8 +69,7 @@ R exec_and_check(RvalFunc<R, Args...> func, const char *fname, const char *finam
#pragma pack(push, 1) #pragma pack(push, 1)
using blade_sample_type = std::complex<int16_t>; using blade_sample_type = std::complex<int16_t>;
enum class blade_speed_buffer_type { HS, SS }; enum class blade_speed_buffer_type { HS, SS };
template <blade_speed_buffer_type T> template <blade_speed_buffer_type T> struct blade_usb_message {
struct blade_usb_message {
uint32_t reserved; uint32_t reserved;
uint64_t ts; uint64_t ts;
uint32_t meta_flags; uint32_t meta_flags;
@@ -79,8 +78,7 @@ 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::SS>) == 2048, "blade buffer mismatch!");
static_assert(sizeof(blade_usb_message<blade_speed_buffer_type::HS>) == 1024, "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> template <unsigned int SZ, blade_speed_buffer_type T> struct blade_otw_buffer {
struct blade_otw_buffer {
static_assert((SZ >= 2 && !(SZ % 2)), "min size is 2x usb buffer!"); static_assert((SZ >= 2 && !(SZ % 2)), "min size is 2x usb buffer!");
blade_usb_message<T> m[SZ]; blade_usb_message<T> m[SZ];
int actual_samples_per_msg() int actual_samples_per_msg()
@@ -112,7 +110,7 @@ struct blade_otw_buffer {
int readall(blade_sample_type *outaddr) int readall(blade_sample_type *outaddr)
{ {
blade_sample_type *addr = outaddr; blade_sample_type *addr = outaddr;
for (unsigned int i = 0; i < SZ; i++) { for (int i = 0; i < SZ; i++) {
memcpy(addr, &m[i].d[0], actual_samples_per_msg() * sizeof(blade_sample_type)); memcpy(addr, &m[i].d[0], actual_samples_per_msg() * sizeof(blade_sample_type));
addr += actual_samples_per_msg(); addr += actual_samples_per_msg();
} }
@@ -160,7 +158,7 @@ struct blade_otw_buffer {
{ {
assert(num <= actual_samples_per_buffer()); assert(num <= actual_samples_per_buffer());
int len_rem = num; int len_rem = num;
for (unsigned int i = 0; i < SZ; i++) { for (int i = 0; i < SZ; i++) {
m[i] = {}; m[i] = {};
m[i].ts = first_ts + i * actual_samples_per_msg(); m[i].ts = first_ts + i * actual_samples_per_msg();
if (len_rem) { if (len_rem) {
@@ -176,8 +174,7 @@ struct blade_otw_buffer {
}; };
#pragma pack(pop) #pragma pack(pop)
template <unsigned int SZ, blade_speed_buffer_type T> template <unsigned int SZ, blade_speed_buffer_type T> struct blade_otw_buffer_helper {
struct blade_otw_buffer_helper {
static_assert((SZ >= 1024 && ((SZ & (SZ - 1)) == 0)), "only buffer size multiples of 1024 allowed!"); static_assert((SZ >= 1024 && ((SZ & (SZ - 1)) == 0)), "only buffer size multiples of 1024 allowed!");
static blade_otw_buffer<SZ / 512, T> x; static blade_otw_buffer<SZ / 512, T> x;
}; };
@@ -186,19 +183,16 @@ 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 buf_in_use = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
using bh_fn_t = std::function<int(dev_buf_t *)>; using bh_fn_t = std::function<int(dev_buf_t *)>;
template <typename T> template <typename T> struct blade_hw {
struct blade_hw {
struct bladerf *dev; struct bladerf *dev;
struct bladerf_stream *rx_stream; struct bladerf_stream *rx_stream;
struct bladerf_stream *tx_stream; struct bladerf_stream *tx_stream;
// using pkt2buf = blade_otw_buffer<2, blade_speed_buffer_type::SS>; // using pkt2buf = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
using tx_buf_q_type = spsc_cond_timeout<BLADE_NUM_BUFFERS, dev_buf_t *, true, false>; using tx_buf_q_type = spsc_cond<BLADE_NUM_BUFFERS, dev_buf_t *, true, false>;
const unsigned int rxFullScale, txFullScale; const unsigned int rxFullScale, txFullScale;
const int rxtxdelay; const int rxtxdelay;
float rxgain, txgain; float rxgain, txgain;
static std::atomic<bool> stop_lower_threads_flag;
double rxfreq_cache, txfreq_cache;
struct ms_trx_config { struct ms_trx_config {
int tx_freq; int tx_freq;
@@ -223,23 +217,21 @@ struct blade_hw {
{ {
close_device(); close_device();
} }
blade_hw() blade_hw() : rxFullScale(2047), txFullScale(2047), rxtxdelay(-60)
: rxFullScale(2047), txFullScale(2047), rxtxdelay(-60), rxgain(30), txgain(30), rxfreq_cache(0),
txfreq_cache(0)
{ {
} }
void close_device() void close_device()
{ {
if (dev) { if (dev) {
if (tx_stream) {
bladerf_deinit_stream(tx_stream);
}
if (rx_stream) { if (rx_stream) {
bladerf_deinit_stream(rx_stream); bladerf_deinit_stream(rx_stream);
} }
if (tx_stream) {
bladerf_deinit_stream(tx_stream);
}
bladerf_enable_module(dev, BLADERF_MODULE_RX, false); bladerf_enable_module(dev, BLADERF_MODULE_RX, false);
bladerf_enable_module(dev, BLADERF_MODULE_TX, false); bladerf_enable_module(dev, BLADERF_MODULE_TX, false);
@@ -254,7 +246,6 @@ struct blade_hw {
bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_DEBUG); bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_DEBUG);
bladerf_set_usb_reset_on_open(true); bladerf_set_usb_reset_on_open(true);
blade_check(bladerf_open, &dev, ""); blade_check(bladerf_open, &dev, "");
if (!dev) { if (!dev) {
std::cerr << "open failed, device missing?" << std::endl; std::cerr << "open failed, device missing?" << std::endl;
@@ -269,8 +260,7 @@ struct blade_hw {
bool is_locked; bool is_locked;
blade_check(bladerf_set_pll_enable, dev, true); blade_check(bladerf_set_pll_enable, dev, true);
uint64_t refclock = 10000000UL; blade_check(bladerf_set_pll_refclk, dev, 10000000UL);
blade_check(bladerf_set_pll_refclk, dev, refclock);
for (int i = 0; i < 20; i++) { for (int i = 0; i < 20; i++) {
usleep(50 * 1000); usleep(50 * 1000);
bladerf_get_pll_lock_state(dev, &is_locked); bladerf_get_pll_lock_state(dev, &is_locked);
@@ -283,6 +273,15 @@ struct blade_hw {
return -1; 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_RX(0), &rate, &actual);
blade_check(bladerf_set_rational_sample_rate, dev, BLADERF_CHANNEL_TX(0), &rate, &actual); blade_check(bladerf_set_rational_sample_rate, dev, BLADERF_CHANNEL_TX(0), &rate, &actual);
@@ -295,51 +294,46 @@ struct blade_hw {
(bladerf_bandwidth *)NULL); (bladerf_bandwidth *)NULL);
blade_check(bladerf_set_gain_mode, dev, BLADERF_CHANNEL_RX(0), BLADERF_GAIN_MGC); blade_check(bladerf_set_gain_mode, dev, BLADERF_CHANNEL_RX(0), BLADERF_GAIN_MGC);
setRxGain(rxgain, 0); // blade_check(bladerf_set_gain, dev, BLADERF_CHANNEL_RX(0), (bladerf_gain)30);
setTxGain(txgain, 0); // 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);
usleep(1000); 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, 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); 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, 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); BLADERF_FORMAT_SC16_Q11_META, BLADE_BUFFER_SIZE, NUM_TRANSFERS, (void *)this);
for (unsigned int i = 0; i < BLADE_NUM_BUFFERS; i++) { for (int i = 0; i < BLADE_NUM_BUFFERS; i++) {
auto cur_buffer = reinterpret_cast<tx_buf_q_type::elem_t *>(buf_mgmt.tx_samples); auto cur_buffer = reinterpret_cast<tx_buf_q_type::elem_t *>(buf_mgmt.tx_samples);
buf_mgmt.bufptrqueue.spsc_push(&cur_buffer[i]); buf_mgmt.bufptrqueue.spsc_push(&cur_buffer[i]);
} }
return 0; setRxGain(20);
} setTxGain(30);
void actually_enable_streams()
{
blade_check(bladerf_enable_module, dev, BLADERF_MODULE_RX, true);
usleep(1000); usleep(1000);
blade_check(bladerf_enable_module, dev, BLADERF_MODULE_TX, true);
// bladerf_set_stream_timeout(dev, BLADERF_TX, 4);
// bladerf_set_stream_timeout(dev, BLADERF_RX, 4);
return 0;
} }
bool tuneTx(double freq, size_t chan = 0) bool tuneTx(double freq, size_t chan = 0)
{ {
if (txfreq_cache == freq)
return true;
msleep(15); msleep(15);
blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_TX(0), (bladerf_frequency)freq); blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_TX(0), (bladerf_frequency)freq);
txfreq_cache = freq;
msleep(15); msleep(15);
return true; return true;
}; };
bool tuneRx(double freq, size_t chan = 0) bool tuneRx(double freq, size_t chan = 0)
{ {
if (rxfreq_cache == freq)
return true;
msleep(15); msleep(15);
blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_RX(0), (bladerf_frequency)freq); blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_RX(0), (bladerf_frequency)freq);
rxfreq_cache = freq;
msleep(15); msleep(15);
return true; return true;
}; };
@@ -395,9 +389,6 @@ struct blade_hw {
static int to_skip = 0; static int to_skip = 0;
dev_buf_t *rcd = (dev_buf_t *)samples; 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 if (to_skip < 120) // prevents weird overflows on startup
to_skip++; to_skip++;
else { else {
@@ -420,60 +411,72 @@ struct blade_hw {
if (samples) // put buffer address back into queue, ready to be reused if (samples) // put buffer address back into queue, ready to be reused
trx->buf_mgmt.bufptrqueue.spsc_push(&ptr); trx->buf_mgmt.bufptrqueue.spsc_push(&ptr);
if (stop_lower_threads_flag)
return BLADERF_STREAM_SHUTDOWN;
return BLADERF_STREAM_NO_DATA; return BLADERF_STREAM_NO_DATA;
}; };
} }
auto get_rx_burst_handler_fn(bh_fn_t burst_handler) auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
{ {
using thist = decltype(this); auto fn = [this] {
auto fn = [](void *args) -> void * { int status;
thist t = reinterpret_cast<thist>(args); set_name_aff_sched("rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2);
int status = 0; status = bladerf_stream(rx_stream, BLADERF_RX_X1);
if (!stop_lower_threads_flag)
status = bladerf_stream(t->rx_stream, BLADERF_RX_X1);
if (status < 0) if (status < 0)
std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl; std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
return 0; return NULL;
}; };
return fn; return fn;
} }
auto get_tx_burst_handler_fn(bh_fn_t burst_handler) auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
{ {
using thist = decltype(this); auto fn = [this] {
auto fn = [](void *args) -> void * { int status;
thist t = reinterpret_cast<thist>(args); set_name_aff_sched("txrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1);
int status = 0; status = bladerf_stream(tx_stream, BLADERF_TX_X1);
if (!stop_lower_threads_flag)
status = bladerf_stream(t->tx_stream, BLADERF_TX_X1);
if (status < 0) if (status < 0)
std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl; std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
return 0; return NULL;
}; };
return fn; return fn;
} }
void submit_burst_ts(blade_sample_type *buffer, int len, uint64_t ts) 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; 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)) while (!buf_mgmt.bufptrqueue.spsc_pop(&rcd))
buf_mgmt.bufptrqueue.spsc_prep_pop(); buf_mgmt.bufptrqueue.spsc_prep_pop();
assert(rcd != nullptr); assert(rcd != nullptr);
rcd->write_n_burst(buffer, len, ts + rxtxdelay); // blade xa4 specific delay! 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); 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

@@ -0,0 +1,259 @@
#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);
// }
}
};

473
Transceiver52M/ms/ipcif.c Normal file
View File

@@ -0,0 +1,473 @@
/*
* 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

@@ -1,275 +0,0 @@
/*
* 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

@@ -1,71 +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/>.
*
*/
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

@@ -1,160 +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 "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

@@ -0,0 +1,227 @@
/*
* (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

@@ -0,0 +1,211 @@
/*
* (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

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

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

View File

@@ -0,0 +1,301 @@
/*
* (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

@@ -0,0 +1,111 @@
#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

@@ -1,78 +0,0 @@
/*
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <atomic>
#include "ms_trxcon_if.h"
extern "C" {
#include <osmocom/bb/trxcon/trxcon.h>
#include <osmocom/bb/trxcon/l1ctl_server.h>
#include <osmocom/core/panic.h>
}
extern tx_queue_t txq;
extern cmd_queue_t cmdq_to_phy;
extern cmdr_queue_t cmdq_from_phy;
extern std::atomic<bool> g_exit_flag;
// trxcon C call(back) if
extern "C" {
int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon_phyif_burst_req *br)
{
if (br->burst_len == 0) // dummy/nope
return 0;
OSMO_ASSERT(br->burst != 0);
internal_q_tx_buf b(br);
if (!g_exit_flag)
txq.spsc_push(&b);
return 0;
}
int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon_phyif_cmd *cmd)
{
#ifdef TXDEBUG
DBGLG() << "TOP C: " << cmd2str(cmd->type) << std::endl;
#endif
if (!g_exit_flag)
cmdq_to_phy.spsc_push(cmd);
// q for resp polling happens in main loop
return 0;
}
void trxcon_phyif_close(void *phyif)
{
}
void trxcon_l1ctl_close(struct trxcon_inst *trxcon)
{
/* Avoid use-after-free: both *fi and *trxcon are children of
* the L2IF (L1CTL connection), so we need to re-parent *fi
* to NULL before calling l1ctl_client_conn_close(). */
talloc_steal(NULL, trxcon->fi);
l1ctl_client_conn_close((struct l1ctl_client *)trxcon->l2if);
}
int trxcon_l1ctl_send(struct trxcon_inst *trxcon, struct msgb *msg)
{
struct l1ctl_client *l1c = (struct l1ctl_client *)trxcon->l2if;
return l1ctl_client_send(l1c, msg);
}
}

View File

@@ -1,42 +0,0 @@
#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

@@ -1,512 +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 <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

@@ -1,48 +0,0 @@
#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

@@ -0,0 +1,337 @@
/*
* (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

@@ -1,4 +1,5 @@
#pragma once #pragma once
/* /*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved * All Rights Reserved
@@ -26,7 +27,7 @@
#include <cstdint> #include <cstdint>
#include <mutex> #include <mutex>
#include <iostream> #include <iostream>
// #include <thread> #include <thread>
#if defined(BUILDBLADE) #if defined(BUILDBLADE)
#include "bladerf_specific.h" #include "bladerf_specific.h"
@@ -34,92 +35,37 @@
#elif defined(BUILDUHD) #elif defined(BUILDUHD)
#include "uhd_specific.h" #include "uhd_specific.h"
#define BASET uhd_hw<ms_trx> #define BASET uhd_hw<ms_trx>
#elif defined(BUILDIPC)
#include "ipc_specific.h"
#define BASET ipc_hw<ms_trx>
#else #else
#error wat? no device.. #error wat? no device..
#endif #endif
#include "Complex.h"
#include "GSMCommon.h" #include "GSMCommon.h"
#include "itrq.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 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*/); const unsigned int SCH_LEN_SPS = (ONE_TS_BURST_LEN * 8 /*ts*/ * 12 /*frames*/);
template <typename T> template <typename T> void clamp_array(T *start2, unsigned int len, T max)
void clamp_array(T *start2, unsigned int len, T max)
{ {
for (unsigned int i = 0; i < len; i++) { for (int i = 0; i < len; i++) {
const T t1 = start2[i] < -max ? -max : start2[i]; const T t1 = start2[i] < -max ? -max : start2[i];
const T t2 = t1 > max ? max : t1; const T t2 = t1 > max ? max : t1;
start2[i] = t2; start2[i] = t2;
} }
} }
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++)
dst[i] = static_cast<float>(src[i]) * scale;
}
template <typename DST_T, typename ST>
void convert_and_scale_i(DST_T *dst, const float *src, unsigned int src_len, ST scale)
{
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> 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) void convert_and_scale(void *dst, void *src, unsigned int src_len, ST scale)
{ {
using vd = typename cvt_internal::is_complex<DST_T>::baset; for (unsigned int i = 0; i < src_len; i++)
using vs = typename cvt_internal::is_complex<SRC_T>::baset; reinterpret_cast<DST_T *>(dst)[i] = static_cast<DST_T>((reinterpret_cast<SRC_T *>(src)[i])) * scale;
return cvt_internal::convert_and_scale_i((vd *)dst, (vs *)src, src_len, scale);
} }
template <typename DST_T, typename SRC_T> void convert_and_scale_default(void *dst, void *src, unsigned int src_len)
template <typename array_t>
float normed_abs_sum(array_t *src, int len)
{ {
using vd = typename cvt_internal::is_complex<array_t>::baset; return convert_and_scale<DST_T, SRC_T>(dst, src, src_len, SAMPLE_SCALE_FACTOR);
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 { struct one_burst {
@@ -129,11 +75,11 @@ struct one_burst {
GSM::Time gsmts; GSM::Time gsmts;
union { union {
blade_sample_type burst[ONE_TS_BURST_LEN]; blade_sample_type burst[ONE_TS_BURST_LEN];
char sch_bits[148]; unsigned char sch_bits[148];
}; };
}; };
using rx_queue_t = spsc_cond_timeout<4, one_burst, true, false>; using rx_queue_t = spsc_cond<8 * NUM_RXQ_FRAMES, one_burst, true, true>;
enum class SCH_STATE { SEARCHING, FOUND }; enum class SCH_STATE { SEARCHING, FOUND };
@@ -219,21 +165,7 @@ class time_keeper {
using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>; using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>;
// used to globally initialize the sched/hw information struct ms_trx : public BASET {
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; using base = BASET;
static dummylog dummy_log; static dummylog dummy_log;
unsigned int mTSC; unsigned int mTSC;
@@ -241,12 +173,15 @@ struct ms_trx : public BASET, public sched_hw_info {
int timing_advance; int timing_advance;
bool do_auto_gain; bool do_auto_gain;
pthread_t lower_rx_task; std::thread rx_task;
pthread_t lower_tx_task; std::thread tx_task;
std::thread *calcrval_task;
// provides bursts to upper rx thread // provides bursts to upper rx thread
rx_queue_t rxqueue; rx_queue_t rxqueue;
#ifdef SYNCTHINGONLY
ts_hitter_q_t ts_hitter_q;
#endif
blade_sample_type *first_sch_buf; blade_sample_type *first_sch_buf;
blade_sample_type *burst_copy_buffer; blade_sample_type *burst_copy_buffer;
@@ -258,29 +193,28 @@ struct ms_trx : public BASET, public sched_hw_info {
int64_t first_sch_ts_start = -1; int64_t first_sch_ts_start = -1;
time_keeper timekeeper; time_keeper timekeeper;
single_thread_pool worker_thread; // uses base class sched target hw info
void start_lower_ms(); void start();
std::atomic<bool> upper_is_ready; std::atomic<bool> upper_is_ready;
void set_upper_ready(bool is_ready); void set_upper_ready(bool is_ready);
bool handle_sch_or_nb(); bool handle_sch_or_nb();
bool handle_sch(bool first = false); bool handle_sch(bool first = false);
bool decode_sch(char *bits, bool update_global_clock); bool decode_sch(float *bits, bool update_global_clock);
SCH_STATE search_for_sch(dev_buf_t *rcd); SCH_STATE search_for_sch(dev_buf_t *rcd);
void grab_bursts(dev_buf_t *rcd); void grab_bursts(dev_buf_t *rcd);
int init_dev_and_streams(); int init_device();
int init_streams(void *rx_cb, void *tx_cb);
int init_dev_and_streams(void *rx_cb, void *tx_cb);
void stop_threads(); void stop_threads();
void *rx_cb(ms_trx *t); void *rx_cb(ms_trx *t);
void *tx_cb(); void *tx_cb();
void maybe_update_gain(one_burst &brst); void maybe_update_gain(one_burst &brst);
ms_trx() ms_trx()
: mTSC(0), mBSIC(0), timing_advance(0), do_auto_gain(false), rxqueue(), : timing_advance(0), do_auto_gain(false), rxqueue(), first_sch_buf(new blade_sample_type[SCH_LEN_SPS]),
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 }
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,95 +0,0 @@
#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

@@ -1,104 +0,0 @@
/*
* (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

@@ -1,68 +0,0 @@
#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,4 +1,5 @@
#pragma once #pragma once
/* /*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved * All Rights Reserved
@@ -65,8 +66,7 @@ struct uhd_buf_wrap {
using dev_buf_t = uhd_buf_wrap; using dev_buf_t = uhd_buf_wrap;
using bh_fn_t = std::function<int(dev_buf_t *)>; using bh_fn_t = std::function<int(dev_buf_t *)>;
template <typename T> template <typename T> struct uhd_hw {
struct uhd_hw {
uhd::usrp::multi_usrp::sptr dev; uhd::usrp::multi_usrp::sptr dev;
uhd::rx_streamer::sptr rx_stream; uhd::rx_streamer::sptr rx_stream;
uhd::tx_streamer::sptr tx_stream; uhd::tx_streamer::sptr tx_stream;
@@ -77,38 +77,26 @@ struct uhd_hw {
const unsigned int rxFullScale, txFullScale; const unsigned int rxFullScale, txFullScale;
const int rxtxdelay; const int rxtxdelay;
float rxgain, txgain; float rxgain, txgain;
static std::atomic<bool> stop_lower_threads_flag;
double rxfreq_cache, txfreq_cache;
virtual ~uhd_hw() virtual ~uhd_hw()
{ {
delete[] one_pkt_buf; delete[] one_pkt_buf;
} }
uhd_hw() : rxFullScale(32767), txFullScale(32767 * 0.3), rxtxdelay(-67), rxfreq_cache(0), txfreq_cache(0) uhd_hw() : rxFullScale(32767), txFullScale(32767), rxtxdelay(-67)
{
}
void close_device()
{ {
} }
bool tuneTx(double freq, size_t chan = 0) bool tuneTx(double freq, size_t chan = 0)
{ {
if (txfreq_cache == freq)
return true;
msleep(25); msleep(25);
dev->set_tx_freq(freq, chan); dev->set_tx_freq(freq, chan);
txfreq_cache = freq;
msleep(25); msleep(25);
return true; return true;
}; };
bool tuneRx(double freq, size_t chan = 0) bool tuneRx(double freq, size_t chan = 0)
{ {
if (rxfreq_cache == freq)
return true;
msleep(25); msleep(25);
dev->set_rx_freq(freq, chan); dev->set_rx_freq(freq, chan);
rxfreq_cache = freq;
msleep(25); msleep(25);
return true; return true;
}; };
@@ -141,7 +129,6 @@ struct uhd_hw {
int init_device(bh_fn_t rxh, bh_fn_t txh) int init_device(bh_fn_t rxh, bh_fn_t txh)
{ {
auto const lock_delay_ms = 500; auto const lock_delay_ms = 500;
auto clock_lock_attempts = 15; // x lock_delay_ms
auto const mcr = 26e6; auto const mcr = 26e6;
auto const rate = (1625e3 / 6) * 4; auto const rate = (1625e3 / 6) * 4;
auto const ref = "external"; auto const ref = "external";
@@ -149,8 +136,7 @@ struct uhd_hw {
auto const freq = 931.4e6; // 936.8e6 auto const freq = 931.4e6; // 936.8e6
auto bw = 0.5e6; auto bw = 0.5e6;
auto const channel = 0; auto const channel = 0;
// aligned to blade: 1020 samples per transfer std::string args = {};
std::string args = { "recv_frame_size=4092,send_frame_size=4092" };
dev = uhd::usrp::multi_usrp::make(args); dev = uhd::usrp::multi_usrp::make(args);
std::cout << "Using Device: " << dev->get_pp_string() << std::endl; std::cout << "Using Device: " << dev->get_pp_string() << std::endl;
@@ -166,18 +152,8 @@ struct uhd_hw {
dev->set_tx_bandwidth(bw, channel); dev->set_tx_bandwidth(bw, channel);
while (!(dev->get_rx_sensor("lo_locked", channel).to_bool() && 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)); 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"); uhd::stream_args_t stream_args("sc16", "sc16");
rx_stream = dev->get_rx_stream(stream_args); rx_stream = dev->get_rx_stream(stream_args);
@@ -192,26 +168,27 @@ struct uhd_hw {
return 0; return 0;
} }
void actually_enable_streams()
{
// nop: stream cmd in handler
}
void *rx_cb(bh_fn_t burst_handler) void *rx_cb(bh_fn_t burst_handler)
{ {
void *ret = nullptr; void *ret;
static int to_skip = 0; static int to_skip = 0;
uhd::rx_metadata_t md; uhd::rx_metadata_t md;
auto num_rx_samps = rx_stream->recv(pkt_ptrs.front(), rx_spp, md, 1.0, true); 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) { if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) {
std::cerr << boost::format("Timeout while streaming") << std::endl; std::cerr << boost::format("Timeout while streaming") << std::endl;
exit(0); exit(0);
} }
if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW) { if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW) {
std::cerr << boost::format("Got an overflow indication\n") << std::endl; 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); exit(0);
;
} }
if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) { if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) {
std::cerr << str(boost::format("Receiver error: %s") % md.strerror()); std::cerr << str(boost::format("Receiver error: %s") % md.strerror());
@@ -231,29 +208,24 @@ struct uhd_hw {
auto get_rx_burst_handler_fn(bh_fn_t burst_handler) auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
{ {
// C cb -> ghetto closure capture, which is fine, the args never change. auto fn = [this, burst_handler] {
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"); pthread_setname_np(pthread_self(), "rxrun");
uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS); uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
stream_cmd.stream_now = true; stream_cmd.stream_now = true;
stream_cmd.time_spec = uhd::time_spec_t(); stream_cmd.time_spec = uhd::time_spec_t();
rx_burst_cap_this->rx_stream->issue_stream_cmd(stream_cmd); rx_stream->issue_stream_cmd(stream_cmd);
while (!rx_burst_cap_this->stop_lower_threads_flag) { while (1) {
rx_burst_cap_this->rx_cb(rx_burst_cap_bh); rx_cb(burst_handler);
} }
return 0;
}; };
return fn; return fn;
} }
auto get_tx_burst_handler_fn(bh_fn_t burst_handler) auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
{ {
auto fn = [](void *args) -> void * { auto fn = [] {
// dummy // dummy
return 0;
}; };
return fn; return fn;
} }
@@ -266,14 +238,37 @@ struct uhd_hw {
m.time_spec = m.time_spec.from_ticks(ts + rxtxdelay, rxticks); // uhd specific b210 delay! m.time_spec = m.time_spec.from_ticks(ts + rxtxdelay, rxticks); // uhd specific b210 delay!
std::vector<void *> ptrs(1, buffer); std::vector<void *> ptrs(1, buffer);
tx_stream->send(ptrs, len, m, 1.0); tx_stream->send(ptrs, len, m);
#ifdef DBGXX
uhd::async_metadata_t async_md; uhd::async_metadata_t async_md;
bool tx_ack = false; bool tx_ack = false;
while (!tx_ack && tx_stream->recv_async_msg(async_md)) { while (!tx_ack && tx_stream->recv_async_msg(async_md)) {
tx_ack = (async_md.event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK); 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; std::cout << (tx_ack ? "yay" : "nay") << " " << async_md.time_spec.to_ticks(rxticks) << std::endl;
#endif }
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

@@ -77,24 +77,6 @@ static struct ctrl_handle *g_ctrlh;
static RadioDevice *usrp; static RadioDevice *usrp;
static RadioInterface *radio; 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 /* Create radio interface
* The interface consists of sample rate changes, frequency shifts, * The interface consists of sample rate changes, frequency shifts,
* channel multiplexing, and other conversions. The transceiver core * channel multiplexing, and other conversions. The transceiver core
@@ -109,17 +91,17 @@ RadioInterface *makeRadioInterface(struct trx_ctx *trx,
switch (type) { switch (type) {
case RadioDevice::NORMAL: case RadioDevice::NORMAL:
radio = new rif_va_wrapper<RadioInterface>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps, radio = new RadioInterface(usrp, trx->cfg.tx_sps,
trx->cfg.num_chans); trx->cfg.rx_sps, trx->cfg.num_chans);
break; break;
case RadioDevice::RESAMP_64M: case RadioDevice::RESAMP_64M:
case RadioDevice::RESAMP_100M: case RadioDevice::RESAMP_100M:
radio = new rif_va_wrapper<RadioInterfaceResamp>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps); trx->cfg.rx_sps);
break; break;
case RadioDevice::MULTI_ARFCN: case RadioDevice::MULTI_ARFCN:
radio = new rif_va_wrapper<RadioInterfaceMulti>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps, radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
trx->cfg.num_chans); trx->cfg.rx_sps, trx->cfg.num_chans);
break; break;
default: default:
LOG(ALERT) << "Unsupported radio interface configuration"; LOG(ALERT) << "Unsupported radio interface configuration";
@@ -483,12 +465,6 @@ int trx_validate_config(struct trx_ctx *trx)
return -1; 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; return 0;
} }
@@ -595,14 +571,24 @@ static void trx_stop()
static int trx_start(struct trx_ctx *trx) static int trx_start(struct trx_ctx *trx)
{ {
int type, chans; int type, chans;
unsigned int i;
std::vector<std::string> rx_paths, tx_paths;
RadioDevice::InterfaceType iface = RadioDevice::NORMAL; RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
/* Create the low level device object */ /* Create the low level device object */
if (trx->cfg.multi_arfcn) if (trx->cfg.multi_arfcn)
iface = RadioDevice::MULTI_ARFCN; iface = RadioDevice::MULTI_ARFCN;
usrp = RadioDevice::make(iface, &trx->cfg); /* Generate vector of rx/tx_path: */
type = usrp->open(); 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);
if (type < 0) { if (type < 0) {
LOG(ALERT) << "Failed to create radio device" << std::endl; LOG(ALERT) << "Failed to create radio device" << std::endl;
goto shutdown; goto shutdown;
@@ -645,7 +631,6 @@ int main(int argc, char *argv[])
osmo_init_logging2(tall_trx_ctx, &log_info); osmo_init_logging2(tall_trx_ctx, &log_info);
log_enable_multithread(); log_enable_multithread();
log_cache_enable();
osmo_stats_init(tall_trx_ctx); osmo_stats_init(tall_trx_ctx);
vty_init(&g_vty_info); vty_init(&g_vty_info);
logging_vty_add_cmds(); logging_vty_add_cmds();
@@ -666,11 +651,11 @@ int main(int argc, char *argv[])
exit(2); exit(2);
} }
rc = telnet_init_default(tall_trx_ctx, NULL, OSMO_VTY_PORT_TRX); rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX);
if (rc < 0) if (rc < 0)
exit(1); exit(1);
g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL); g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_TRX, NULL);
if (!g_ctrlh) { if (!g_ctrlh) {
LOG(ERROR) << "Failed to create CTRL interface.\n"; LOG(ERROR) << "Failed to create CTRL interface.\n";
exit(1); exit(1);

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,25 +1,3 @@
/*
* (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 <complex.h>
#include <stdio.h> #include <stdio.h>
#include <math.h> #include <math.h>
@@ -34,9 +12,6 @@
#include "sch.h" #include "sch.h"
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-variable"
/* GSM 04.08, 9.1.30 Synchronization channel information */ /* GSM 04.08, 9.1.30 Synchronization channel information */
struct sch_packed_info { struct sch_packed_info {
ubit_t t1_hi[2]; ubit_t t1_hi[2];
@@ -130,14 +105,6 @@ int gsm_sch_check_fn(int fn)
return 0; 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 */ /* SCH (T1, T2, T3p) to full FN value */
int gsm_sch_to_fn(struct sch_info *sch) int gsm_sch_to_fn(struct sch_info *sch)
{ {
@@ -185,7 +152,7 @@ int gsm_sch_parse(const uint8_t *info, struct sch_info *desc)
} }
/* From osmo-bts */ /* From osmo-bts */
int gsm_sch_decode(uint8_t *info, sbit_t *data) __attribute__((xray_always_instrument)) __attribute__((noinline)) int gsm_sch_decode(uint8_t *info, sbit_t *data)
{ {
int rc; int rc;
ubit_t uncoded[GSM_SCH_UNCODED_LEN]; ubit_t uncoded[GSM_SCH_UNCODED_LEN];
@@ -325,5 +292,3 @@ static __attribute__((constructor)) void init()
} }
} }
#pragma GCC diagnostic pop

27
Transceiver52M/sch.h Normal file
View File

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

View File

@@ -82,10 +82,10 @@ AC_TYPE_SIZE_T
AC_HEADER_TIME AC_HEADER_TIME
AC_C_BIGENDIAN AC_C_BIGENDIAN
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.10.0) PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.10.0) PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.10.0) PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.10.0) PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.6.0)
AC_ARG_ENABLE(sanitize, AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING( [AS_HELP_STRING(
@@ -143,11 +143,6 @@ AC_ARG_WITH(bladerf, [
[enable bladeRF]) [enable bladeRF])
]) ])
AC_ARG_WITH(mstrx, [
AS_HELP_STRING([--with-mstrx],
[enable MS TRX])
])
AC_ARG_WITH(singledb, [ AC_ARG_WITH(singledb, [
AS_HELP_STRING([--with-singledb], AS_HELP_STRING([--with-singledb],
[enable single daughterboard use on USRP1]) [enable single daughterboard use on USRP1])
@@ -195,39 +190,12 @@ AS_IF([test "x$with_uhd" = "xyes"],[
[PKG_CHECK_MODULES(UHD, uhd >= 003.005)] [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"], [ AS_IF([test "x$with_bladerf" = "xyes"], [
PKG_CHECK_MODULES(BLADE, libbladeRF >= 2.0) 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"], [ AS_IF([test "x$with_singledb" = "xyes"], [
AC_DEFINE(SINGLEDB, 1, Define to 1 for single daughterboard) AC_DEFINE(SINGLEDB, 1, Define to 1 for single daughterboard)
]) ])
@@ -240,6 +208,7 @@ AS_IF([test "x$with_sse" != "xno"], [
AM_CONDITIONAL(HAVE_SSE4_1, false) AM_CONDITIONAL(HAVE_SSE4_1, false)
]) ])
dnl Check if the compiler supports specified GCC's built-in function
AC_DEFUN([CHECK_BUILTIN_SUPPORT], [ AC_DEFUN([CHECK_BUILTIN_SUPPORT], [
AC_CACHE_CHECK( AC_CACHE_CHECK(
[whether ${CC} has $1 built-in], [whether ${CC} has $1 built-in],
@@ -283,7 +252,6 @@ AM_CONDITIONAL(DEVICE_IPC, [test "x$with_ipc" = "xyes"])
AM_CONDITIONAL(DEVICE_BLADE, [test "x$with_bladerf" = "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, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
AM_CONDITIONAL(ARCH_ARM_A15, [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(LIBUSB, libusb-1.0)
PKG_CHECK_MODULES(FFTWF, fftw3f) PKG_CHECK_MODULES(FFTWF, fftw3f)
@@ -351,10 +319,10 @@ AC_MSG_RESULT([CFLAGS="$CFLAGS"])
AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"]) AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"])
AC_MSG_RESULT([LDFLAGS="$LDFLAGS"]) AC_MSG_RESULT([LDFLAGS="$LDFLAGS"])
dnl Output files dnl Output files
AC_CONFIG_FILES([\ AC_CONFIG_FILES([\
Makefile \ Makefile \
trxcon/Makefile \
CommonLibs/Makefile \ CommonLibs/Makefile \
GSM/Makefile \ GSM/Makefile \
Transceiver52M/Makefile \ Transceiver52M/Makefile \
@@ -368,16 +336,18 @@ AC_CONFIG_FILES([\
Transceiver52M/device/usrp1/Makefile \ Transceiver52M/device/usrp1/Makefile \
Transceiver52M/device/lms/Makefile \ Transceiver52M/device/lms/Makefile \
Transceiver52M/device/ipc/Makefile \ Transceiver52M/device/ipc/Makefile \
Transceiver52M/device/ipc2/Makefile \
Transceiver52M/device/bladerf/Makefile \ Transceiver52M/device/bladerf/Makefile \
tests/Makefile \ tests/Makefile \
tests/CommonLibs/Makefile \ tests/CommonLibs/Makefile \
tests/Transceiver52M/Makefile \ tests/Transceiver52M/Makefile \
utils/Makefile \ utils/Makefile \
utils/va-test/Makefile \
doc/Makefile \ doc/Makefile \
doc/examples/Makefile \ doc/examples/Makefile \
contrib/Makefile \ contrib/Makefile \
contrib/systemd/Makefile \ contrib/systemd/Makefile \
doc/manuals/Makefile \
]) ])
AC_OUTPUT
AC_OUTPUT(
doc/manuals/Makefile
contrib/osmo-trx.spec)

View File

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

254
contrib/osmo-trx.spec.in Normal file
View File

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

View File

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

View File

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

View File

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

View File

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

174
debian/changelog vendored
View File

@@ -1,177 +1,3 @@
osmo-trx (1.7.0) unstable; urgency=medium
[ Eric ]
* ms: adjust ts advance
* ms: reduce rx burst queue size
* ms: init blade with fpga control
* devices: fix wrong gain to power mapping
* ms: get rid of std::thread
* ms: add demod test tool and data
[ Vadim Yanitskiy ]
* osmo-trx-ms: bump osmocom-bb submodule commit
* Transceiver::ctrl_sock_handle_rx(): fix copy-pasted comments
* build: include version files into the release tarball
* doc/examples: fix missing config files in release tarballs
* README.md: cosmetic: fix a typo
[ Harald Welte ]
* osmo-trx-uhd: Make sure HOME environment variable is set
* README.md: Improve markdown formatting
* README.md: Add Forum and Issue Tracker links
* Add funding link to github mirror
* README.md: Remove stray apostrophe
[ Andreas Eversberg ]
* Use uniform log format for default config files
[ Eric Wild ]
* ms: do not set the blade tuning mode
* ms: hard preswapped VA gsm bits
* ms: add sigproclib demod
* ms: fix up template deduction failure
* ms: update osmocom-bb submodule
* ms: disabe uhd ms build
* transceiver: use log level cache
[ Oliver Smith ]
* gitignore: add .version
* debian/rules: make configure args diff friendly
* debian: add osmo-trx-ms-blade
* contrib/jenkins: make configure args diff friendly
* contrib/jenkins: add --with-bladerf
* contrib: remove rpm spec file
* contrib/systemd: run as osmocom user
* contrib/systemd/osmo-trx-uhd: fix HOME=
[ Pau Espin Pedrol ]
* code-architecture.adoc: Fix missing alignment in digraph
* doc: Introduce documentation for osmo-trx-ipc and its IPC interface
-- Oliver Smith <osmith@sysmocom.de> Wed, 24 Jul 2024 15:57:14 +0200
osmo-trx (1.6.0) unstable; urgency=medium
[ Vadim Yanitskiy ]
* configure.ac: check if LIBTRXCON_DIR (submodule) exists
* tests: Makefile.am: move -I flags from AM_CFLAGS to AM_CPPFLAGS
* tests: there shall be no libraries in LDFLAGS
* tests: use -no-install libtool flag to avoid ./lt-* scripts
* tests: LMSDeviceTest: fix CPPFLAGS vs CXXFLAGS
* ipc-driver-test: clean up variables in Makefile.am
* CommonLibs: remove unused *trx in cfg[_no]_ctr_error_threshold_cmd
* CommonLibs: clean up and fix Makefile.am
* ms: logging: print category, level, and extended timestamp
[ Oliver Smith ]
* Run struct_endianness.py
* debian: set compat level to 10
* systemd: depend on networking-online.target
* USRPDevice:updateAlignment: remove byteswap code
[ Eric ]
* .clang-format: adjust template formatting
* ms: update submodule to currently known working version
* ms: adjust tx scaling for tx samples
* ms : fix the template formatting
* ms: fix the gain init for blade
* ms: prettify scheduling + add odroid
* ms: fix startup & shutdown of blade
* ms: block burst q to upper layer
* ms: use single thread pool
* ms : rename var
* ms : rename var
* ms: cache frequency
* ms: pretty tx buf class
* ms: rearrange internal trxcon<->phy if
* ms: remove syncthing tool
* ms: prune common sch acq code
* ms: rearrange code to allow clean exits
* ms: flexible template for value_type buffer sum
* ms: make init call less confusing
* transceiver: pass cfg struct instead of args
* devices: unify band handling
* ms: fix blocking logging
* ms: drop the tx burst padding
* trx: fix dev-args issue
* ms: update osmocom-bb
* ms: restructure the va code to add rach support
* transceiver: add experimental viterbi equalizer support
* ms/va: make ancient gcc < 8 happy
* ms: fix thread prio startup issue
* ms: fix a few coverity complaints related to initialization
* ms: bump osmocom-bb submodule to current head
[ Eric Wild ]
* ms: adjust float<->integral type conversion
* ms: sch: drop intermediate softvector
* devices: add freq/gain override for uhd
[ arehbein ]
* Transition to use of 'telnet_init_default'
[ Pau Espin Pedrol ]
* Call osmo_fd_unregister() before closing and changing bfd->fd
* ms: update osmocom-bb submodule
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 15:56:57 +0200
osmo-trx (1.5.0) unstable; urgency=medium
[ Oliver Smith ]
* configure.ac: add -lboost_thread for uhd < 4.2.0
* gitignore: add uhddev_ipc.cpp
* contrib/jenkins: don't run "make distcheck" on arm
[ Vadim Yanitskiy ]
* threshold_timer_update_intv(): call osmo_timer_del() unconditionally
* Transceiver::expectedCorrType(): RACH is always 8-bit on PTCCH/U
* contrib/jenkins.sh: dump submodule status before building
* configure.ac: fix: properly check whether to enable ms-trx
* configure.ac: allow building without cloning submodules
* configure.ac: cosmetic: rearrange MS TRX related logic
* configure.ac: make use of AC_MSG_CHECKING and AC_MSG_RESULT
[ Max ]
* Set working directory in systemd service file
* Add realtime scheduling and set priority in service file
* ctrl: take both address and port from vty config
[ Eric ]
* ignore vscode dirs
* rename noisevector class -> avgvector
* osmocom-bb for ms-trx side trxcon integration
* add checkpatch config
* bladerf xa4 support
* update osmocom-bb submodule to fix make distcheck
* vita demod by piotr krysik, modified
* properly update osmocom-bb submodule, for real this time..
* ms-trx support
* clean up mutex, scopedlock, and signal classes
* ipc: add missing override
* clang-format: proper c++ standard
* ipc: remove old autotools workaround
* ms: init trash used to escape the usb callbacks
* radio interface: fix init
[ Eric Wild ]
* mstrx: do not wait forever if clock locking fails
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 17:08:17 +0100
osmo-trx (1.4.1) unstable; urgency=medium
[ Oliver Smith ]
* 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 osmo-trx (1.4.0) unstable; urgency=medium
[ Pau Espin Pedrol ] [ Pau Espin Pedrol ]

2
debian/compat vendored
View File

@@ -1 +1 @@
10 9

30
debian/control vendored
View File

@@ -2,7 +2,7 @@ Source: osmo-trx
Section: net Section: net
Priority: optional Priority: optional
Maintainer: Osmocom team <openbsc@lists.osmocom.org> Maintainer: Osmocom team <openbsc@lists.osmocom.org>
Build-Depends: debhelper (>= 10), Build-Depends: debhelper (>= 9),
autotools-dev, autotools-dev,
autoconf-archive, autoconf-archive,
pkg-config, pkg-config,
@@ -14,12 +14,11 @@ Build-Depends: debhelper (>= 10),
libtalloc-dev, libtalloc-dev,
libusrp-dev, libusrp-dev,
liblimesuite-dev, liblimesuite-dev,
libbladerf-dev, libosmocore-dev (>= 1.6.0),
libosmocore-dev (>= 1.10.0), osmo-gsm-manuals-dev
osmo-gsm-manuals-dev (>= 1.6.0)
Standards-Version: 3.9.6 Standards-Version: 3.9.6
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx Vcs-Browser: http://cgit.osmocom.org/osmo-trx
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx Vcs-Git: git://git.osmocom.org/osmo-trx
Homepage: https://projects.osmocom.org/projects/osmotrx Homepage: https://projects.osmocom.org/projects/osmotrx
Package: osmo-trx Package: osmo-trx
@@ -111,25 +110,6 @@ Description: SDR transceiver that implements Layer 1 of a GSM BTS (generic IPC)
between different telecommunication associations for developing new between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM) 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 Package: osmo-trx-doc
Architecture: all Architecture: all
Section: doc Section: doc

2
debian/copyright vendored
View File

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

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