mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
				synced 2025-11-03 21:53:18 +00:00 
			
		
		
		
	Compare commits
	
		
			91 Commits
		
	
	
		
			2023q1
			...
			osmith/wip
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					098a5b4c7f | ||
| 
						 | 
					9c4bfecfe3 | ||
| 
						 | 
					4f6e9eb4cf | ||
| 
						 | 
					24f938bfa5 | ||
| 
						 | 
					132ec7452d | ||
| 
						 | 
					553d6b0b51 | ||
| 
						 | 
					a7f5826351 | ||
| 
						 | 
					75cf9254a3 | ||
| 
						 | 
					c5f623f966 | ||
| 
						 | 
					8fd51cb7c9 | ||
| 
						 | 
					e6dc78e5c5 | ||
| 
						 | 
					9982b33597 | ||
| 
						 | 
					52ef221657 | ||
| 
						 | 
					ec13193cae | ||
| 
						 | 
					3d8598d460 | ||
| 
						 | 
					1d7f5a9175 | ||
| 
						 | 
					511d62733e | ||
| 
						 | 
					8f19df3bd7 | ||
| 
						 | 
					b7cde0f6a2 | ||
| 
						 | 
					25590be470 | ||
| 
						 | 
					f1ce0e7692 | ||
| 
						 | 
					99e0746f37 | ||
| 
						 | 
					1f8eb7c658 | ||
| 
						 | 
					d8a1dee2c9 | ||
| 
						 | 
					56c7b777f3 | ||
| 
						 | 
					989fe75038 | ||
| 
						 | 
					8aea236c56 | ||
| 
						 | 
					a2d76f1d2f | ||
| 
						 | 
					a118d98ec3 | ||
| 
						 | 
					c7fc94dff6 | ||
| 
						 | 
					06c0810e98 | ||
| 
						 | 
					6ee9dccddb | ||
| 
						 | 
					242ceb25d1 | ||
| 
						 | 
					b52650f157 | ||
| 
						 | 
					992a49e586 | ||
| 
						 | 
					ad9b8b4211 | ||
| 
						 | 
					0f4381d480 | ||
| 
						 | 
					2ada887367 | ||
| 
						 | 
					5042156437 | ||
| 
						 | 
					70ed3d586e | ||
| 
						 | 
					3dfbb6d9f6 | ||
| 
						 | 
					58294fde4b | ||
| 
						 | 
					d372eb2f0b | ||
| 
						 | 
					287ae681b7 | ||
| 
						 | 
					cca5d93f66 | ||
| 
						 | 
					a5a2275a08 | ||
| 
						 | 
					1499f0343a | ||
| 
						 | 
					8c1d59086e | ||
| 
						 | 
					f57a86131e | ||
| 
						 | 
					7d5c16590c | ||
| 
						 | 
					f8c7a52521 | ||
| 
						 | 
					c0f78a37ed | ||
| 
						 | 
					19e134a626 | ||
| 
						 | 
					a98521ac05 | ||
| 
						 | 
					df4520df77 | ||
| 
						 | 
					2f40abd8f5 | ||
| 
						 | 
					6a3e4b32f0 | ||
| 
						 | 
					c3e515a28b | ||
| 
						 | 
					bcaafcaa9d | ||
| 
						 | 
					ea7bd5fb91 | ||
| 
						 | 
					135d64b1a9 | ||
| 
						 | 
					e44cf44af4 | ||
| 
						 | 
					c0f0a6105a | ||
| 
						 | 
					da5ffd6e01 | ||
| 
						 | 
					40978041ad | ||
| 
						 | 
					3e7f4b0da9 | ||
| 
						 | 
					4080cd05ba | ||
| 
						 | 
					b3157b91bb | ||
| 
						 | 
					805e0d9c6b | ||
| 
						 | 
					4b2b98b067 | ||
| 
						 | 
					2e6c362b9c | ||
| 
						 | 
					7e47a521ef | ||
| 
						 | 
					f35515c015 | ||
| 
						 | 
					874542ca7c | ||
| 
						 | 
					f476a6755b | ||
| 
						 | 
					1ddd727bb4 | ||
| 
						 | 
					8a4362459d | ||
| 
						 | 
					5ba130c381 | ||
| 
						 | 
					fed58d97b8 | ||
| 
						 | 
					0e13bfd18c | ||
| 
						 | 
					cf1ca2e92e | ||
| 
						 | 
					20ecc4f531 | ||
| 
						 | 
					097a16e384 | ||
| 
						 | 
					c9af0b0ba0 | ||
| 
						 | 
					621a49eb69 | ||
| 
						 | 
					f538397826 | ||
| 
						 | 
					8c4336dba9 | ||
| 
						 | 
					fe1b9cef40 | ||
| 
						 | 
					5e63151f9f | ||
| 
						 | 
					10b4e31655 | ||
| 
						 | 
					0c433350da | 
@@ -1,4 +1,5 @@
 | 
			
		||||
--exclude osmocom-bb/.*
 | 
			
		||||
--exclude .*h
 | 
			
		||||
--exclude Transceiver52M/grgsm_vitac/.*
 | 
			
		||||
--exclude utils/va-test/.*
 | 
			
		||||
--ignore FUNCTION_WITHOUT_ARGS
 | 
			
		||||
@@ -25,7 +25,7 @@ AllowShortLoopsOnASingleLine: false
 | 
			
		||||
AlwaysBreakAfterDefinitionReturnType: None
 | 
			
		||||
AlwaysBreakAfterReturnType: None
 | 
			
		||||
AlwaysBreakBeforeMultilineStrings: false
 | 
			
		||||
AlwaysBreakTemplateDeclarations: false
 | 
			
		||||
AlwaysBreakTemplateDeclarations: true
 | 
			
		||||
BinPackArguments: true
 | 
			
		||||
BinPackParameters: true
 | 
			
		||||
BraceWrapping:
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								.github/FUNDING.yml
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
open_collective: osmocom
 | 
			
		||||
							
								
								
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -38,6 +38,7 @@ Transceiver52M/device/ipc/ipc-driver-test
 | 
			
		||||
.deps
 | 
			
		||||
.libs
 | 
			
		||||
.dirstamp
 | 
			
		||||
.version
 | 
			
		||||
*~
 | 
			
		||||
Makefile
 | 
			
		||||
config.log
 | 
			
		||||
@@ -83,6 +84,7 @@ contrib/osmo-trx.spec
 | 
			
		||||
!contrib/osmo-trx.spec.in
 | 
			
		||||
 | 
			
		||||
utils/osmo-prbs-tool
 | 
			
		||||
utils/va-test/osmo-burst-gen
 | 
			
		||||
/.qtc_clangd/*
 | 
			
		||||
/.cache/*
 | 
			
		||||
/.vscode/*
 | 
			
		||||
 
 | 
			
		||||
@@ -22,8 +22,8 @@
 | 
			
		||||
include $(top_srcdir)/Makefile.common
 | 
			
		||||
 | 
			
		||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
 | 
			
		||||
AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
 | 
			
		||||
AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
 | 
			
		||||
AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
 | 
			
		||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
 | 
			
		||||
 | 
			
		||||
noinst_LTLIBRARIES = libcommon.la
 | 
			
		||||
 | 
			
		||||
@@ -37,7 +37,12 @@ libcommon_la_SOURCES = \
 | 
			
		||||
	trx_rate_ctr.cpp \
 | 
			
		||||
	trx_vty.c \
 | 
			
		||||
	debug.c
 | 
			
		||||
libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
 | 
			
		||||
libcommon_la_LIBADD = \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOCTRL_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	-lpthread \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = \
 | 
			
		||||
	BitVector.h \
 | 
			
		||||
 
 | 
			
		||||
@@ -57,4 +57,15 @@ struct trx_cfg {
 | 
			
		||||
	unsigned int stack_size;
 | 
			
		||||
	unsigned int num_chans;
 | 
			
		||||
	struct trx_chan chans[TRX_CHAN_MAX];
 | 
			
		||||
	struct {
 | 
			
		||||
		bool ul_freq_override;
 | 
			
		||||
		bool dl_freq_override;
 | 
			
		||||
		bool ul_gain_override;
 | 
			
		||||
		bool dl_gain_override;
 | 
			
		||||
		double ul_freq;
 | 
			
		||||
		double dl_freq;
 | 
			
		||||
		double ul_gain;
 | 
			
		||||
		double dl_gain;
 | 
			
		||||
	} overrides;
 | 
			
		||||
	bool use_va;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -285,6 +285,79 @@ DEFUN_ATTR(cfg_ul_fn_offset, cfg_ul_fn_offset_cmd,
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_ATTR(cfg_ul_freq_override, cfg_ul_freq_override_cmd,
 | 
			
		||||
	   "ul-freq-override FLOAT",
 | 
			
		||||
	   "Overrides Rx carrier frequency\n"
 | 
			
		||||
	   "Frequency in Hz (e.g. 145300000)\n",
 | 
			
		||||
	   CMD_ATTR_HIDDEN)
 | 
			
		||||
{
 | 
			
		||||
	struct trx_ctx *trx = trx_from_vty(vty);
 | 
			
		||||
 | 
			
		||||
	trx->cfg.overrides.ul_freq_override = true;
 | 
			
		||||
	trx->cfg.overrides.ul_freq = atof(argv[0]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
DEFUN_ATTR(cfg_dl_freq_override, cfg_dl_freq_override_cmd,
 | 
			
		||||
	   "dl-freq-override FLOAT",
 | 
			
		||||
	   "Overrides Tx carrier frequency\n"
 | 
			
		||||
	   "Frequency in Hz (e.g. 145300000)\n",
 | 
			
		||||
	   CMD_ATTR_HIDDEN)
 | 
			
		||||
{
 | 
			
		||||
	struct trx_ctx *trx = trx_from_vty(vty);
 | 
			
		||||
 | 
			
		||||
	trx->cfg.overrides.dl_freq_override = true;
 | 
			
		||||
	trx->cfg.overrides.dl_freq = atof(argv[0]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_ATTR(cfg_ul_gain_override, cfg_ul_gain_override_cmd,
 | 
			
		||||
	   "ul-gain-override FLOAT",
 | 
			
		||||
	   "Overrides Rx gain\n"
 | 
			
		||||
	   "gain in dB\n",
 | 
			
		||||
	   CMD_ATTR_HIDDEN)
 | 
			
		||||
{
 | 
			
		||||
	struct trx_ctx *trx = trx_from_vty(vty);
 | 
			
		||||
 | 
			
		||||
	trx->cfg.overrides.ul_gain_override = true;
 | 
			
		||||
	trx->cfg.overrides.ul_gain = atof(argv[0]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
DEFUN_ATTR(cfg_dl_gain_override, cfg_dl_gain_override_cmd,
 | 
			
		||||
	   "dl-gain-override FLOAT",
 | 
			
		||||
	   "Overrides Tx gain\n"
 | 
			
		||||
	   "gain in dB\n",
 | 
			
		||||
	   CMD_ATTR_HIDDEN)
 | 
			
		||||
{
 | 
			
		||||
	struct trx_ctx *trx = trx_from_vty(vty);
 | 
			
		||||
 | 
			
		||||
	trx->cfg.overrides.dl_gain_override = true;
 | 
			
		||||
	trx->cfg.overrides.dl_gain = atof(argv[0]);
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN_ATTR(cfg_use_viterbi, cfg_use_viterbi_cmd,
 | 
			
		||||
	"viterbi-eq (disable|enable)",
 | 
			
		||||
	"Use viterbi equalizer for gmsk (default=disable)\n"
 | 
			
		||||
	"Disable VA\n"
 | 
			
		||||
	"Enable VA\n",
 | 
			
		||||
	CMD_ATTR_HIDDEN)
 | 
			
		||||
{
 | 
			
		||||
	struct trx_ctx *trx = trx_from_vty(vty);
 | 
			
		||||
 | 
			
		||||
	if (strcmp("disable", argv[0]) == 0)
 | 
			
		||||
		trx->cfg.use_va = false;
 | 
			
		||||
	else if (strcmp("enable", argv[0]) == 0)
 | 
			
		||||
		trx->cfg.use_va = true;
 | 
			
		||||
	else
 | 
			
		||||
		return CMD_WARNING;
 | 
			
		||||
 | 
			
		||||
	return CMD_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
 | 
			
		||||
	"swap-channels (disable|enable)",
 | 
			
		||||
	"Swap primary and secondary channels of the PHY (if any)\n"
 | 
			
		||||
@@ -466,7 +539,6 @@ DEFUN_ATTR(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct ctr_threshold ctr;
 | 
			
		||||
 | 
			
		||||
	struct trx_ctx *trx = trx_from_vty(vty);
 | 
			
		||||
	rc = vty_ctr_name_2_id(argv[0]);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
 | 
			
		||||
@@ -498,7 +570,6 @@ DEFUN_ATTR(cfg_no_ctr_error_threshold, cfg_no_ctr_error_threshold_cmd,
 | 
			
		||||
	int rc;
 | 
			
		||||
	struct ctr_threshold ctr;
 | 
			
		||||
 | 
			
		||||
	struct trx_ctx *trx = trx_from_vty(vty);
 | 
			
		||||
	rc = vty_ctr_name_2_id(argv[0]);
 | 
			
		||||
	if (rc < 0) {
 | 
			
		||||
		vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
 | 
			
		||||
@@ -609,7 +680,7 @@ static int config_write_trx(struct vty *vty)
 | 
			
		||||
		vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
 | 
			
		||||
	if (trx->cfg.base_port != DEFAULT_TRX_PORT)
 | 
			
		||||
		vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE);
 | 
			
		||||
	if (trx->cfg.dev_args)
 | 
			
		||||
	if (strlen(trx->cfg.dev_args))
 | 
			
		||||
		vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE);
 | 
			
		||||
	if (trx->cfg.tx_sps != DEFAULT_TX_SPS)
 | 
			
		||||
		vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
 | 
			
		||||
@@ -640,6 +711,16 @@ static int config_write_trx(struct vty *vty)
 | 
			
		||||
		vty_out(vty, " stack-size %u%s", trx->cfg.stack_size, VTY_NEWLINE);
 | 
			
		||||
	if (trx->cfg.ul_fn_offset != 0)
 | 
			
		||||
		vty_out(vty, " ul-fn-offset %d%s", trx->cfg.ul_fn_offset, VTY_NEWLINE);
 | 
			
		||||
	if (trx->cfg.overrides.dl_freq_override)
 | 
			
		||||
		vty_out(vty, " dl-freq-override %f%s", trx->cfg.overrides.dl_freq, VTY_NEWLINE);
 | 
			
		||||
	if (trx->cfg.overrides.ul_freq_override)
 | 
			
		||||
		vty_out(vty, " ul-freq-override %f%s", trx->cfg.overrides.ul_freq, VTY_NEWLINE);
 | 
			
		||||
	if (trx->cfg.overrides.dl_gain_override)
 | 
			
		||||
		vty_out(vty, " dl-gain-override %f%s", trx->cfg.overrides.dl_gain, VTY_NEWLINE);
 | 
			
		||||
	if (trx->cfg.overrides.ul_gain_override)
 | 
			
		||||
		vty_out(vty, " ul-gain-override %f%s", trx->cfg.overrides.ul_gain, VTY_NEWLINE);
 | 
			
		||||
	if (trx->cfg.use_va)
 | 
			
		||||
		vty_out(vty, " viterbi-eq %s%s", trx->cfg.use_va ? "enable" : "disable", VTY_NEWLINE);
 | 
			
		||||
	trx_rate_ctr_threshold_write_config(vty, " ");
 | 
			
		||||
 | 
			
		||||
	for (i = 0; i < trx->cfg.num_chans; i++) {
 | 
			
		||||
@@ -762,6 +843,7 @@ struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx)
 | 
			
		||||
	trx->cfg.rx_sps = DEFAULT_RX_SPS;
 | 
			
		||||
	trx->cfg.filler = FILLER_ZERO;
 | 
			
		||||
	trx->cfg.rssi_offset = 0.0f;
 | 
			
		||||
	trx->cfg.dev_args = talloc_strdup(trx, "");
 | 
			
		||||
 | 
			
		||||
	return trx;
 | 
			
		||||
}
 | 
			
		||||
@@ -804,6 +886,11 @@ int trx_vty_init(struct trx_ctx* trx)
 | 
			
		||||
 | 
			
		||||
	install_element(TRX_NODE, &cfg_chan_cmd);
 | 
			
		||||
	install_element(TRX_NODE, &cfg_ul_fn_offset_cmd);
 | 
			
		||||
	install_element(TRX_NODE, &cfg_ul_freq_override_cmd);
 | 
			
		||||
	install_element(TRX_NODE, &cfg_dl_freq_override_cmd);
 | 
			
		||||
	install_element(TRX_NODE, &cfg_ul_gain_override_cmd);
 | 
			
		||||
	install_element(TRX_NODE, &cfg_dl_gain_override_cmd);
 | 
			
		||||
	install_element(TRX_NODE, &cfg_use_viterbi_cmd);
 | 
			
		||||
	install_node(&chan_node, dummy_config_write);
 | 
			
		||||
	install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
 | 
			
		||||
	install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
 | 
			
		||||
 
 | 
			
		||||
@@ -55,12 +55,15 @@ const BitVector GSM::gEdgeTrainingSequence[] = {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
 | 
			
		||||
const BitVector GSM::gDummyBurstTSC("01110001011100010111000101");
 | 
			
		||||
 | 
			
		||||
/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */
 | 
			
		||||
const BitVector GSM::gRACHSynchSequenceTS0("01001011011111111001100110101010001111000");  /* GSM, GMSK (default) */
 | 
			
		||||
const BitVector GSM::gRACHSynchSequenceTS1("01010100111110001000011000101111001001101");  /* EGPRS, 8-PSK */
 | 
			
		||||
const BitVector GSM::gRACHSynchSequenceTS2("11101111001001110101011000001101101110111");  /* EGPRS, GMSK */
 | 
			
		||||
 | 
			
		||||
const BitVector GSM::gSCHSynchSequence("1011100101100010000001000000111100101101010001010111011000011011");
 | 
			
		||||
 | 
			
		||||
//                               |-head-||---------midamble----------------------||--------------data----------------||t|
 | 
			
		||||
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -52,11 +52,16 @@ extern const BitVector gEdgeTrainingSequence[];
 | 
			
		||||
 | 
			
		||||
/** C0T0 filler burst, GSM 05.02, 5.2.6 */
 | 
			
		||||
extern const BitVector gDummyBurst;
 | 
			
		||||
extern const BitVector gDummyBurstTSC;
 | 
			
		||||
 | 
			
		||||
/** Random access burst synch. sequence */
 | 
			
		||||
extern const BitVector gRACHSynchSequenceTS0;
 | 
			
		||||
extern const BitVector gRACHSynchSequenceTS1;
 | 
			
		||||
extern const BitVector gRACHSynchSequenceTS2;
 | 
			
		||||
 | 
			
		||||
/** Synchronization burst sync sequence */
 | 
			
		||||
extern const BitVector gSCHSynchSequence;
 | 
			
		||||
 | 
			
		||||
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
 | 
			
		||||
extern const BitVector gRACHBurst;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -43,12 +43,19 @@ SUBDIRS += \
 | 
			
		||||
	doc \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
BUILT_SOURCES = $(top_srcdir)/.version
 | 
			
		||||
$(top_srcdir)/.version:
 | 
			
		||||
	echo $(VERSION) > $@-t && mv $@-t $@
 | 
			
		||||
dist-hook:
 | 
			
		||||
	echo $(VERSION) > $(distdir)/.tarball-version
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	.version \
 | 
			
		||||
	LEGAL \
 | 
			
		||||
	COPYING \
 | 
			
		||||
	README.md \
 | 
			
		||||
	contrib/osmo-trx.spec.in \
 | 
			
		||||
	debian \
 | 
			
		||||
	git-version-gen \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_DISTCHECK_CONFIGURE_FLAGS = \
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										28
									
								
								README.md
									
									
									
									
									
								
							
							
						
						
									
										28
									
								
								README.md
									
									
									
									
									
								
							@@ -4,17 +4,17 @@ About OsmoTRX
 | 
			
		||||
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
 | 
			
		||||
physical layer of a BTS comprising the following 3GPP specifications:
 | 
			
		||||
 | 
			
		||||
* TS 05.01 "Physical layer on the radio path"
 | 
			
		||||
* TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
 | 
			
		||||
* TS 05.04 "Modulation"
 | 
			
		||||
* TS 05.10 "Radio subsystem synchronization"
 | 
			
		||||
* TS 05.01 *Physical layer on the radio path*
 | 
			
		||||
* TS 05.02 *Multiplexing and Multiple Access on the Radio Path*
 | 
			
		||||
* TS 05.04 *Modulation*
 | 
			
		||||
* TS 05.10 *Radio subsystem synchronization*
 | 
			
		||||
 | 
			
		||||
OsmoTRX is originally based on the transceiver code from the
 | 
			
		||||
[OpenBTS](https://osmocom.org/projects/osmobts/wiki/OpenBTS) project, but setup
 | 
			
		||||
to operate independently with the purpose of using with non-OpenBTS software and
 | 
			
		||||
projects, specifically within the Osmocom stack.  Used together with
 | 
			
		||||
[OsmoBTS](https://osmocom.org/projects/osmobts/wiki) you can get a pretty
 | 
			
		||||
standard GSM BTS with Abis interface as per the relevant 3GPP specifications.
 | 
			
		||||
standard GSM/GPRS/EGPRS BTS with Abis interface as per the relevant 3GPP specifications.
 | 
			
		||||
 | 
			
		||||
Homepage
 | 
			
		||||
--------
 | 
			
		||||
@@ -27,7 +27,7 @@ GIT Repository
 | 
			
		||||
 | 
			
		||||
You can clone from the official osmo-trx.git repository using
 | 
			
		||||
 | 
			
		||||
        git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-trx`
 | 
			
		||||
        git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
 | 
			
		||||
 | 
			
		||||
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-trx>
 | 
			
		||||
 | 
			
		||||
@@ -39,6 +39,13 @@ also available online for each of the sub-libraries at User Manual for OsmoTRX
 | 
			
		||||
can be generated during the build process, and is also available online at
 | 
			
		||||
<https://ftp.osmocom.org/docs/latest/osmotrx-usermanual.pdf>.
 | 
			
		||||
 | 
			
		||||
Forum
 | 
			
		||||
-----
 | 
			
		||||
 | 
			
		||||
We welcome any osmo-trx related discussions in the
 | 
			
		||||
[Cellular Network Infrastructure -> 2 RAN (GERAN)](https://discourse.osmocom.org/c/cni/geran)
 | 
			
		||||
section of the osmocom discourse (web based Forum).
 | 
			
		||||
 | 
			
		||||
Mailing List
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
@@ -50,13 +57,20 @@ Please observe the [Osmocom Mailing List
 | 
			
		||||
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
 | 
			
		||||
when posting.
 | 
			
		||||
 | 
			
		||||
Issue Tracker
 | 
			
		||||
-------------
 | 
			
		||||
 | 
			
		||||
We use the [issue tracker of the osmo-trx project on osmocom.org](https://osmocom.org/projects/osmotrx/issues) for
 | 
			
		||||
tracking the state of bug reports and feature requests.  Feel free to submit any issues you may find, or help
 | 
			
		||||
us out by resolving existing issues.
 | 
			
		||||
 | 
			
		||||
Contributing
 | 
			
		||||
------------
 | 
			
		||||
 | 
			
		||||
Our coding standards are described at
 | 
			
		||||
<https://osmocom.org/projects/cellular-infrastructure/wiki/Coding_standards>
 | 
			
		||||
 | 
			
		||||
We us a gerrit based patch submission/review process for managing contributions.
 | 
			
		||||
We use a Gerrit based patch submission/review process for managing contributions.
 | 
			
		||||
Please see <https://osmocom.org/projects/cellular-infrastructure/wiki/Gerrit>
 | 
			
		||||
for more details
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -0,0 +1,9 @@
 | 
			
		||||
# 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
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,7 @@ unlike the built-in complex<> templates, these inline most operations for speed
 | 
			
		||||
template<class Real> class Complex {
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
  typedef Real value_type;
 | 
			
		||||
  Real r, i;
 | 
			
		||||
 | 
			
		||||
  /**@name constructors */
 | 
			
		||||
 
 | 
			
		||||
@@ -40,7 +40,9 @@ COMMON_SOURCES = \
 | 
			
		||||
	ChannelizerBase.cpp \
 | 
			
		||||
	Channelizer.cpp \
 | 
			
		||||
	Synthesis.cpp \
 | 
			
		||||
	proto_trxd.c
 | 
			
		||||
	proto_trxd.c \
 | 
			
		||||
	grgsm_vitac/grgsm_vitac.cpp \
 | 
			
		||||
	grgsm_vitac/viterbi_detector.cc
 | 
			
		||||
 | 
			
		||||
libtransceiver_common_la_SOURCES = \
 | 
			
		||||
	$(COMMON_SOURCES) \
 | 
			
		||||
@@ -80,23 +82,34 @@ AM_CPPFLAGS += -I${srcdir}
 | 
			
		||||
TRXCON_LDADD = \
 | 
			
		||||
	$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libtrxcon.a \
 | 
			
		||||
	$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libl1sched.a \
 | 
			
		||||
	$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libl1gprs.a \
 | 
			
		||||
	$(LIBOSMOCODING_LIBS)
 | 
			
		||||
 | 
			
		||||
MS_SOURCES = \
 | 
			
		||||
MS_LOWER_SRC = \
 | 
			
		||||
	ms/sch.c \
 | 
			
		||||
	ms/ms.cpp \
 | 
			
		||||
	ms/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_rx_burst.h \
 | 
			
		||||
	ms/ms_upper.h \
 | 
			
		||||
	ms/ms_trxcon_if.h \
 | 
			
		||||
	ms/itrq.h \
 | 
			
		||||
	ms/sch.h \
 | 
			
		||||
	ms/threadpool.h \
 | 
			
		||||
	grgsm_vitac/viterbi_detector.h \
 | 
			
		||||
	grgsm_vitac/constants.h \
 | 
			
		||||
	grgsm_vitac/grgsm_vitac.h
 | 
			
		||||
@@ -114,25 +127,16 @@ osmo_trx_uhd_LDADD = \
 | 
			
		||||
	$(UHD_LIBS)
 | 
			
		||||
osmo_trx_uhd_CPPFLAGS  = $(AM_CPPFLAGS) $(UHD_CFLAGS)
 | 
			
		||||
 | 
			
		||||
if ENABLE_MS_TRX
 | 
			
		||||
bin_PROGRAMS += osmo-trx-ms-uhd
 | 
			
		||||
osmo_trx_ms_uhd_SOURCES = $(MS_SOURCES) ms/ms_upper.cpp ms/l1ctl_server.c ms/logging.c ms/l1ctl_server_cb.cpp
 | 
			
		||||
osmo_trx_ms_uhd_LDADD = \
 | 
			
		||||
	$(builddir)/device/uhd/libdevice.la \
 | 
			
		||||
	$(COMMON_LDADD) \
 | 
			
		||||
	$(UHD_LIBS) \
 | 
			
		||||
	$(TRXCON_LDADD)
 | 
			
		||||
osmo_trx_ms_uhd_CPPFLAGS  = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DBUILDUHD
 | 
			
		||||
 | 
			
		||||
bin_PROGRAMS += osmo-trx-syncthing-uhd
 | 
			
		||||
osmo_trx_syncthing_uhd_SOURCES = $(MS_SOURCES) ms/ms_rx_burst_test.cpp
 | 
			
		||||
osmo_trx_syncthing_uhd_LDADD = \
 | 
			
		||||
	$(builddir)/device/uhd/libdevice.la \
 | 
			
		||||
	$(COMMON_LDADD) \
 | 
			
		||||
	$(UHD_LIBS) \
 | 
			
		||||
	$(TRXCON_LDADD)
 | 
			
		||||
osmo_trx_syncthing_uhd_CPPFLAGS  = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DSYNCTHINGONLY -DBUILDUHD
 | 
			
		||||
endif
 | 
			
		||||
#if ENABLE_MS_TRX
 | 
			
		||||
#bin_PROGRAMS += osmo-trx-ms-uhd
 | 
			
		||||
#osmo_trx_ms_uhd_SOURCES = $(MS_LOWER_SRC) $(MS_UPPER_SRC)
 | 
			
		||||
#osmo_trx_ms_uhd_LDADD = \
 | 
			
		||||
#	$(builddir)/device/uhd/libdevice.la \
 | 
			
		||||
#	$(COMMON_LDADD) \
 | 
			
		||||
#	$(UHD_LIBS) \
 | 
			
		||||
#	$(TRXCON_LDADD)
 | 
			
		||||
#osmo_trx_ms_uhd_CPPFLAGS  = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DBUILDUHD
 | 
			
		||||
#endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
if DEVICE_USRP1
 | 
			
		||||
@@ -166,22 +170,13 @@ osmo_trx_blade_CPPFLAGS  = $(AM_CPPFLAGS) $(LMS_CFLAGS)
 | 
			
		||||
 | 
			
		||||
if ENABLE_MS_TRX
 | 
			
		||||
bin_PROGRAMS += osmo-trx-ms-blade
 | 
			
		||||
osmo_trx_ms_blade_SOURCES = $(MS_SOURCES) ms/ms_upper.cpp ms/l1ctl_server.c ms/logging.c ms/l1ctl_server_cb.cpp
 | 
			
		||||
osmo_trx_ms_blade_SOURCES = $(MS_LOWER_SRC) $(MS_UPPER_SRC)
 | 
			
		||||
osmo_trx_ms_blade_LDADD = \
 | 
			
		||||
	$(builddir)/device/bladerf/libdevice.la \
 | 
			
		||||
	$(COMMON_LDADD) \
 | 
			
		||||
	$(BLADE_LIBS) \
 | 
			
		||||
	$(TRXCON_LDADD)
 | 
			
		||||
osmo_trx_ms_blade_CPPFLAGS  = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DBUILDBLADE
 | 
			
		||||
 | 
			
		||||
bin_PROGRAMS += osmo-trx-syncthing-blade
 | 
			
		||||
osmo_trx_syncthing_blade_SOURCES = $(MS_SOURCES) ms/ms_rx_burst_test.cpp
 | 
			
		||||
osmo_trx_syncthing_blade_LDADD =  \
 | 
			
		||||
	$(builddir)/device/bladerf/libdevice.la \
 | 
			
		||||
	$(COMMON_LDADD) \
 | 
			
		||||
	$(BLADE_LIBS) \
 | 
			
		||||
	$(TRXCON_LDADD)
 | 
			
		||||
osmo_trx_syncthing_blade_CPPFLAGS  = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DSYNCTHINGONLY -DBUILDBLADE -I../device/ipc
 | 
			
		||||
endif
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -32,7 +32,7 @@ extern "C" {
 | 
			
		||||
#define M_PI			3.14159265358979323846264338327f
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define MAX_OUTPUT_LEN		4096
 | 
			
		||||
#define MAX_OUTPUT_LEN		4096*4
 | 
			
		||||
 | 
			
		||||
using namespace std;
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -29,6 +29,7 @@
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include "Transceiver.h"
 | 
			
		||||
#include <Logger.h>
 | 
			
		||||
#include <grgsm_vitac/grgsm_vitac.h>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include "osmo_signal.h"
 | 
			
		||||
@@ -208,6 +209,8 @@ bool Transceiver::init()
 | 
			
		||||
    return false;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  initvita();
 | 
			
		||||
 | 
			
		||||
  mDataSockets.resize(mChans, -1);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -614,6 +617,44 @@ double Transceiver::rssiOffset(size_t chan)
 | 
			
		||||
  return mRadioInterface->rssiOffset(chan) + cfg->rssi_offset;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static SoftVector *demodAnyBurst_va(const signalVector &burst, CorrType type, int sps, int rach_max_toa, int tsc)
 | 
			
		||||
{
 | 
			
		||||
	auto conved_beg = reinterpret_cast<const std::complex<float> *>(&burst.begin()[0]);
 | 
			
		||||
	std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
	float ncmax;
 | 
			
		||||
	const unsigned burst_len_bits = 148 + 8;
 | 
			
		||||
	char demodded_softbits[burst_len_bits];
 | 
			
		||||
	SoftVector *bits = new SoftVector(burst_len_bits);
 | 
			
		||||
 | 
			
		||||
	if (type == CorrType::TSC) {
 | 
			
		||||
		auto rach_burst_start = get_norm_chan_imp_resp(conved_beg, chan_imp_resp, &ncmax, tsc);
 | 
			
		||||
		rach_burst_start = std::max(rach_burst_start, 0);
 | 
			
		||||
		detect_burst_nb(conved_beg, chan_imp_resp, rach_burst_start, demodded_softbits);
 | 
			
		||||
	} else {
 | 
			
		||||
		auto normal_burst_start = get_access_imp_resp(conved_beg, chan_imp_resp, &ncmax, 0);
 | 
			
		||||
		normal_burst_start = std::max(normal_burst_start, 0);
 | 
			
		||||
		detect_burst_ab(conved_beg, chan_imp_resp, normal_burst_start, demodded_softbits, rach_max_toa);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	float *s = &bits->begin()[0];
 | 
			
		||||
	for (unsigned int i = 0; i < 148; i++)
 | 
			
		||||
		s[i] = demodded_softbits[i] * -1;
 | 
			
		||||
	for (unsigned int i = 148; i < burst_len_bits; i++)
 | 
			
		||||
		s[i] = 0;
 | 
			
		||||
	return bits;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define USE_VA
 | 
			
		||||
 | 
			
		||||
#ifdef USE_VA
 | 
			
		||||
// signalvector is owning despite claiming not to, but we can pretend, too..
 | 
			
		||||
static void dummy_free(void *wData){};
 | 
			
		||||
static void *dummy_alloc(size_t newSize)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Pull bursts from the FIFO and handle according to the slot
 | 
			
		||||
 * and burst correlation type. Equalzation is currently disabled.
 | 
			
		||||
@@ -634,6 +675,9 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
 | 
			
		||||
  TransceiverState *state = &mStates[chan];
 | 
			
		||||
  bool ctr_changed = false;
 | 
			
		||||
  double rssi_offset;
 | 
			
		||||
  static complex burst_shift_buffer[625];
 | 
			
		||||
  static signalVector shift_vec(burst_shift_buffer, 0, 625, dummy_alloc, dummy_free);
 | 
			
		||||
  signalVector *shvec_ptr = &shift_vec;
 | 
			
		||||
 | 
			
		||||
  /* Blocking FIFO read */
 | 
			
		||||
  radioVector *radio_burst = mReceiveFIFO[chan]->read();
 | 
			
		||||
@@ -713,8 +757,15 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
 | 
			
		||||
  max_toa = (type == RACH || type == EXT_RACH) ?
 | 
			
		||||
            mMaxExpectedDelayAB : mMaxExpectedDelayNB;
 | 
			
		||||
 | 
			
		||||
  if (cfg->use_va) {
 | 
			
		||||
    // shifted burst copy to make the old demod and detection happy
 | 
			
		||||
    std::copy(burst->begin() + 20, burst->end() - 20, shift_vec.begin());
 | 
			
		||||
  } else {
 | 
			
		||||
    shvec_ptr = burst;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  /* Detect normal or RACH bursts */
 | 
			
		||||
  rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
 | 
			
		||||
  rc = detectAnyBurst(*shvec_ptr, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
 | 
			
		||||
  if (rc <= 0) {
 | 
			
		||||
    if (rc == -SIGERR_CLIP) {
 | 
			
		||||
      LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst";
 | 
			
		||||
@@ -728,7 +779,13 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
 | 
			
		||||
    goto ret_idle;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  rxBurst = demodAnyBurst(*burst, (CorrType) rc, cfg->rx_sps, &ebp);
 | 
			
		||||
  if (cfg->use_va) {
 | 
			
		||||
    scaleVector(*burst, { (1. / (float)((1 << 14) - 1)), 0 });
 | 
			
		||||
    rxBurst = demodAnyBurst_va(*burst, (CorrType)rc, cfg->rx_sps, max_toa, mTSC);
 | 
			
		||||
  } else {
 | 
			
		||||
    rxBurst = demodAnyBurst(*shvec_ptr, (CorrType)rc, cfg->rx_sps, &ebp);
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bi->toa = ebp.toa;
 | 
			
		||||
  bi->tsc = ebp.tsc;
 | 
			
		||||
  bi->ci = ebp.ci;
 | 
			
		||||
@@ -902,19 +959,18 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
 | 
			
		||||
      sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
 | 
			
		||||
    }
 | 
			
		||||
  } else if (match_cmd(command, "SETMAXDLY", ¶ms)) {
 | 
			
		||||
    //set expected maximum time-of-arrival
 | 
			
		||||
    //set expected maximum time-of-arrival for Access Bursts
 | 
			
		||||
    int maxDelay;
 | 
			
		||||
    sscanf(params, "%d", &maxDelay);
 | 
			
		||||
    mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
 | 
			
		||||
    sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
 | 
			
		||||
  } else if (match_cmd(command, "SETMAXDLYNB", ¶ms)) {
 | 
			
		||||
    //set expected maximum time-of-arrival
 | 
			
		||||
    //set expected maximum time-of-arrival for Normal Bursts
 | 
			
		||||
    int maxDelay;
 | 
			
		||||
    sscanf(params, "%d", &maxDelay);
 | 
			
		||||
    mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
 | 
			
		||||
    sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
 | 
			
		||||
  } else if (match_cmd(command, "SETRXGAIN", ¶ms)) {
 | 
			
		||||
    //set expected maximum time-of-arrival
 | 
			
		||||
    int newGain;
 | 
			
		||||
    sscanf(params, "%d", &newGain);
 | 
			
		||||
    newGain = mRadioInterface->setRxGain(newGain, chan);
 | 
			
		||||
 
 | 
			
		||||
@@ -162,9 +162,9 @@ struct ctrl_sock_state {
 | 
			
		||||
  }
 | 
			
		||||
  ~ctrl_sock_state() {
 | 
			
		||||
      if(conn_bfd.fd >= 0) {
 | 
			
		||||
          osmo_fd_unregister(&conn_bfd);
 | 
			
		||||
          close(conn_bfd.fd);
 | 
			
		||||
          conn_bfd.fd = -1;
 | 
			
		||||
          osmo_fd_unregister(&conn_bfd);
 | 
			
		||||
      }
 | 
			
		||||
  }
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -47,25 +47,11 @@ extern "C" {
 | 
			
		||||
			LOGC(DDEV, ERROR) << bladerf_strerror(status);                                                 \
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
/* Device Type, Tx-SPS, Rx-SPS */
 | 
			
		||||
typedef std::tuple<blade_dev_type, int, int> dev_key;
 | 
			
		||||
 | 
			
		||||
/* Device parameter descriptor */
 | 
			
		||||
struct dev_desc {
 | 
			
		||||
	unsigned channels;
 | 
			
		||||
	double mcr;
 | 
			
		||||
	double rate;
 | 
			
		||||
	double offset;
 | 
			
		||||
	std::string str;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const std::map<dev_key, dev_desc> dev_param_map{
 | 
			
		||||
static const dev_map_t dev_param_map{
 | 
			
		||||
	{ std::make_tuple(blade_dev_type::BLADE2, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef std::tuple<blade_dev_type, enum gsm_band> dev_band_key;
 | 
			
		||||
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
 | 
			
		||||
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map{
 | 
			
		||||
static const power_map_t dev_band_nom_power_param_map{
 | 
			
		||||
	{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
 | 
			
		||||
	{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
 | 
			
		||||
	{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
 | 
			
		||||
@@ -84,11 +70,10 @@ static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
 | 
			
		||||
	return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
blade_device::blade_device(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
 | 
			
		||||
			   const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
 | 
			
		||||
	: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths), dev(nullptr), rx_gain_min(0.0),
 | 
			
		||||
	  rx_gain_max(0.0), band_ass_curr_sess(false), band((enum gsm_band)0), tx_spp(0), rx_spp(0), started(false),
 | 
			
		||||
	  aligned(false), drop_cnt(0), prev_ts(0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
 | 
			
		||||
blade_device::blade_device(InterfaceType iface, const struct trx_cfg *cfg)
 | 
			
		||||
	: RadioDevice(iface, cfg), band_manager(dev_band_nom_power_param_map, dev_param_map), dev(nullptr),
 | 
			
		||||
	  rx_gain_min(0.0), rx_gain_max(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), drop_cnt(0),
 | 
			
		||||
	  prev_ts(0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -105,47 +90,6 @@ blade_device::~blade_device()
 | 
			
		||||
		delete rx_buffers[i];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void blade_device::assign_band_desc(enum gsm_band req_band)
 | 
			
		||||
{
 | 
			
		||||
	dev_band_map_it it;
 | 
			
		||||
 | 
			
		||||
	it = dev_band_nom_power_param_map.find(dev_band_key(dev_type, req_band));
 | 
			
		||||
	if (it == dev_band_nom_power_param_map.end()) {
 | 
			
		||||
		dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
 | 
			
		||||
		LOGC(DDEV, ERROR) << "No Power parameters exist for device " << desc.str << " on band "
 | 
			
		||||
				  << gsm_band_name(req_band) << ", using B210 ones as fallback";
 | 
			
		||||
		it = dev_band_nom_power_param_map.find(dev_band_key(blade_dev_type::BLADE2, req_band));
 | 
			
		||||
	}
 | 
			
		||||
	OSMO_ASSERT(it != dev_band_nom_power_param_map.end())
 | 
			
		||||
	band_desc = it->second;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool blade_device::set_band(enum gsm_band req_band)
 | 
			
		||||
{
 | 
			
		||||
	if (band_ass_curr_sess && req_band != band) {
 | 
			
		||||
		LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band) << " different from previous band "
 | 
			
		||||
				  << gsm_band_name(band);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (req_band != band) {
 | 
			
		||||
		band = req_band;
 | 
			
		||||
		assign_band_desc(band);
 | 
			
		||||
	}
 | 
			
		||||
	band_ass_curr_sess = true;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void blade_device::get_dev_band_desc(dev_band_desc &desc)
 | 
			
		||||
{
 | 
			
		||||
	if (band == 0) {
 | 
			
		||||
		LOGC(DDEV, ERROR)
 | 
			
		||||
			<< "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
 | 
			
		||||
		assign_band_desc(GSM_BAND_900);
 | 
			
		||||
	}
 | 
			
		||||
	desc = band_desc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void blade_device::init_gains()
 | 
			
		||||
{
 | 
			
		||||
	double tx_gain_min, tx_gain_max;
 | 
			
		||||
@@ -312,15 +256,15 @@ int blade_device::getNominalTxPower(size_t chan)
 | 
			
		||||
	return desc.nom_out_tx_power;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int blade_device::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
int blade_device::open()
 | 
			
		||||
{
 | 
			
		||||
	bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_VERBOSE);
 | 
			
		||||
	bladerf_set_usb_reset_on_open(true);
 | 
			
		||||
	auto success = bladerf_open(&dev, args.c_str());
 | 
			
		||||
	auto success = bladerf_open(&dev, cfg->dev_args);
 | 
			
		||||
	if (success != 0) {
 | 
			
		||||
		struct bladerf_devinfo *info;
 | 
			
		||||
		auto num_devs = bladerf_get_device_list(&info);
 | 
			
		||||
		LOGC(DDEV, ALERT) << "No bladerf devices found with identifier '" << args << "'";
 | 
			
		||||
		LOGC(DDEV, ALERT) << "No bladerf devices found with identifier '" << cfg->dev_args << "'";
 | 
			
		||||
		if (num_devs) {
 | 
			
		||||
			for (int i = 0; i < num_devs; i++)
 | 
			
		||||
				LOGC(DDEV, ALERT) << "Found device:" << info[i].product << " serial " << info[i].serial;
 | 
			
		||||
@@ -335,6 +279,7 @@ int blade_device::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
 | 
			
		||||
	dev_type = blade_dev_type::BLADE2;
 | 
			
		||||
	tx_window = TX_WINDOW_FIXED;
 | 
			
		||||
	update_band_dev(dev_key(dev_type, tx_sps, rx_sps));
 | 
			
		||||
 | 
			
		||||
	struct bladerf_devinfo info;
 | 
			
		||||
	bladerf_get_devinfo(dev, &info);
 | 
			
		||||
@@ -346,7 +291,7 @@ int blade_device::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
	rx_gains.resize(chans);
 | 
			
		||||
	rx_buffers.resize(chans);
 | 
			
		||||
 | 
			
		||||
	switch (ref) {
 | 
			
		||||
	switch (cfg->clock_ref) {
 | 
			
		||||
	case REF_INTERNAL:
 | 
			
		||||
	case REF_EXTERNAL:
 | 
			
		||||
		break;
 | 
			
		||||
@@ -355,7 +300,7 @@ int blade_device::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (ref == REF_EXTERNAL) {
 | 
			
		||||
	if (cfg->clock_ref == REF_EXTERNAL) {
 | 
			
		||||
		bool is_locked;
 | 
			
		||||
		int status = bladerf_set_pll_enable(dev, true);
 | 
			
		||||
		CHKRET()
 | 
			
		||||
@@ -374,7 +319,8 @@ int blade_device::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	LOGC(DDEV, INFO) << "Selected clock source is " << ((ref == REF_INTERNAL) ? "internal" : "external 10Mhz");
 | 
			
		||||
	LOGC(DDEV, INFO)
 | 
			
		||||
		<< "Selected clock source is " << ((cfg->clock_ref == REF_INTERNAL) ? "internal" : "external 10Mhz");
 | 
			
		||||
 | 
			
		||||
	set_rates();
 | 
			
		||||
 | 
			
		||||
@@ -457,7 +403,7 @@ bool blade_device::stop()
 | 
			
		||||
	for (size_t i = 0; i < rx_buffers.size(); i++)
 | 
			
		||||
		rx_buffers[i]->reset();
 | 
			
		||||
 | 
			
		||||
	band_ass_curr_sess = false;
 | 
			
		||||
	band_reset();
 | 
			
		||||
 | 
			
		||||
	started = false;
 | 
			
		||||
	return true;
 | 
			
		||||
@@ -580,27 +526,13 @@ bool blade_device::set_freq(double freq, size_t chan, bool tx)
 | 
			
		||||
 | 
			
		||||
bool blade_device::setTxFreq(double wFreq, size_t chan)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t req_arfcn;
 | 
			
		||||
	enum gsm_band req_band;
 | 
			
		||||
 | 
			
		||||
	if (chan >= tx_freqs.size()) {
 | 
			
		||||
		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	ScopedLock lock(tune_lock);
 | 
			
		||||
 | 
			
		||||
	req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 0);
 | 
			
		||||
	if (req_arfcn == 0xffff) {
 | 
			
		||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
 | 
			
		||||
		LOGCHAN(chan, DDEV, ALERT)
 | 
			
		||||
			<< "Unknown GSM band for Tx Frequency " << wFreq << " Hz (ARFCN " << req_arfcn << " )";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!set_band(req_band))
 | 
			
		||||
	if (!update_band_from_freq(wFreq, chan, true))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (!set_freq(wFreq, chan, true))
 | 
			
		||||
@@ -611,27 +543,13 @@ bool blade_device::setTxFreq(double wFreq, size_t chan)
 | 
			
		||||
 | 
			
		||||
bool blade_device::setRxFreq(double wFreq, size_t chan)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t req_arfcn;
 | 
			
		||||
	enum gsm_band req_band;
 | 
			
		||||
 | 
			
		||||
	if (chan >= rx_freqs.size()) {
 | 
			
		||||
		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	ScopedLock lock(tune_lock);
 | 
			
		||||
 | 
			
		||||
	req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
 | 
			
		||||
	if (req_arfcn == 0xffff) {
 | 
			
		||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
 | 
			
		||||
		LOGCHAN(chan, DDEV, ALERT)
 | 
			
		||||
			<< "Unknown GSM band for Rx Frequency " << wFreq << " Hz (ARFCN " << req_arfcn << " )";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!set_band(req_band))
 | 
			
		||||
	if (!update_band_from_freq(wFreq, chan, false))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return set_freq(wFreq, chan, false);
 | 
			
		||||
@@ -687,8 +605,7 @@ double blade_device::fullScaleOutputValue()
 | 
			
		||||
	return (double)2047;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
 | 
			
		||||
			       const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
 | 
			
		||||
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
 | 
			
		||||
{
 | 
			
		||||
	return new blade_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
 | 
			
		||||
	return new blade_device(type, cfg);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -22,10 +22,12 @@
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <map>
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "bandmanager.h"
 | 
			
		||||
#include "radioDevice.h"
 | 
			
		||||
#include "smpl_buf.h"
 | 
			
		||||
 | 
			
		||||
@@ -33,8 +35,6 @@ extern "C" {
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include <bladerf.h>
 | 
			
		||||
 | 
			
		||||
enum class blade_dev_type { BLADE1, BLADE2 };
 | 
			
		||||
 | 
			
		||||
struct dev_band_desc {
 | 
			
		||||
@@ -52,13 +52,26 @@ struct dev_band_desc {
 | 
			
		||||
	double rxgain2rssioffset_rel; /* dB */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class blade_device : public RadioDevice {
 | 
			
		||||
/* Device parameter descriptor */
 | 
			
		||||
struct dev_desc {
 | 
			
		||||
	unsigned channels;
 | 
			
		||||
	double mcr;
 | 
			
		||||
	double rate;
 | 
			
		||||
	double offset;
 | 
			
		||||
	std::string desc_str;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using dev_key = std::tuple<blade_dev_type, int, int>;
 | 
			
		||||
using dev_band_key = std::tuple<blade_dev_type, enum gsm_band>;
 | 
			
		||||
using power_map_t = std::map<dev_band_key, dev_band_desc>;
 | 
			
		||||
using dev_map_t = std::map<dev_key, dev_desc>;
 | 
			
		||||
 | 
			
		||||
class blade_device : public RadioDevice, public band_manager<power_map_t, dev_map_t> {
 | 
			
		||||
    public:
 | 
			
		||||
	blade_device(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chan_num, double offset,
 | 
			
		||||
		     const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths);
 | 
			
		||||
	blade_device(InterfaceType iface, const struct trx_cfg *cfg);
 | 
			
		||||
	~blade_device();
 | 
			
		||||
 | 
			
		||||
	int open(const std::string &args, int ref, bool swap_channels);
 | 
			
		||||
	int open();
 | 
			
		||||
	bool start();
 | 
			
		||||
	bool stop();
 | 
			
		||||
	bool restart();
 | 
			
		||||
@@ -154,9 +167,6 @@ class blade_device : public RadioDevice {
 | 
			
		||||
 | 
			
		||||
	std::vector<double> tx_gains, rx_gains;
 | 
			
		||||
	std::vector<double> tx_freqs, rx_freqs;
 | 
			
		||||
	bool band_ass_curr_sess; /* true if  "band" was set after last POWEROFF */
 | 
			
		||||
	enum gsm_band band;
 | 
			
		||||
	struct dev_band_desc band_desc;
 | 
			
		||||
	size_t tx_spp, rx_spp;
 | 
			
		||||
 | 
			
		||||
	bool started;
 | 
			
		||||
@@ -179,9 +189,6 @@ class blade_device : public RadioDevice {
 | 
			
		||||
	bool flush_recv(size_t num_pkts);
 | 
			
		||||
 | 
			
		||||
	bool set_freq(double freq, size_t chan, bool tx);
 | 
			
		||||
	void get_dev_band_desc(dev_band_desc &desc);
 | 
			
		||||
	bool set_band(enum gsm_band req_band);
 | 
			
		||||
	void assign_band_desc(enum gsm_band req_band);
 | 
			
		||||
 | 
			
		||||
	Thread *async_event_thrd;
 | 
			
		||||
	Mutex tune_lock;
 | 
			
		||||
 
 | 
			
		||||
@@ -4,7 +4,7 @@ AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES)
 | 
			
		||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = radioDevice.h smpl_buf.h
 | 
			
		||||
noinst_HEADERS = radioDevice.h smpl_buf.h bandmanager.h
 | 
			
		||||
 | 
			
		||||
noinst_LTLIBRARIES = libdevice_common.la
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										137
									
								
								Transceiver52M/device/common/bandmanager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								Transceiver52M/device/common/bandmanager.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Eric Wild <ewild@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <string>
 | 
			
		||||
#include <tuple>
 | 
			
		||||
 | 
			
		||||
#include "Logger.h"
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename powermapt, typename devmapt>
 | 
			
		||||
class band_manager {
 | 
			
		||||
	using powerkeyt = typename powermapt::key_type;
 | 
			
		||||
	using powermappedt = typename powermapt::mapped_type;
 | 
			
		||||
	using devkeyt = typename devmapt::key_type;
 | 
			
		||||
	devkeyt m_dev_type;
 | 
			
		||||
	const powermapt &m_power_map;
 | 
			
		||||
	const devmapt &m_dev_map;
 | 
			
		||||
	powerkeyt m_fallback;
 | 
			
		||||
	enum gsm_band m_band;
 | 
			
		||||
	powermappedt m_band_desc;
 | 
			
		||||
	bool band_ass_curr_sess{}; /* true if  "band" was set after last POWEROFF */
 | 
			
		||||
 | 
			
		||||
	// looks up either first tuple element (->enum) or straight enum
 | 
			
		||||
	template <typename T, typename std::enable_if<std::is_enum<T>::value>::type *dummy = nullptr>
 | 
			
		||||
	auto key_helper(T &t) -> T
 | 
			
		||||
	{
 | 
			
		||||
		return t;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	template <typename T>
 | 
			
		||||
	auto key_helper(T t) -> typename std::tuple_element<0, T>::type
 | 
			
		||||
	{
 | 
			
		||||
		return std::get<0>(t);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void assign_band_desc(enum gsm_band req_band)
 | 
			
		||||
	{
 | 
			
		||||
		auto key = key_helper(m_dev_type);
 | 
			
		||||
		auto fallback_key = key_helper(m_fallback);
 | 
			
		||||
		auto it = m_power_map.find({ key, req_band });
 | 
			
		||||
		if (it == m_power_map.end()) {
 | 
			
		||||
			auto desc = m_dev_map.at(m_dev_type);
 | 
			
		||||
			LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device " << desc.desc_str
 | 
			
		||||
					  << " on band " << gsm_band_name(req_band) << ", using fallback..";
 | 
			
		||||
			it = m_power_map.find({ fallback_key, req_band });
 | 
			
		||||
		}
 | 
			
		||||
		OSMO_ASSERT(it != m_power_map.end());
 | 
			
		||||
		m_band_desc = it->second;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool set_band(enum gsm_band req_band)
 | 
			
		||||
	{
 | 
			
		||||
		if (band_ass_curr_sess && req_band != m_band) {
 | 
			
		||||
			LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
 | 
			
		||||
					  << " different from previous band " << gsm_band_name(m_band);
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (req_band != m_band) {
 | 
			
		||||
			m_band = req_band;
 | 
			
		||||
			assign_band_desc(m_band);
 | 
			
		||||
		}
 | 
			
		||||
		band_ass_curr_sess = true;
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
	band_manager(const devkeyt &dev_type, const powermapt &power_map, const devmapt &dev_map, powerkeyt fallback)
 | 
			
		||||
		: m_dev_type(dev_type), m_power_map(power_map), m_dev_map(dev_map), m_fallback(fallback),
 | 
			
		||||
		  m_band((enum gsm_band)0)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
	band_manager(const powermapt &power_map, const devmapt &dev_map)
 | 
			
		||||
		: m_dev_type(dev_map.begin()->first), m_power_map(power_map), m_dev_map(dev_map),
 | 
			
		||||
		  m_fallback(m_power_map.begin()->first), m_band((enum gsm_band)0)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
	void band_reset()
 | 
			
		||||
	{
 | 
			
		||||
		band_ass_curr_sess = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void update_band_dev(devkeyt dev_type) {
 | 
			
		||||
		m_dev_type = dev_type;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void get_dev_band_desc(powermappedt &desc)
 | 
			
		||||
	{
 | 
			
		||||
		if (m_band == 0) {
 | 
			
		||||
			LOGC(DDEV, ERROR)
 | 
			
		||||
				<< "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
 | 
			
		||||
			assign_band_desc(GSM_BAND_900);
 | 
			
		||||
		}
 | 
			
		||||
		desc = m_band_desc;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool update_band_from_freq(double wFreq, int chan, bool is_tx)
 | 
			
		||||
	{
 | 
			
		||||
		enum gsm_band req_band;
 | 
			
		||||
		auto dirstr = is_tx ? "Tx" : "Rx";
 | 
			
		||||
		auto req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, !is_tx);
 | 
			
		||||
		if (req_arfcn == 0xffff) {
 | 
			
		||||
			LOGCHAN(chan, DDEV, ALERT)
 | 
			
		||||
				<< "Unknown ARFCN for " << dirstr << " Frequency " << wFreq / 1000 << " kHz";
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
 | 
			
		||||
			LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for " << dirstr << " Frequency " << wFreq
 | 
			
		||||
						   << " Hz (ARFCN " << req_arfcn << " )";
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return set_band(req_band);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
@@ -51,13 +51,10 @@ class RadioDevice {
 | 
			
		||||
    MULTI_ARFCN,
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type,
 | 
			
		||||
                           size_t chans = 1, double offset = 0.0,
 | 
			
		||||
                           const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""),
 | 
			
		||||
                           const std::vector<std::string>& rx_paths = std::vector<std::string>(1, ""));
 | 
			
		||||
  static RadioDevice *make(InterfaceType type, const struct trx_cfg *cfg);
 | 
			
		||||
 | 
			
		||||
  /** Initialize the USRP */
 | 
			
		||||
  virtual int open(const std::string &args, int ref, bool swap_channels)=0;
 | 
			
		||||
  virtual int open() = 0;
 | 
			
		||||
 | 
			
		||||
  virtual ~RadioDevice() { }
 | 
			
		||||
 | 
			
		||||
@@ -164,23 +161,30 @@ class RadioDevice {
 | 
			
		||||
  double lo_offset;
 | 
			
		||||
  std::vector<std::string> tx_paths, rx_paths;
 | 
			
		||||
  std::vector<struct device_counters> m_ctr;
 | 
			
		||||
  const struct trx_cfg *cfg;
 | 
			
		||||
 | 
			
		||||
  RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chan_num, double offset,
 | 
			
		||||
              const std::vector<std::string>& tx_paths,
 | 
			
		||||
              const std::vector<std::string>& rx_paths):
 | 
			
		||||
		tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chan_num), lo_offset(offset),
 | 
			
		||||
		tx_paths(tx_paths), rx_paths(rx_paths), m_ctr(chans)
 | 
			
		||||
	{
 | 
			
		||||
		if (iface == MULTI_ARFCN) {
 | 
			
		||||
			LOGC(DDEV, INFO) << "Multi-ARFCN: "<< chan_num << " logical chans -> 1 physical chans";
 | 
			
		||||
			chans = 1;
 | 
			
		||||
		}
 | 
			
		||||
#define charp2str(a) ((a) ? std::string(a) : std::string(""))
 | 
			
		||||
 | 
			
		||||
		for (size_t i = 0; i < chans; i++) {
 | 
			
		||||
			memset(&m_ctr[i], 0, sizeof(m_ctr[i]));
 | 
			
		||||
			m_ctr[i].chan = i;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
  RadioDevice(InterfaceType type, const struct trx_cfg *cfg)
 | 
			
		||||
	  : tx_sps(cfg->tx_sps), rx_sps(cfg->rx_sps), iface(type), chans(cfg->num_chans), lo_offset(cfg->offset),
 | 
			
		||||
	    m_ctr(chans), cfg(cfg)
 | 
			
		||||
  {
 | 
			
		||||
	  /* Generate vector of rx/tx_path: */
 | 
			
		||||
	  for (unsigned int i = 0; i < cfg->num_chans; i++) {
 | 
			
		||||
		  rx_paths.push_back(charp2str(cfg->chans[i].rx_path));
 | 
			
		||||
		  tx_paths.push_back(charp2str(cfg->chans[i].tx_path));
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
	  if (iface == MULTI_ARFCN) {
 | 
			
		||||
		  LOGC(DDEV, INFO) << "Multi-ARFCN: " << chans << " logical chans -> 1 physical chans";
 | 
			
		||||
		  chans = 1;
 | 
			
		||||
	  }
 | 
			
		||||
 | 
			
		||||
	  for (size_t i = 0; i < chans; i++) {
 | 
			
		||||
		  memset(&m_ctr[i], 0, sizeof(m_ctr[i]));
 | 
			
		||||
		  m_ctr[i].chan = i;
 | 
			
		||||
	  }
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  bool set_antennas() {
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
 
 | 
			
		||||
@@ -56,11 +56,10 @@ using namespace std;
 | 
			
		||||
 | 
			
		||||
static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags);
 | 
			
		||||
 | 
			
		||||
IPCDevice::IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
 | 
			
		||||
		     const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
 | 
			
		||||
	: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths), sk_chan_state(chans, ipc_per_trx_sock_state()),
 | 
			
		||||
	  tx_attenuation(), tmp_state(IPC_IF_MSG_GREETING_REQ), shm(NULL), shm_dec(0),
 | 
			
		||||
	  rx_buffers(chans), started(false), tx_gains(chans), rx_gains(chans)
 | 
			
		||||
IPCDevice::IPCDevice(InterfaceType iface, const struct trx_cfg *cfg)
 | 
			
		||||
	: RadioDevice(iface, cfg), sk_chan_state(chans, ipc_per_trx_sock_state()), tx_attenuation(),
 | 
			
		||||
	  tmp_state(IPC_IF_MSG_GREETING_REQ), shm(NULL), shm_dec(0), rx_buffers(chans), started(false), tx_gains(chans),
 | 
			
		||||
	  rx_gains(chans)
 | 
			
		||||
{
 | 
			
		||||
	LOGC(DDEV, INFO) << "creating IPC device...";
 | 
			
		||||
 | 
			
		||||
@@ -537,9 +536,9 @@ void IPCDevice::ipc_sock_close(struct ipc_per_trx_sock_state *state)
 | 
			
		||||
 | 
			
		||||
	LOGP(DDEV, LOGL_NOTICE, "IPC socket has LOST connection\n");
 | 
			
		||||
 | 
			
		||||
	osmo_fd_unregister(bfd);
 | 
			
		||||
	close(bfd->fd);
 | 
			
		||||
	bfd->fd = -1;
 | 
			
		||||
	osmo_fd_unregister(bfd);
 | 
			
		||||
 | 
			
		||||
	/* flush the queue */
 | 
			
		||||
	while (!llist_empty(&state->upqueue)) {
 | 
			
		||||
@@ -771,11 +770,12 @@ static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags)
 | 
			
		||||
	return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int IPCDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
int IPCDevice::open()
 | 
			
		||||
{
 | 
			
		||||
	std::string k, v;
 | 
			
		||||
	std::string::size_type keyend;
 | 
			
		||||
	int rc;
 | 
			
		||||
	std::string args(cfg->dev_args);
 | 
			
		||||
 | 
			
		||||
	if ((keyend = args.find('=')) != std::string::npos) {
 | 
			
		||||
		k = args.substr(0, keyend++);
 | 
			
		||||
@@ -810,7 +810,7 @@ int IPCDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
	while (tmp_state != IPC_IF_MSG_INFO_CNF)
 | 
			
		||||
		osmo_select_main(0);
 | 
			
		||||
 | 
			
		||||
	ipc_tx_open_req(&master_sk_state, chans, ref);
 | 
			
		||||
	ipc_tx_open_req(&master_sk_state, chans, cfg->clock_ref);
 | 
			
		||||
	/* Wait until confirmation is recieved */
 | 
			
		||||
	while (tmp_state != IPC_IF_MSG_OPEN_CNF)
 | 
			
		||||
		osmo_select_main(0);
 | 
			
		||||
@@ -1259,16 +1259,15 @@ bool IPCDevice::setRxFreq(double wFreq, size_t chan)
 | 
			
		||||
	return send_chan_wait_rsp(chan, msg, IPC_IF_MSG_SETFREQ_CNF);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
 | 
			
		||||
			       const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
 | 
			
		||||
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
 | 
			
		||||
{
 | 
			
		||||
	if (tx_sps != rx_sps) {
 | 
			
		||||
	if (cfg->tx_sps != cfg->rx_sps) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "IPC Requires tx_sps == rx_sps";
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	if (lo_offset != 0.0) {
 | 
			
		||||
	if (cfg->offset != 0.0) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "IPC doesn't support lo_offset";
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	return new IPCDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
 | 
			
		||||
	return new IPCDevice(type, cfg);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -115,12 +115,11 @@ class IPCDevice : public RadioDevice {
 | 
			
		||||
	int ipc_chan_sock_write(osmo_fd *bfd);
 | 
			
		||||
 | 
			
		||||
	/** Object constructor */
 | 
			
		||||
	IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
 | 
			
		||||
		  const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths);
 | 
			
		||||
	IPCDevice(InterfaceType iface, const struct trx_cfg *cfg);
 | 
			
		||||
	virtual ~IPCDevice() override;
 | 
			
		||||
 | 
			
		||||
	/** Instantiate the IPC */
 | 
			
		||||
	virtual int open(const std::string &args, int ref, bool swap_channels) override;
 | 
			
		||||
	virtual int open() override;
 | 
			
		||||
 | 
			
		||||
	/** Start the IPC */
 | 
			
		||||
	virtual bool start() override;
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,8 @@
 | 
			
		||||
include $(top_srcdir)/Makefile.common
 | 
			
		||||
 | 
			
		||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
 | 
			
		||||
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
 | 
			
		||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
 | 
			
		||||
AM_LDFLAGS = -lpthread -lrt
 | 
			
		||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
 | 
			
		||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(UHD_CFLAGS)
 | 
			
		||||
AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(UHD_CFLAGS)
 | 
			
		||||
 | 
			
		||||
noinst_HEADERS = IPCDevice.h shm.h ipc_shm.h ipc_chan.h ipc_sock.h
 | 
			
		||||
 | 
			
		||||
@@ -14,22 +13,22 @@ endif
 | 
			
		||||
noinst_LTLIBRARIES = libdevice.la
 | 
			
		||||
 | 
			
		||||
libdevice_la_SOURCES = IPCDevice.cpp shm.c ipc_shm.c ipc_chan.c ipc_sock.c
 | 
			
		||||
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
 | 
			
		||||
libdevice_la_CXXFLAGS = $(AM_CXXFLAGS) -DIPCMAGIC
 | 
			
		||||
libdevice_la_CPPFLAGS = $(AM_CPPFLAGS) -DIPCMAGIC
 | 
			
		||||
libdevice_la_LIBADD = \
 | 
			
		||||
	$(top_builddir)/Transceiver52M/device/common/libdevice_common.la \
 | 
			
		||||
	-lpthread \
 | 
			
		||||
	-lrt \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
if DEVICE_UHD
 | 
			
		||||
 | 
			
		||||
bin_PROGRAMS = ipc-driver-test
 | 
			
		||||
#ipc_driver_test_SHORTNAME = drvt
 | 
			
		||||
ipc_driver_test_SOURCES = ipc-driver-test.c uhdwrap.cpp ipc_shm.c ipc_chan.c ipc_sock.c ../uhd/UHDDevice.cpp
 | 
			
		||||
ipc_driver_test_SOURCES = ipc-driver-test.c uhdwrap.cpp ../uhd/UHDDevice.cpp
 | 
			
		||||
ipc_driver_test_LDADD = \
 | 
			
		||||
        shm.lo \
 | 
			
		||||
	$(top_builddir)/Transceiver52M/device/common/libdevice_common.la \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
	libdevice.la \
 | 
			
		||||
	$(COMMON_LA) \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(UHD_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
ipc_driver_test_CXXFLAGS = $(AM_CXXFLAGS) $(UHD_CFLAGS)
 | 
			
		||||
ipc_driver_test_CPPFLAGS  = $(AM_CPPFLAGS) $(UHD_CFLAGS)
 | 
			
		||||
ipc_driver_test_CFLAGS  = $(AM_CFLAGS) $(UHD_CFLAGS)
 | 
			
		||||
ipc_driver_test_LDFLAGS  = $(AM_LDFLAGS) $(UHD_LIBS)
 | 
			
		||||
endif
 | 
			
		||||
 
 | 
			
		||||
@@ -97,9 +97,9 @@ void ipc_sock_close(struct ipc_sock_state *state)
 | 
			
		||||
 | 
			
		||||
	ipc_exit_requested = 1;
 | 
			
		||||
 | 
			
		||||
	osmo_fd_unregister(bfd);
 | 
			
		||||
	close(bfd->fd);
 | 
			
		||||
	bfd->fd = -1;
 | 
			
		||||
	osmo_fd_unregister(bfd);
 | 
			
		||||
 | 
			
		||||
	/* re-enable the generation of ACCEPT for new connections */
 | 
			
		||||
	osmo_fd_read_enable(&state->listen_bfd);
 | 
			
		||||
 
 | 
			
		||||
@@ -35,9 +35,12 @@ extern "C" {
 | 
			
		||||
#include "Threads.h"
 | 
			
		||||
#include "Utils.h"
 | 
			
		||||
 | 
			
		||||
int uhd_wrap::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
// no vty source for cfg params here, so we have to build our own
 | 
			
		||||
static struct trx_cfg actual_cfg = {};
 | 
			
		||||
 | 
			
		||||
int uhd_wrap::open()
 | 
			
		||||
{
 | 
			
		||||
	int rv = uhd_device::open(args, ref, swap_channels);
 | 
			
		||||
	int rv = uhd_device::open();
 | 
			
		||||
	samps_per_buff_rx = rx_stream->get_max_num_samps();
 | 
			
		||||
	samps_per_buff_tx = tx_stream->get_max_num_samps();
 | 
			
		||||
	channel_count = usrp_dev->get_rx_num_channels();
 | 
			
		||||
@@ -84,36 +87,33 @@ int uhd_wrap::wrap_read(TIMESTAMP *timestamp)
 | 
			
		||||
 | 
			
		||||
extern "C" void *uhdwrap_open(struct ipc_sk_if_open_req *open_req)
 | 
			
		||||
{
 | 
			
		||||
	unsigned int rx_sps, tx_sps;
 | 
			
		||||
	actual_cfg.num_chans = open_req->num_chans;
 | 
			
		||||
	actual_cfg.swap_channels = false;
 | 
			
		||||
	/* FIXME: this is actually the sps value, not the sample rate!
 | 
			
		||||
	 * sample rate is looked up according to the sps rate by uhd backend */
 | 
			
		||||
	actual_cfg.rx_sps = open_req->rx_sample_freq_num / open_req->rx_sample_freq_den;
 | 
			
		||||
	actual_cfg.tx_sps = open_req->tx_sample_freq_num / open_req->tx_sample_freq_den;
 | 
			
		||||
 | 
			
		||||
	/* FIXME: dev arg string* */
 | 
			
		||||
	/* FIXME: rx frontend bw? */
 | 
			
		||||
	/* FIXME: tx frontend bw? */
 | 
			
		||||
	ReferenceType cref;
 | 
			
		||||
	switch (open_req->clockref) {
 | 
			
		||||
	case FEATURE_MASK_CLOCKREF_EXTERNAL:
 | 
			
		||||
		cref = ReferenceType::REF_EXTERNAL;
 | 
			
		||||
		actual_cfg.clock_ref = ReferenceType::REF_EXTERNAL;
 | 
			
		||||
		break;
 | 
			
		||||
	case FEATURE_MASK_CLOCKREF_INTERNAL:
 | 
			
		||||
	default:
 | 
			
		||||
		cref = ReferenceType::REF_INTERNAL;
 | 
			
		||||
		actual_cfg.clock_ref = ReferenceType::REF_INTERNAL;
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::vector<std::string> tx_paths;
 | 
			
		||||
	std::vector<std::string> rx_paths;
 | 
			
		||||
	for (unsigned int i = 0; i < open_req->num_chans; i++) {
 | 
			
		||||
		tx_paths.push_back(open_req->chan_info[i].tx_path);
 | 
			
		||||
		rx_paths.push_back(open_req->chan_info[i].rx_path);
 | 
			
		||||
		actual_cfg.chans[i].rx_path = open_req->chan_info[i].tx_path;
 | 
			
		||||
		actual_cfg.chans[i].tx_path = open_req->chan_info[i].rx_path;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* FIXME: this is actually the sps value, not the sample rate!
 | 
			
		||||
	 * sample rate is looked up according to the sps rate by uhd backend */
 | 
			
		||||
	rx_sps = open_req->rx_sample_freq_num / open_req->rx_sample_freq_den;
 | 
			
		||||
	tx_sps = open_req->tx_sample_freq_num / open_req->tx_sample_freq_den;
 | 
			
		||||
	uhd_wrap *uhd_wrap_dev =
 | 
			
		||||
		new uhd_wrap(tx_sps, rx_sps, RadioDevice::NORMAL, open_req->num_chans, 0.0, tx_paths, rx_paths);
 | 
			
		||||
	uhd_wrap_dev->open("", cref, false);
 | 
			
		||||
	uhd_wrap *uhd_wrap_dev = new uhd_wrap(RadioDevice::NORMAL, &actual_cfg);
 | 
			
		||||
	uhd_wrap_dev->open();
 | 
			
		||||
 | 
			
		||||
	return uhd_wrap_dev;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -41,7 +41,7 @@ class uhd_wrap : public uhd_device {
 | 
			
		||||
 | 
			
		||||
	//    void ipc_sock_close() override {};
 | 
			
		||||
	int wrap_read(TIMESTAMP *timestamp);
 | 
			
		||||
	virtual int open(const std::string &args, int ref, bool swap_channels) override;
 | 
			
		||||
	virtual int open() override;
 | 
			
		||||
 | 
			
		||||
	//	bool start() override;
 | 
			
		||||
	//	bool stop() override;
 | 
			
		||||
 
 | 
			
		||||
@@ -52,39 +52,16 @@ extern "C" {
 | 
			
		||||
#define LMS_DEV_SDR_MINI_PREFIX_NAME "LimeSDR-Mini"
 | 
			
		||||
#define LMS_DEV_NET_MICRO_PREFIX_NAME "LimeNET-Micro"
 | 
			
		||||
 | 
			
		||||
/* Device parameter descriptor */
 | 
			
		||||
struct dev_desc {
 | 
			
		||||
	/* Does LimeSuite allow switching the clock source for this device?
 | 
			
		||||
	 * LimeSDR-Mini does not have switches but needs soldering to select
 | 
			
		||||
	 * external/internal clock. Any call to LMS_SetClockFreq() will fail.
 | 
			
		||||
	 */
 | 
			
		||||
	bool clock_src_switchable;
 | 
			
		||||
	/* Does LimeSuite allow using REF_INTERNAL for this device?
 | 
			
		||||
	 * LimeNET-Micro does not like selecting internal clock
 | 
			
		||||
	 */
 | 
			
		||||
	bool clock_src_int_usable;
 | 
			
		||||
	/* Sample rate coef (without having TX/RX samples per symbol into account) */
 | 
			
		||||
	double rate;
 | 
			
		||||
	/* Sample rate coef (without having TX/RX samples per symbol into account), if multi-arfcn is enabled */
 | 
			
		||||
	double rate_multiarfcn;
 | 
			
		||||
	/* Coefficient multiplied by TX sample rate in order to shift Tx time */
 | 
			
		||||
	double ts_offset_coef;
 | 
			
		||||
	/* Coefficient multiplied by TX sample rate in order to shift Tx time, if multi-arfcn is enabled */
 | 
			
		||||
	double ts_offset_coef_multiarfcn;
 | 
			
		||||
	/* Device Name Prefix as presented by LimeSuite API LMS_GetDeviceInfo() */
 | 
			
		||||
	std::string name_prefix;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const std::map<enum lms_dev_type, struct dev_desc> dev_param_map {
 | 
			
		||||
 | 
			
		||||
static const dev_map_t dev_param_map {
 | 
			
		||||
	{ LMS_DEV_SDR_USB,   { true,  true,  GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_SDR_USB_PREFIX_NAME } },
 | 
			
		||||
	{ LMS_DEV_SDR_MINI,  { false, true,  GSMRATE, MCBTS_SPACING, 8.9e-5, 8.2e-5, LMS_DEV_SDR_MINI_PREFIX_NAME } },
 | 
			
		||||
	{ LMS_DEV_NET_MICRO, { true,  false, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_NET_MICRO_PREFIX_NAME } },
 | 
			
		||||
	{ LMS_DEV_UNKNOWN,   { true,  true,  GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, "UNKNOWN" } },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef std::tuple<lms_dev_type, enum gsm_band> dev_band_key;
 | 
			
		||||
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
 | 
			
		||||
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
 | 
			
		||||
static const power_map_t dev_band_nom_power_param_map {
 | 
			
		||||
	{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_850),	{ 73.0, 11.2,  -6.0  } },
 | 
			
		||||
	{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_900),	{ 73.0, 10.8,  -6.0  } },
 | 
			
		||||
	{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_1800),	{ 65.0, -3.5,  -17.0 } }, /* FIXME: OS#4583: 1800Mhz is failing above TxGain=65, which is around -3.5dBm (already < 0 dBm) */
 | 
			
		||||
@@ -122,8 +99,8 @@ static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
 | 
			
		||||
		enum lms_dev_type dev_type = it->first;
 | 
			
		||||
		struct dev_desc desc = it->second;
 | 
			
		||||
 | 
			
		||||
		if (strncmp(device_info->deviceName, desc.name_prefix.c_str(), desc.name_prefix.length()) == 0) {
 | 
			
		||||
			LOGC(DDEV, INFO) << "Device identified as " << desc.name_prefix;
 | 
			
		||||
		if (strncmp(device_info->deviceName, desc.desc_str.c_str(), desc.desc_str.length()) == 0) {
 | 
			
		||||
			LOGC(DDEV, INFO) << "Device identified as " << desc.desc_str;
 | 
			
		||||
			return dev_type;
 | 
			
		||||
		}
 | 
			
		||||
		it++;
 | 
			
		||||
@@ -131,12 +108,10 @@ static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
 | 
			
		||||
	return LMS_DEV_UNKNOWN;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
 | 
			
		||||
		     const std::vector<std::string>& tx_paths,
 | 
			
		||||
		     const std::vector<std::string>& rx_paths):
 | 
			
		||||
		     RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
 | 
			
		||||
		     m_lms_dev(NULL), started(false), band_ass_curr_sess(false), band((enum gsm_band)0),
 | 
			
		||||
		     m_dev_type(LMS_DEV_UNKNOWN)
 | 
			
		||||
LMSDevice::LMSDevice(InterfaceType iface, const struct trx_cfg *cfg)
 | 
			
		||||
	: RadioDevice(iface, cfg),
 | 
			
		||||
	  band_manager(m_dev_type, dev_band_nom_power_param_map, dev_param_map, {LMS_DEV_SDR_USB, GSM_BAND_850}), m_lms_dev(NULL),
 | 
			
		||||
	  started(false), m_dev_type(LMS_DEV_UNKNOWN)
 | 
			
		||||
{
 | 
			
		||||
	LOGC(DDEV, INFO) << "creating LMS device...";
 | 
			
		||||
 | 
			
		||||
@@ -223,48 +198,7 @@ int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::str
 | 
			
		||||
	return -1;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LMSDevice::assign_band_desc(enum gsm_band req_band)
 | 
			
		||||
{
 | 
			
		||||
	dev_band_map_it it;
 | 
			
		||||
 | 
			
		||||
	it = dev_band_nom_power_param_map.find(dev_band_key(m_dev_type, req_band));
 | 
			
		||||
	if (it == dev_band_nom_power_param_map.end()) {
 | 
			
		||||
		dev_desc desc = dev_param_map.at(m_dev_type);
 | 
			
		||||
		LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device "
 | 
			
		||||
				    << desc.name_prefix << " on band " << gsm_band_name(req_band)
 | 
			
		||||
				    << ", using LimeSDR-USB ones as fallback";
 | 
			
		||||
		it = dev_band_nom_power_param_map.find(dev_band_key(LMS_DEV_SDR_USB, req_band));
 | 
			
		||||
	}
 | 
			
		||||
	OSMO_ASSERT(it != dev_band_nom_power_param_map.end());
 | 
			
		||||
	band_desc = it->second;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool LMSDevice::set_band(enum gsm_band req_band)
 | 
			
		||||
{
 | 
			
		||||
	if (band_ass_curr_sess && req_band != band) {
 | 
			
		||||
		LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
 | 
			
		||||
				  << " different from previous band " << gsm_band_name(band);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (req_band != band) {
 | 
			
		||||
		band = req_band;
 | 
			
		||||
		assign_band_desc(band);
 | 
			
		||||
	}
 | 
			
		||||
	band_ass_curr_sess = true;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void LMSDevice::get_dev_band_desc(dev_band_desc& desc)
 | 
			
		||||
{
 | 
			
		||||
	if (band == 0) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
 | 
			
		||||
		assign_band_desc(GSM_BAND_900);
 | 
			
		||||
	}
 | 
			
		||||
	desc = band_desc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
int LMSDevice::open()
 | 
			
		||||
{
 | 
			
		||||
	lms_info_str_t* info_list;
 | 
			
		||||
	lms_range_t range_sr;
 | 
			
		||||
@@ -292,9 +226,9 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
	for (i = 0; i < n; i++)
 | 
			
		||||
		LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i];
 | 
			
		||||
 | 
			
		||||
	dev_id = info_list_find(info_list, n, args);
 | 
			
		||||
	dev_id = info_list_find(info_list, n, cfg->dev_args);
 | 
			
		||||
	if (dev_id == -1) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "No LMS device found with address '" << args << "'";
 | 
			
		||||
		LOGC(DDEV, ERROR) << "No LMS device found with address '" << cfg->dev_args << "'";
 | 
			
		||||
		delete[] info_list;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -311,14 +245,15 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
 | 
			
		||||
	m_dev_type = parse_dev_type(m_lms_dev);
 | 
			
		||||
	dev_desc = dev_param_map.at(m_dev_type);
 | 
			
		||||
	update_band_dev(m_dev_type);
 | 
			
		||||
 | 
			
		||||
	if ((ref != REF_EXTERNAL) && (ref != REF_INTERNAL)){
 | 
			
		||||
	if ((cfg->clock_ref != REF_EXTERNAL) && (cfg->clock_ref != REF_INTERNAL)) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "Invalid reference type";
 | 
			
		||||
		goto out_close;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* if reference clock is external, setup must happen _before_ calling LMS_Init */
 | 
			
		||||
	if (ref == REF_EXTERNAL) {
 | 
			
		||||
	if (cfg->clock_ref == REF_EXTERNAL) {
 | 
			
		||||
		LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz";
 | 
			
		||||
		/* FIXME: Assume an external 10 MHz reference clock. make
 | 
			
		||||
		   external reference frequency configurable */
 | 
			
		||||
@@ -333,7 +268,7 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* if reference clock is internal, setup must happen _after_ calling LMS_Init */
 | 
			
		||||
	if (ref == REF_INTERNAL) {
 | 
			
		||||
	if (cfg->clock_ref == REF_INTERNAL) {
 | 
			
		||||
		LOGC(DDEV, INFO) << "Setting Internal clock reference";
 | 
			
		||||
		/* Internal freq param is not used */
 | 
			
		||||
		if (!do_clock_src_freq(REF_INTERNAL, 0))
 | 
			
		||||
@@ -469,7 +404,7 @@ bool LMSDevice::stop()
 | 
			
		||||
		LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	band_ass_curr_sess = false;
 | 
			
		||||
	band_reset();
 | 
			
		||||
 | 
			
		||||
	started = false;
 | 
			
		||||
	return true;
 | 
			
		||||
@@ -486,8 +421,8 @@ bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
 | 
			
		||||
		break;
 | 
			
		||||
	case REF_INTERNAL:
 | 
			
		||||
		if (!dev_desc.clock_src_int_usable) {
 | 
			
		||||
			LOGC(DDEV, ERROR) << "Device type " << dev_desc.name_prefix
 | 
			
		||||
					  << " doesn't support internal reference clock";
 | 
			
		||||
			LOGC(DDEV, ERROR)
 | 
			
		||||
				<< "Device type " << dev_desc.desc_str << " doesn't support internal reference clock";
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
		/* According to lms using LMS_CLOCK_EXTREF with a
 | 
			
		||||
@@ -505,8 +440,8 @@ bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
 | 
			
		||||
		if (LMS_SetClockFreq(m_lms_dev, lms_clk_id, freq) < 0)
 | 
			
		||||
			return false;
 | 
			
		||||
	} else {
 | 
			
		||||
		LOGC(DDEV, INFO) << "Device type " << dev_desc.name_prefix
 | 
			
		||||
				 << " doesn't support switching clock source through SW";
 | 
			
		||||
		LOGC(DDEV, INFO)
 | 
			
		||||
			<< "Device type " << dev_desc.desc_str << " doesn't support switching clock source through SW";
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
@@ -999,9 +934,6 @@ bool LMSDevice::updateAlignment(TIMESTAMP timestamp)
 | 
			
		||||
 | 
			
		||||
bool LMSDevice::setTxFreq(double wFreq, size_t chan)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t req_arfcn;
 | 
			
		||||
	enum gsm_band req_band;
 | 
			
		||||
 | 
			
		||||
	if (chan >= chans) {
 | 
			
		||||
		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
			
		||||
		return false;
 | 
			
		||||
@@ -1009,18 +941,7 @@ bool LMSDevice::setTxFreq(double wFreq, size_t chan)
 | 
			
		||||
 | 
			
		||||
	LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz";
 | 
			
		||||
 | 
			
		||||
	req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
 | 
			
		||||
	if (req_arfcn == 0xffff) {
 | 
			
		||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
 | 
			
		||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
 | 
			
		||||
					   << " Hz (ARFCN " << req_arfcn << " )";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!set_band(req_band))
 | 
			
		||||
	if (!update_band_from_freq(wFreq, chan, true))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
 | 
			
		||||
@@ -1033,23 +954,9 @@ bool LMSDevice::setTxFreq(double wFreq, size_t chan)
 | 
			
		||||
 | 
			
		||||
bool LMSDevice::setRxFreq(double wFreq, size_t chan)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t req_arfcn;
 | 
			
		||||
	enum gsm_band req_band;
 | 
			
		||||
 | 
			
		||||
	LOGCHAN(chan, DDEV, NOTICE) << "Setting Rx Freq to " << wFreq << " Hz";
 | 
			
		||||
 | 
			
		||||
	req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
 | 
			
		||||
	if (req_arfcn == 0xffff) {
 | 
			
		||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
 | 
			
		||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq
 | 
			
		||||
					   << " Hz (ARFCN " << req_arfcn << " )";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!set_band(req_band))
 | 
			
		||||
	if (!update_band_from_freq(wFreq, chan, false))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) {
 | 
			
		||||
@@ -1060,18 +967,15 @@ bool LMSDevice::setRxFreq(double wFreq, size_t chan)
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
 | 
			
		||||
			       InterfaceType iface, size_t chans, double lo_offset,
 | 
			
		||||
			       const std::vector < std::string > &tx_paths,
 | 
			
		||||
			       const std::vector < std::string > &rx_paths)
 | 
			
		||||
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
 | 
			
		||||
{
 | 
			
		||||
	if (tx_sps != rx_sps) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "LMS Requires tx_sps == rx_sps";
 | 
			
		||||
	if (cfg->tx_sps != cfg->rx_sps) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "LMS requires tx_sps == rx_sps";
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	if (lo_offset != 0.0) {
 | 
			
		||||
	if (cfg->offset != 0.0) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset";
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	return new LMSDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
 | 
			
		||||
	return new LMSDevice(type, cfg);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -18,11 +18,13 @@
 | 
			
		||||
#ifndef _LMS_DEVICE_H_
 | 
			
		||||
#define _LMS_DEVICE_H_
 | 
			
		||||
 | 
			
		||||
#include <map>
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "radioDevice.h"
 | 
			
		||||
#include "bandmanager.h"
 | 
			
		||||
#include "smpl_buf.h"
 | 
			
		||||
 | 
			
		||||
#include <sys/time.h>
 | 
			
		||||
@@ -69,8 +71,35 @@ struct dev_band_desc {
 | 
			
		||||
	double rxgain2rssioffset_rel; /* dB */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Device parameter descriptor */
 | 
			
		||||
struct dev_desc {
 | 
			
		||||
	/* Does LimeSuite allow switching the clock source for this device?
 | 
			
		||||
	 * LimeSDR-Mini does not have switches but needs soldering to select
 | 
			
		||||
	 * external/internal clock. Any call to LMS_SetClockFreq() will fail.
 | 
			
		||||
	 */
 | 
			
		||||
	bool clock_src_switchable;
 | 
			
		||||
	/* Does LimeSuite allow using REF_INTERNAL for this device?
 | 
			
		||||
	 * LimeNET-Micro does not like selecting internal clock
 | 
			
		||||
	 */
 | 
			
		||||
	bool clock_src_int_usable;
 | 
			
		||||
	/* Sample rate coef (without having TX/RX samples per symbol into account) */
 | 
			
		||||
	double rate;
 | 
			
		||||
	/* Sample rate coef (without having TX/RX samples per symbol into account), if multi-arfcn is enabled */
 | 
			
		||||
	double rate_multiarfcn;
 | 
			
		||||
	/* Coefficient multiplied by TX sample rate in order to shift Tx time */
 | 
			
		||||
	double ts_offset_coef;
 | 
			
		||||
	/* Coefficient multiplied by TX sample rate in order to shift Tx time, if multi-arfcn is enabled */
 | 
			
		||||
	double ts_offset_coef_multiarfcn;
 | 
			
		||||
	/* Device Name Prefix as presented by LimeSuite API LMS_GetDeviceInfo() */
 | 
			
		||||
	std::string desc_str;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using dev_band_key_t = std::tuple<lms_dev_type, gsm_band>;
 | 
			
		||||
using power_map_t = std::map<dev_band_key_t, dev_band_desc>;
 | 
			
		||||
using dev_map_t = std::map<lms_dev_type, struct dev_desc>;
 | 
			
		||||
 | 
			
		||||
/** A class to handle a LimeSuite supported device */
 | 
			
		||||
class LMSDevice:public RadioDevice {
 | 
			
		||||
class LMSDevice:public RadioDevice, public band_manager<power_map_t, dev_map_t> {
 | 
			
		||||
 | 
			
		||||
private:
 | 
			
		||||
	lms_device_t *m_lms_dev;
 | 
			
		||||
@@ -87,9 +116,6 @@ private:
 | 
			
		||||
	TIMESTAMP ts_initial, ts_offset;
 | 
			
		||||
 | 
			
		||||
	std::vector<double> tx_gains, rx_gains;
 | 
			
		||||
	bool band_ass_curr_sess; /* true if  "band" was set after last POWEROFF */
 | 
			
		||||
	enum gsm_band band;
 | 
			
		||||
	struct dev_band_desc band_desc;
 | 
			
		||||
 | 
			
		||||
	enum lms_dev_type m_dev_type;
 | 
			
		||||
 | 
			
		||||
@@ -101,29 +127,25 @@ private:
 | 
			
		||||
	void update_stream_stats_rx(size_t chan, bool *overrun);
 | 
			
		||||
	void update_stream_stats_tx(size_t chan, bool *underrun);
 | 
			
		||||
	bool do_clock_src_freq(enum ReferenceType ref, double freq);
 | 
			
		||||
	void get_dev_band_desc(dev_band_desc& desc);
 | 
			
		||||
	bool set_band(enum gsm_band req_band);
 | 
			
		||||
	void assign_band_desc(enum gsm_band req_band);
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
	/** Object constructor */
 | 
			
		||||
	LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
 | 
			
		||||
		  const std::vector<std::string>& tx_paths,
 | 
			
		||||
		  const std::vector<std::string>& rx_paths);
 | 
			
		||||
	~LMSDevice();
 | 
			
		||||
    LMSDevice(InterfaceType iface, const struct trx_cfg *cfg);
 | 
			
		||||
    ~LMSDevice();
 | 
			
		||||
 | 
			
		||||
	/** Instantiate the LMS */
 | 
			
		||||
	int open(const std::string &args, int ref, bool swap_channels);
 | 
			
		||||
    /** Instantiate the LMS */
 | 
			
		||||
    int open();
 | 
			
		||||
 | 
			
		||||
	/** Start the LMS */
 | 
			
		||||
	bool start();
 | 
			
		||||
    /** Start the LMS */
 | 
			
		||||
    bool start();
 | 
			
		||||
 | 
			
		||||
	/** Stop the LMS */
 | 
			
		||||
	bool stop();
 | 
			
		||||
    /** Stop the LMS */
 | 
			
		||||
    bool stop();
 | 
			
		||||
 | 
			
		||||
	enum TxWindowType getWindowType() {
 | 
			
		||||
		return TX_WINDOW_LMS1;
 | 
			
		||||
	}
 | 
			
		||||
    enum TxWindowType getWindowType()
 | 
			
		||||
    {
 | 
			
		||||
	    return TX_WINDOW_LMS1;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	/**
 | 
			
		||||
	Read samples from the LMS.
 | 
			
		||||
 
 | 
			
		||||
@@ -91,19 +91,7 @@ extern "C" {
 | 
			
		||||
 *   USRP1 with timestamps is not supported by UHD.
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
/* Device Type, Tx-SPS, Rx-SPS */
 | 
			
		||||
typedef std::tuple<uhd_dev_type, int, int> dev_key;
 | 
			
		||||
 | 
			
		||||
/* Device parameter descriptor */
 | 
			
		||||
struct dev_desc {
 | 
			
		||||
	unsigned channels;
 | 
			
		||||
	double mcr;
 | 
			
		||||
	double rate;
 | 
			
		||||
	double offset;
 | 
			
		||||
	std::string str;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const std::map<dev_key, dev_desc> dev_param_map {
 | 
			
		||||
static const dev_map_t dev_param_map {
 | 
			
		||||
	{ std::make_tuple(USRP2, 1, 1), { 1, 0.0,  390625,  1.2184e-4,  "N2XX 1 SPS"         } },
 | 
			
		||||
	{ std::make_tuple(USRP2, 4, 1), { 1, 0.0,  390625,  7.6547e-5,  "N2XX 4/1 Tx/Rx SPS" } },
 | 
			
		||||
	{ std::make_tuple(USRP2, 4, 4), { 1, 0.0,  390625,  4.6080e-5,  "N2XX 4 SPS"         } },
 | 
			
		||||
@@ -129,9 +117,7 @@ static const std::map<dev_key, dev_desc> dev_param_map {
 | 
			
		||||
	{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
typedef std::tuple<uhd_dev_type, enum gsm_band> dev_band_key;
 | 
			
		||||
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
 | 
			
		||||
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
 | 
			
		||||
static const power_map_t dev_band_nom_power_param_map {
 | 
			
		||||
	{ std::make_tuple(B200, GSM_BAND_850),	{ 89.75, 13.3, -7.5  } },
 | 
			
		||||
	{ std::make_tuple(B200, GSM_BAND_900),	{ 89.75, 13.3, -7.5  } },
 | 
			
		||||
	{ std::make_tuple(B200, GSM_BAND_1800),	{ 89.75, 7.5,  -11.0 } },
 | 
			
		||||
@@ -220,15 +206,10 @@ static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
 | 
			
		||||
	return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
 | 
			
		||||
		       InterfaceType iface, size_t chan_num, double lo_offset,
 | 
			
		||||
		       const std::vector<std::string>& tx_paths,
 | 
			
		||||
		       const std::vector<std::string>& rx_paths)
 | 
			
		||||
	: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
 | 
			
		||||
	  rx_gain_min(0.0), rx_gain_max(0.0), band_ass_curr_sess(false),
 | 
			
		||||
	  band((enum gsm_band)0), tx_spp(0), rx_spp(0),
 | 
			
		||||
	  started(false), aligned(false), drop_cnt(0),
 | 
			
		||||
	  prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
 | 
			
		||||
uhd_device::uhd_device(InterfaceType iface, const struct trx_cfg *cfg)
 | 
			
		||||
	: RadioDevice(iface, cfg), band_manager(dev_band_nom_power_param_map, dev_param_map), rx_gain_min(0.0),
 | 
			
		||||
	  rx_gain_max(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), drop_cnt(0), prev_ts(0, 0),
 | 
			
		||||
	  ts_initial(0), ts_offset(0), async_event_thrd(NULL)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -240,47 +221,6 @@ uhd_device::~uhd_device()
 | 
			
		||||
		delete rx_buffers[i];
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void uhd_device::assign_band_desc(enum gsm_band req_band)
 | 
			
		||||
{
 | 
			
		||||
	dev_band_map_it it;
 | 
			
		||||
 | 
			
		||||
	it = dev_band_nom_power_param_map.find(dev_band_key(dev_type, req_band));
 | 
			
		||||
	if (it == dev_band_nom_power_param_map.end()) {
 | 
			
		||||
		dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
 | 
			
		||||
		LOGC(DDEV, ERROR) << "No Power parameters exist for device "
 | 
			
		||||
				    << desc.str << " on band " << gsm_band_name(req_band)
 | 
			
		||||
				    << ", using B210 ones as fallback";
 | 
			
		||||
		it = dev_band_nom_power_param_map.find(dev_band_key(B210, req_band));
 | 
			
		||||
	}
 | 
			
		||||
	OSMO_ASSERT(it != dev_band_nom_power_param_map.end())
 | 
			
		||||
	band_desc = it->second;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool uhd_device::set_band(enum gsm_band req_band)
 | 
			
		||||
{
 | 
			
		||||
	if (band_ass_curr_sess && req_band != band) {
 | 
			
		||||
		LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
 | 
			
		||||
				  << " different from previous band " << gsm_band_name(band);
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (req_band != band) {
 | 
			
		||||
		band = req_band;
 | 
			
		||||
		assign_band_desc(band);
 | 
			
		||||
	}
 | 
			
		||||
	band_ass_curr_sess = true;
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void uhd_device::get_dev_band_desc(dev_band_desc& desc)
 | 
			
		||||
{
 | 
			
		||||
	if (band == 0) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
 | 
			
		||||
		assign_band_desc(GSM_BAND_900);
 | 
			
		||||
	}
 | 
			
		||||
	desc = band_desc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void uhd_device::init_gains()
 | 
			
		||||
{
 | 
			
		||||
	double tx_gain_min, tx_gain_max;
 | 
			
		||||
@@ -345,7 +285,7 @@ void uhd_device::set_rates()
 | 
			
		||||
	rx_rate = usrp_dev->get_rx_rate();
 | 
			
		||||
 | 
			
		||||
	ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
 | 
			
		||||
	LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
 | 
			
		||||
	LOGC(DDEV, INFO) << "Rates configured for " << desc.desc_str;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
double uhd_device::setRxGain(double db, size_t chan)
 | 
			
		||||
@@ -355,6 +295,9 @@ double uhd_device::setRxGain(double db, size_t chan)
 | 
			
		||||
		return 0.0f;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cfg->overrides.ul_gain_override)
 | 
			
		||||
		return rx_gains[chan];
 | 
			
		||||
 | 
			
		||||
	usrp_dev->set_rx_gain(db, chan);
 | 
			
		||||
	rx_gains[chan] = usrp_dev->get_rx_gain(chan);
 | 
			
		||||
 | 
			
		||||
@@ -397,6 +340,9 @@ double uhd_device::setPowerAttenuation(int atten, size_t chan) {
 | 
			
		||||
		return 0.0f;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cfg->overrides.dl_gain_override)
 | 
			
		||||
		return atten; // ensures caller does not apply digital attenuation
 | 
			
		||||
 | 
			
		||||
	get_dev_band_desc(desc);
 | 
			
		||||
	tx_power = desc.nom_out_tx_power - atten;
 | 
			
		||||
	db = TxPower2TxGain(desc, tx_power);
 | 
			
		||||
@@ -548,7 +494,7 @@ void uhd_device::set_channels(bool swap)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
int uhd_device::open()
 | 
			
		||||
{
 | 
			
		||||
	const char *refstr;
 | 
			
		||||
	int clock_lock_attempts = 15;
 | 
			
		||||
@@ -564,10 +510,10 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// Find UHD devices
 | 
			
		||||
	uhd::device_addr_t addr(args);
 | 
			
		||||
	uhd::device_addr_t addr(cfg->dev_args);
 | 
			
		||||
	uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
 | 
			
		||||
	if (dev_addrs.size() == 0) {
 | 
			
		||||
		LOGC(DDEV, ALERT) << "No UHD devices found with address '" << args << "'";
 | 
			
		||||
		LOGC(DDEV, ALERT) << "No UHD devices found with address '" << cfg->dev_args << "'";
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -576,7 +522,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
	try {
 | 
			
		||||
		usrp_dev = uhd::usrp::multi_usrp::make(addr);
 | 
			
		||||
	} catch(uhd::key_error::exception &e) {
 | 
			
		||||
		LOGC(DDEV, ALERT) << "UHD make failed, device " << args << ", exception:\n" << e.what();
 | 
			
		||||
		LOGC(DDEV, ALERT) << "UHD make failed, device " << cfg->dev_args << ", exception:\n" << e.what();
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -584,14 +530,16 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
	if (!parse_dev_type())
 | 
			
		||||
		return -1;
 | 
			
		||||
 | 
			
		||||
	update_band_dev(dev_key(dev_type, tx_sps, rx_sps));
 | 
			
		||||
 | 
			
		||||
	if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
 | 
			
		||||
		LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater";
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
		set_channels(swap_channels);
 | 
			
		||||
        } catch (const std::exception &e) {
 | 
			
		||||
		set_channels(cfg->swap_channels);
 | 
			
		||||
	} catch (const std::exception &e) {
 | 
			
		||||
		LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
@@ -607,7 +555,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
	rx_gains.resize(chans);
 | 
			
		||||
	rx_buffers.resize(chans);
 | 
			
		||||
 | 
			
		||||
	switch (ref) {
 | 
			
		||||
	switch (cfg->clock_ref) {
 | 
			
		||||
	case REF_INTERNAL:
 | 
			
		||||
		refstr = "internal";
 | 
			
		||||
		break;
 | 
			
		||||
@@ -686,6 +634,32 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
			
		||||
	// Print configuration
 | 
			
		||||
	LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
 | 
			
		||||
 | 
			
		||||
	if (cfg->overrides.dl_freq_override) {
 | 
			
		||||
		uhd::tune_request_t treq_tx = uhd::tune_request_t(cfg->overrides.dl_freq, 0);
 | 
			
		||||
		auto tres = usrp_dev->set_tx_freq(treq_tx, 0);
 | 
			
		||||
		tx_freqs[0] = usrp_dev->get_tx_freq(0);
 | 
			
		||||
		LOGCHAN(0, DDEV, INFO) << "OVERRIDE set_freq(" << tx_freqs[0] << ", TX): " << tres.to_pp_string() << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cfg->overrides.ul_freq_override) {
 | 
			
		||||
		uhd::tune_request_t treq_rx = uhd::tune_request_t(cfg->overrides.ul_freq, 0);
 | 
			
		||||
		auto tres = usrp_dev->set_rx_freq(treq_rx, 0);
 | 
			
		||||
		rx_freqs[0] = usrp_dev->get_rx_freq(0);
 | 
			
		||||
		LOGCHAN(0, DDEV, INFO) << "OVERRIDE set_freq(" << rx_freqs[0] << ", RX): " << tres.to_pp_string() << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cfg->overrides.ul_gain_override) {
 | 
			
		||||
		usrp_dev->set_rx_gain(cfg->overrides.ul_gain, 0);
 | 
			
		||||
		rx_gains[0] = usrp_dev->get_rx_gain(0);
 | 
			
		||||
		LOGCHAN(0, DDEV, INFO) << " OVERRIDE RX gain:" << rx_gains[0] << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (cfg->overrides.dl_gain_override) {
 | 
			
		||||
		usrp_dev->set_tx_gain(cfg->overrides.dl_gain, 0);
 | 
			
		||||
		tx_gains[0] = usrp_dev->get_tx_gain(0);
 | 
			
		||||
		LOGCHAN(0, DDEV, INFO) << " OVERRIDE TX gain:" << tx_gains[0] << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (iface == MULTI_ARFCN)
 | 
			
		||||
		return MULTI_ARFCN;
 | 
			
		||||
 | 
			
		||||
@@ -796,7 +770,7 @@ bool uhd_device::stop()
 | 
			
		||||
	for (size_t i = 0; i < rx_buffers.size(); i++)
 | 
			
		||||
		rx_buffers[i]->reset();
 | 
			
		||||
 | 
			
		||||
	band_ass_curr_sess = false;
 | 
			
		||||
	band_reset();
 | 
			
		||||
 | 
			
		||||
	started = false;
 | 
			
		||||
	return true;
 | 
			
		||||
@@ -1036,17 +1010,22 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<double> freqs;
 | 
			
		||||
	uhd::tune_result_t tres;
 | 
			
		||||
	std::string str_dir = tx ? "Tx" : "Rx";
 | 
			
		||||
 | 
			
		||||
	if (cfg->overrides.dl_freq_override || cfg->overrides.ul_freq_override)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	if (!update_band_from_freq(freq, chan, tx))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	uhd::tune_request_t treq = select_freq(freq, chan, tx);
 | 
			
		||||
	std::string str_dir;
 | 
			
		||||
 | 
			
		||||
	if (tx) {
 | 
			
		||||
		tres = usrp_dev->set_tx_freq(treq, chan);
 | 
			
		||||
		tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
 | 
			
		||||
		str_dir = "Tx";
 | 
			
		||||
	} else {
 | 
			
		||||
		tres = usrp_dev->set_rx_freq(treq, chan);
 | 
			
		||||
		rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
 | 
			
		||||
		str_dir = "Rx";
 | 
			
		||||
	}
 | 
			
		||||
	LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
 | 
			
		||||
 | 
			
		||||
@@ -1076,59 +1055,20 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
 | 
			
		||||
 | 
			
		||||
bool uhd_device::setTxFreq(double wFreq, size_t chan)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t req_arfcn;
 | 
			
		||||
	enum gsm_band req_band;
 | 
			
		||||
 | 
			
		||||
	if (chan >= tx_freqs.size()) {
 | 
			
		||||
		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	ScopedLock lock(tune_lock);
 | 
			
		||||
 | 
			
		||||
	req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
 | 
			
		||||
	if (req_arfcn == 0xffff) {
 | 
			
		||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
 | 
			
		||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
 | 
			
		||||
					   << " Hz (ARFCN " << req_arfcn << " )";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!set_band(req_band))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	if (!set_freq(wFreq, chan, true))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return true;
 | 
			
		||||
	return set_freq(wFreq, chan, true);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool uhd_device::setRxFreq(double wFreq, size_t chan)
 | 
			
		||||
{
 | 
			
		||||
	uint16_t req_arfcn;
 | 
			
		||||
	enum gsm_band req_band;
 | 
			
		||||
 | 
			
		||||
	if (chan >= rx_freqs.size()) {
 | 
			
		||||
		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	ScopedLock lock(tune_lock);
 | 
			
		||||
 | 
			
		||||
	req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
 | 
			
		||||
	if (req_arfcn == 0xffff) {
 | 
			
		||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
 | 
			
		||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq
 | 
			
		||||
					   << " Hz (ARFCN " << req_arfcn << " )";
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (!set_band(req_band))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	return set_freq(wFreq, chan, false);
 | 
			
		||||
}
 | 
			
		||||
@@ -1378,11 +1318,8 @@ std::string uhd_device::str_code(uhd::async_metadata_t metadata)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifndef IPCMAGIC
 | 
			
		||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
 | 
			
		||||
			       InterfaceType iface, size_t chans, double lo_offset,
 | 
			
		||||
			       const std::vector<std::string>& tx_paths,
 | 
			
		||||
			       const std::vector<std::string>& rx_paths)
 | 
			
		||||
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
 | 
			
		||||
{
 | 
			
		||||
	return new uhd_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
 | 
			
		||||
	return new uhd_device(type, cfg);
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
@@ -30,6 +30,7 @@
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "bandmanager.h"
 | 
			
		||||
#include "radioDevice.h"
 | 
			
		||||
#include "smpl_buf.h"
 | 
			
		||||
 | 
			
		||||
@@ -71,6 +72,19 @@ struct dev_band_desc {
 | 
			
		||||
	double rxgain2rssioffset_rel; /* dB */
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct dev_desc {
 | 
			
		||||
	unsigned channels;
 | 
			
		||||
	double mcr;
 | 
			
		||||
	double rate;
 | 
			
		||||
	double offset;
 | 
			
		||||
	std::string desc_str;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using dev_key = std::tuple<uhd_dev_type, int, int>;
 | 
			
		||||
using dev_band_key = std::tuple<uhd_dev_type, enum gsm_band>;
 | 
			
		||||
using power_map_t = std::map<dev_band_key, dev_band_desc>;
 | 
			
		||||
using dev_map_t = std::map<dev_key, dev_desc>;
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
    uhd_device - UHD implementation of the Device interface. Timestamped samples
 | 
			
		||||
                are sent to and received from the device. An intermediate buffer
 | 
			
		||||
@@ -78,19 +92,19 @@ struct dev_band_desc {
 | 
			
		||||
                Events and errors such as underruns are reported asynchronously
 | 
			
		||||
                by the device and received in a separate thread.
 | 
			
		||||
*/
 | 
			
		||||
class uhd_device : public RadioDevice {
 | 
			
		||||
class uhd_device : public RadioDevice, public band_manager<power_map_t, dev_map_t> {
 | 
			
		||||
public:
 | 
			
		||||
	uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
 | 
			
		||||
		   size_t chan_num, double offset,
 | 
			
		||||
		   const std::vector<std::string>& tx_paths,
 | 
			
		||||
		   const std::vector<std::string>& rx_paths);
 | 
			
		||||
	~uhd_device();
 | 
			
		||||
    uhd_device(InterfaceType iface, const struct trx_cfg *cfg);
 | 
			
		||||
    ~uhd_device();
 | 
			
		||||
 | 
			
		||||
	int open(const std::string &args, int ref, bool swap_channels);
 | 
			
		||||
	bool start();
 | 
			
		||||
	bool stop();
 | 
			
		||||
	bool restart();
 | 
			
		||||
	enum TxWindowType getWindowType() { return tx_window; }
 | 
			
		||||
    int open();
 | 
			
		||||
    bool start();
 | 
			
		||||
    bool stop();
 | 
			
		||||
    bool restart();
 | 
			
		||||
    enum TxWindowType getWindowType()
 | 
			
		||||
    {
 | 
			
		||||
	    return tx_window;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
	int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
			
		||||
			TIMESTAMP timestamp, bool *underrun);
 | 
			
		||||
@@ -160,9 +174,6 @@ protected:
 | 
			
		||||
 | 
			
		||||
	std::vector<double> tx_gains, rx_gains;
 | 
			
		||||
	std::vector<double> tx_freqs, rx_freqs;
 | 
			
		||||
	bool band_ass_curr_sess; /* true if  "band" was set after last POWEROFF */
 | 
			
		||||
	enum gsm_band band;
 | 
			
		||||
	struct dev_band_desc band_desc;
 | 
			
		||||
	size_t tx_spp, rx_spp;
 | 
			
		||||
 | 
			
		||||
	bool started;
 | 
			
		||||
@@ -191,10 +202,6 @@ protected:
 | 
			
		||||
 | 
			
		||||
	uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
 | 
			
		||||
	bool set_freq(double freq, size_t chan, bool tx);
 | 
			
		||||
	void get_dev_band_desc(dev_band_desc& desc);
 | 
			
		||||
	bool set_band(enum gsm_band req_band);
 | 
			
		||||
	void assign_band_desc(enum gsm_band req_band);
 | 
			
		||||
 | 
			
		||||
	Thread *async_event_thrd;
 | 
			
		||||
	Mutex tune_lock;
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -60,11 +60,7 @@ const dboardConfigType dboardConfig = TXA_RXB;
 | 
			
		||||
 | 
			
		||||
const double USRPDevice::masterClockRate = 52.0e6;
 | 
			
		||||
 | 
			
		||||
USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
 | 
			
		||||
		       size_t chan_num, double lo_offset,
 | 
			
		||||
		       const std::vector<std::string>& tx_paths,
 | 
			
		||||
		       const std::vector<std::string>& rx_paths):
 | 
			
		||||
		RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths)
 | 
			
		||||
USRPDevice::USRPDevice(InterfaceType iface, const struct trx_cfg *cfg) : RadioDevice(iface, cfg)
 | 
			
		||||
{
 | 
			
		||||
  LOGC(DDEV, INFO) << "creating USRP device...";
 | 
			
		||||
 | 
			
		||||
@@ -94,7 +90,7 @@ USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int USRPDevice::open(const std::string &, int, bool)
 | 
			
		||||
int USRPDevice::open()
 | 
			
		||||
{
 | 
			
		||||
  writeLock.unlock();
 | 
			
		||||
 | 
			
		||||
@@ -587,8 +583,7 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
 | 
			
		||||
{
 | 
			
		||||
#ifndef SWLOOPBACK
 | 
			
		||||
  short data[] = {0x00,0x02,0x00,0x00};
 | 
			
		||||
  uint32_t *wordPtr = (uint32_t *) data;
 | 
			
		||||
  *wordPtr = host_to_usrp_u32(*wordPtr);
 | 
			
		||||
  /* FIXME: big endian */
 | 
			
		||||
  bool tmpUnderrun;
 | 
			
		||||
 | 
			
		||||
  std::vector<short *> buf(1, data);
 | 
			
		||||
@@ -659,22 +654,19 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
 | 
			
		||||
bool USRPDevice::setRxFreq(double wFreq) { return true;};
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
 | 
			
		||||
			       InterfaceType iface, size_t chans, double lo_offset,
 | 
			
		||||
			       const std::vector<std::string>& tx_paths,
 | 
			
		||||
			       const std::vector<std::string>& rx_paths)
 | 
			
		||||
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
 | 
			
		||||
{
 | 
			
		||||
	if (tx_sps != rx_sps) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	if (chans != 1) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	if (lo_offset != 0.0) {
 | 
			
		||||
		LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
 | 
			
		||||
		return NULL;
 | 
			
		||||
	}
 | 
			
		||||
	return new USRPDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
 | 
			
		||||
  if (cfg->tx_sps != cfg->rx_sps) {
 | 
			
		||||
    LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
  if (cfg->num_chans != 1) {
 | 
			
		||||
    LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
  if (cfg->offset != 0.0) {
 | 
			
		||||
    LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
 | 
			
		||||
    return NULL;
 | 
			
		||||
  }
 | 
			
		||||
  return new USRPDevice(type, cfg);
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -104,20 +104,21 @@ private:
 | 
			
		||||
 public:
 | 
			
		||||
 | 
			
		||||
  /** Object constructor */
 | 
			
		||||
  USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
 | 
			
		||||
		const std::vector<std::string>& tx_paths,
 | 
			
		||||
		const std::vector<std::string>& rx_paths);
 | 
			
		||||
     USRPDevice(InterfaceType iface, const struct trx_cfg *cfg);
 | 
			
		||||
 | 
			
		||||
  /** Instantiate the USRP */
 | 
			
		||||
  int open(const std::string &, int, bool);
 | 
			
		||||
     /** Instantiate the USRP */
 | 
			
		||||
     int open();
 | 
			
		||||
 | 
			
		||||
  /** Start the USRP */
 | 
			
		||||
  bool start();
 | 
			
		||||
     /** Start the USRP */
 | 
			
		||||
     bool start();
 | 
			
		||||
 | 
			
		||||
  /** Stop the USRP */
 | 
			
		||||
  bool stop();
 | 
			
		||||
     /** Stop the USRP */
 | 
			
		||||
     bool stop();
 | 
			
		||||
 | 
			
		||||
  enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
 | 
			
		||||
     enum TxWindowType getWindowType()
 | 
			
		||||
     {
 | 
			
		||||
	     return TX_WINDOW_USRP1;
 | 
			
		||||
     }
 | 
			
		||||
 | 
			
		||||
  /**
 | 
			
		||||
	Read samples from the USRP.
 | 
			
		||||
 
 | 
			
		||||
@@ -38,6 +38,7 @@
 | 
			
		||||
#define STEALING_BIT      1
 | 
			
		||||
#define N_TRAIN_BITS      26
 | 
			
		||||
#define N_SYNC_BITS       64
 | 
			
		||||
#define N_ACCESS_BITS     41
 | 
			
		||||
#define USEFUL_BITS       142  //(2*(DATA_BITS+STEALING_BIT) + N_TRAIN_BITS )
 | 
			
		||||
#define FCCH_BITS         USEFUL_BITS
 | 
			
		||||
#define BURST_SIZE        (USEFUL_BITS+2*TAIL_BITS)
 | 
			
		||||
@@ -73,6 +74,12 @@ static const unsigned char SYNC_BITS[] = {
 | 
			
		||||
  0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static const unsigned char ACCESS_BITS [] = {
 | 
			
		||||
 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
 | 
			
		||||
 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0,
 | 
			
		||||
 0, 1, 1, 1, 1, 0, 0, 0
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const unsigned FCCH_FRAMES[] = { 0, 10, 20, 30, 40 };
 | 
			
		||||
const unsigned SCH_FRAMES[] = { 1, 11, 21, 31, 41 };
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -39,13 +39,13 @@
 | 
			
		||||
#include "viterbi_detector.h"
 | 
			
		||||
#include "grgsm_vitac.h"
 | 
			
		||||
 | 
			
		||||
//signalVector mChanResp;
 | 
			
		||||
gr_complex d_acc_training_seq[N_ACCESS_BITS]; ///<encoded training sequence of a RACH burst
 | 
			
		||||
gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a SCH burst
 | 
			
		||||
gr_complex d_norm_training_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS]; ///<encoded training sequences of a normal and dummy burst
 | 
			
		||||
const int d_chan_imp_length = CHAN_IMP_RESP_LENGTH;
 | 
			
		||||
 | 
			
		||||
void initvita() {
 | 
			
		||||
 | 
			
		||||
void initvita()
 | 
			
		||||
{
 | 
			
		||||
	/**
 | 
			
		||||
	 * Prepare SCH sequence bits
 | 
			
		||||
	 *
 | 
			
		||||
@@ -53,59 +53,72 @@ void initvita() {
 | 
			
		||||
	 * Burst and two guard periods
 | 
			
		||||
	 * (one guard period is an arbitrary overlap)
 | 
			
		||||
	 */
 | 
			
		||||
	gmsk_mapper(SYNC_BITS, N_SYNC_BITS,
 | 
			
		||||
		d_sch_training_seq, gr_complex(0.0, -1.0));
 | 
			
		||||
	gmsk_mapper(SYNC_BITS, N_SYNC_BITS, d_sch_training_seq, gr_complex(0.0, -1.0));
 | 
			
		||||
	for (auto &i : d_sch_training_seq)
 | 
			
		||||
		i = conj(i);
 | 
			
		||||
 | 
			
		||||
	/* ab */
 | 
			
		||||
	gmsk_mapper(ACCESS_BITS, N_ACCESS_BITS, d_acc_training_seq, gr_complex(0.0, -1.0));
 | 
			
		||||
	for (auto &i : d_acc_training_seq)
 | 
			
		||||
		i = conj(i);
 | 
			
		||||
 | 
			
		||||
	/* Prepare bits of training sequences */
 | 
			
		||||
	for (int i = 0; i < TRAIN_SEQ_NUM; i++) {
 | 
			
		||||
		/**
 | 
			
		||||
		 * If first bit of the sequence is 0
 | 
			
		||||
		 * => first symbol is 1, else -1
 | 
			
		||||
		 */
 | 
			
		||||
		gr_complex startpoint = train_seq[i][0] == 0 ?
 | 
			
		||||
			gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
 | 
			
		||||
		gmsk_mapper(train_seq[i], N_TRAIN_BITS,
 | 
			
		||||
			d_norm_training_seq[i], startpoint);
 | 
			
		||||
		gr_complex startpoint = train_seq[i][0] == 0 ? gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
 | 
			
		||||
		gmsk_mapper(train_seq[i], N_TRAIN_BITS, d_norm_training_seq[i], startpoint);
 | 
			
		||||
		for (auto &i : d_norm_training_seq[i])
 | 
			
		||||
			i = conj(i);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MULTI_VER_TARGET_ATTR NO_UBSAN
 | 
			
		||||
void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
 | 
			
		||||
template <unsigned int burst_size>
 | 
			
		||||
NO_UBSAN static void detect_burst_generic(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start,
 | 
			
		||||
					  char *output_binary, int ss)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
 | 
			
		||||
	unsigned int stop_states[2] = { 4, 12 };
 | 
			
		||||
	gr_complex filtered_burst[BURST_SIZE];
 | 
			
		||||
	gr_complex filtered_burst[burst_size];
 | 
			
		||||
	gr_complex rhh[CHAN_IMP_RESP_LENGTH];
 | 
			
		||||
	float output[BURST_SIZE];
 | 
			
		||||
	int start_state = 3;
 | 
			
		||||
 | 
			
		||||
	// if(burst_start < 0 ||burst_start > 10)
 | 
			
		||||
	// 	fprintf(stderr, "bo %d\n", burst_start);
 | 
			
		||||
 | 
			
		||||
	// burst_start = burst_start >= 0 ? burst_start : 0;
 | 
			
		||||
	float output[burst_size];
 | 
			
		||||
	int start_state = ss;
 | 
			
		||||
 | 
			
		||||
	autocorrelation(chan_imp_resp, &rhh_temp[0], d_chan_imp_length * d_OSR);
 | 
			
		||||
	for (int ii = 0; ii < d_chan_imp_length; ii++)
 | 
			
		||||
		rhh[ii] = conj(rhh_temp[ii * d_OSR]);
 | 
			
		||||
 | 
			
		||||
	mafi(&input[burst_start], BURST_SIZE, chan_imp_resp,
 | 
			
		||||
		d_chan_imp_length * d_OSR, filtered_burst);
 | 
			
		||||
	mafi(&input[burst_start], burst_size, chan_imp_resp, d_chan_imp_length * d_OSR, filtered_burst);
 | 
			
		||||
 | 
			
		||||
	viterbi_detector(filtered_burst, BURST_SIZE, rhh,
 | 
			
		||||
		start_state, stop_states, 2, output);
 | 
			
		||||
	viterbi_detector(filtered_burst, burst_size, rhh, start_state, stop_states, 2, output);
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < BURST_SIZE; i++)
 | 
			
		||||
		output_binary[i] = output[i] * -127; // pre flip bits!
 | 
			
		||||
	for (unsigned int i = 0; i < burst_size; i++)
 | 
			
		||||
		output_binary[i] = output[i] > 0 ? -127 : 127; // pre flip bits!
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gmsk_mapper(const unsigned char* input,
 | 
			
		||||
	int nitems, gr_complex* gmsk_output, gr_complex start_point)
 | 
			
		||||
NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary,
 | 
			
		||||
			      int ss)
 | 
			
		||||
{
 | 
			
		||||
	return detect_burst_generic<BURST_SIZE>(input, chan_imp_resp, burst_start, output_binary, ss);
 | 
			
		||||
}
 | 
			
		||||
NO_UBSAN void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary,
 | 
			
		||||
			      int ss)
 | 
			
		||||
{
 | 
			
		||||
	return detect_burst_generic<8 + 41 + 36 + 3>(input, chan_imp_resp, burst_start, output_binary, ss);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
 | 
			
		||||
{
 | 
			
		||||
	return detect_burst_nb(input, chan_imp_resp, burst_start, output_binary, 3);
 | 
			
		||||
}
 | 
			
		||||
NO_UBSAN void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
 | 
			
		||||
{
 | 
			
		||||
	return detect_burst_ab(input, chan_imp_resp, burst_start, output_binary, 3);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output, gr_complex start_point)
 | 
			
		||||
{
 | 
			
		||||
	gr_complex j = gr_complex(0.0, 1.0);
 | 
			
		||||
	gmsk_output[0] = start_point;
 | 
			
		||||
@@ -122,16 +135,13 @@ gmsk_mapper(const unsigned char* input,
 | 
			
		||||
		encoded_symbol = current_symbol * previous_symbol;
 | 
			
		||||
 | 
			
		||||
		/* And do GMSK mapping */
 | 
			
		||||
		gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0)
 | 
			
		||||
			* gmsk_output[i - 1];
 | 
			
		||||
		gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0) * gmsk_output[i - 1];
 | 
			
		||||
 | 
			
		||||
		previous_symbol = current_symbol;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gr_complex
 | 
			
		||||
correlate_sequence(const gr_complex* sequence,
 | 
			
		||||
	int length, const gr_complex* input)
 | 
			
		||||
gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_complex *input)
 | 
			
		||||
{
 | 
			
		||||
	gr_complex result(0.0, 0.0);
 | 
			
		||||
 | 
			
		||||
@@ -142,9 +152,7 @@ correlate_sequence(const gr_complex* sequence,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Computes autocorrelation for positive arguments */
 | 
			
		||||
inline void
 | 
			
		||||
autocorrelation(const gr_complex* input,
 | 
			
		||||
	gr_complex* out, int nitems)
 | 
			
		||||
inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems)
 | 
			
		||||
{
 | 
			
		||||
	for (int k = nitems - 1; k >= 0; k--) {
 | 
			
		||||
		out[k] = gr_complex(0, 0);
 | 
			
		||||
@@ -153,9 +161,7 @@ autocorrelation(const gr_complex* input,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
inline void
 | 
			
		||||
mafi(const gr_complex* input, int nitems,
 | 
			
		||||
	gr_complex* filter, int filter_length, gr_complex* output)
 | 
			
		||||
inline void mafi(const gr_complex *input, int nitems, gr_complex *filter, int filter_length, gr_complex *output)
 | 
			
		||||
{
 | 
			
		||||
	for (int n = 0; n < nitems; n++) {
 | 
			
		||||
		int a = n * d_OSR;
 | 
			
		||||
@@ -170,66 +176,45 @@ mafi(const gr_complex* input, int nitems,
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int search_center, int search_start_pos,
 | 
			
		||||
		      int search_stop_pos, gr_complex *tseq, int tseqlen, float *corr_max)
 | 
			
		||||
int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int search_start_pos, int search_stop_pos,
 | 
			
		||||
		      gr_complex *tseq, int tseqlen, float *corr_max)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<gr_complex> correlation_buffer;
 | 
			
		||||
	const int num_search_windows = search_stop_pos - search_start_pos;
 | 
			
		||||
	const int power_search_window_len = d_chan_imp_length * d_OSR;
 | 
			
		||||
	std::vector<float> window_energy_buffer;
 | 
			
		||||
	std::vector<float> power_buffer;
 | 
			
		||||
	std::vector<gr_complex> correlation_buffer;
 | 
			
		||||
 | 
			
		||||
	for (int ii = search_start_pos; ii < search_stop_pos; ii++) {
 | 
			
		||||
		gr_complex correlation = correlate_sequence(tseq, tseqlen, &input[ii]);
 | 
			
		||||
	power_buffer.reserve(num_search_windows);
 | 
			
		||||
	correlation_buffer.reserve(num_search_windows);
 | 
			
		||||
	window_energy_buffer.reserve(num_search_windows);
 | 
			
		||||
 | 
			
		||||
	for (int ii = 0; ii < num_search_windows; ii++) {
 | 
			
		||||
		gr_complex correlation = correlate_sequence(tseq, tseqlen, &input[search_start_pos + ii]);
 | 
			
		||||
		correlation_buffer.push_back(correlation);
 | 
			
		||||
		power_buffer.push_back(std::pow(abs(correlation), 2));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int strongest_corr_nr = max_element(power_buffer.begin(), power_buffer.end()) - power_buffer.begin();
 | 
			
		||||
 | 
			
		||||
	/* Compute window energies */
 | 
			
		||||
	auto window_energy_start_offset = strongest_corr_nr - 6 * d_OSR;
 | 
			
		||||
	window_energy_start_offset = window_energy_start_offset < 0 ? 0 : window_energy_start_offset; //can end up out of range..
 | 
			
		||||
	auto window_energy_end_offset = strongest_corr_nr + 6 * d_OSR + d_chan_imp_length * d_OSR;
 | 
			
		||||
	auto iter = power_buffer.begin() + window_energy_start_offset;
 | 
			
		||||
	auto iter_end = power_buffer.begin() + window_energy_end_offset;
 | 
			
		||||
	while (iter != iter_end) {
 | 
			
		||||
		std::vector<float>::iterator iter_ii = iter;
 | 
			
		||||
		bool loop_end = false;
 | 
			
		||||
		float energy = 0;
 | 
			
		||||
	float windowSum = 0;
 | 
			
		||||
 | 
			
		||||
		int len = d_chan_imp_length * d_OSR;
 | 
			
		||||
		for (int ii = 0; ii < len; ii++, iter_ii++) {
 | 
			
		||||
			if (iter_ii == power_buffer.end()) {
 | 
			
		||||
				loop_end = true;
 | 
			
		||||
				break;
 | 
			
		||||
			}
 | 
			
		||||
	// first window
 | 
			
		||||
	for (int i = 0; i < power_search_window_len; i++) {
 | 
			
		||||
		windowSum += power_buffer[i];
 | 
			
		||||
	}
 | 
			
		||||
	window_energy_buffer.push_back(windowSum);
 | 
			
		||||
 | 
			
		||||
			energy += (*iter_ii);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (loop_end)
 | 
			
		||||
			break;
 | 
			
		||||
 | 
			
		||||
		window_energy_buffer.push_back(energy);
 | 
			
		||||
		iter++;
 | 
			
		||||
	// slide windows
 | 
			
		||||
	for (int i = power_search_window_len; i < num_search_windows; i++) {
 | 
			
		||||
		windowSum += power_buffer[i] - power_buffer[i - power_search_window_len];
 | 
			
		||||
		window_energy_buffer.push_back(windowSum);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Calculate the strongest window number */
 | 
			
		||||
	int strongest_window_nr = window_energy_start_offset +
 | 
			
		||||
				  max_element(window_energy_buffer.begin(), window_energy_buffer.end()) -
 | 
			
		||||
	int strongest_window_nr = std::max_element(window_energy_buffer.begin(), window_energy_buffer.end()) -
 | 
			
		||||
				  window_energy_buffer.begin();
 | 
			
		||||
 | 
			
		||||
	// auto window_search_start = window_energy_buffer.begin() + strongest_corr_nr - 5* d_OSR;
 | 
			
		||||
	// auto window_search_end = window_energy_buffer.begin() + strongest_corr_nr + 10* d_OSR;
 | 
			
		||||
	// window_search_end = window_search_end >= window_energy_buffer.end() ? window_energy_buffer.end() : window_search_end;
 | 
			
		||||
 | 
			
		||||
	// /* Calculate the strongest window number */
 | 
			
		||||
	// int strongest_window_nr = max_element(window_search_start, window_search_end /* - d_chan_imp_length * d_OSR*/) - window_energy_buffer.begin();
 | 
			
		||||
 | 
			
		||||
	// if (strongest_window_nr < 0)
 | 
			
		||||
	// 	strongest_window_nr = 0;
 | 
			
		||||
 | 
			
		||||
	float max_correlation = 0;
 | 
			
		||||
	for (int ii = 0; ii < d_chan_imp_length * d_OSR; ii++) {
 | 
			
		||||
	for (int ii = 0; ii < power_search_window_len; ii++) {
 | 
			
		||||
		gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
 | 
			
		||||
		if (abs(correlation) > max_correlation)
 | 
			
		||||
			max_correlation = abs(correlation);
 | 
			
		||||
@@ -242,7 +227,27 @@ int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int se
 | 
			
		||||
	 * Compute first sample position, which corresponds
 | 
			
		||||
	 * to the first sample of the impulse response
 | 
			
		||||
	 */
 | 
			
		||||
	return search_start_pos + strongest_window_nr - search_center * d_OSR;
 | 
			
		||||
	return search_start_pos + strongest_window_nr;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
8 ext tail bits
 | 
			
		||||
41 sync seq
 | 
			
		||||
36 encrypted bits
 | 
			
		||||
3 tail bits
 | 
			
		||||
68.25 extended tail bits (!)
 | 
			
		||||
 | 
			
		||||
center at 8+5 (actually known tb -> known isi, start at 8?) FIXME
 | 
			
		||||
*/
 | 
			
		||||
int get_access_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int max_delay)
 | 
			
		||||
{
 | 
			
		||||
	const int search_center = 8 + 5;
 | 
			
		||||
	const int search_start_pos = (search_center - 5) * d_OSR + 1;
 | 
			
		||||
	const int search_stop_pos = (search_center + 5 + d_chan_imp_length + max_delay) * d_OSR;
 | 
			
		||||
	const auto tseq = &d_acc_training_seq[TRAIN_BEGINNING];
 | 
			
		||||
	const auto tseqlen = N_ACCESS_BITS - (2 * TRAIN_BEGINNING);
 | 
			
		||||
	return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
 | 
			
		||||
	       search_center * d_OSR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -260,8 +265,8 @@ int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, f
 | 
			
		||||
	const int search_stop_pos = (search_center + 5 + d_chan_imp_length) * d_OSR;
 | 
			
		||||
	const auto tseq = &d_norm_training_seq[bcc][TRAIN_BEGINNING];
 | 
			
		||||
	const auto tseqlen = N_TRAIN_BITS - (2 * TRAIN_BEGINNING);
 | 
			
		||||
	return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen,
 | 
			
		||||
				 corr_max);
 | 
			
		||||
	return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
 | 
			
		||||
	       search_center * d_OSR;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
@@ -281,8 +286,9 @@ int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp)
 | 
			
		||||
 | 
			
		||||
	// strongest_window_nr + chan_imp_resp_center + SYNC_POS *d_OSR - 48 * d_OSR - 2 * d_OSR + 2 ;
 | 
			
		||||
	float corr_max;
 | 
			
		||||
	return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen,
 | 
			
		||||
				 &corr_max);
 | 
			
		||||
	return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, &corr_max) -
 | 
			
		||||
	       search_center * d_OSR;
 | 
			
		||||
	;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max)
 | 
			
		||||
@@ -291,9 +297,9 @@ int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_r
 | 
			
		||||
	const int search_center = SYNC_POS + TRAIN_BEGINNING;
 | 
			
		||||
	const int search_start_pos = 0;
 | 
			
		||||
	// FIXME: proper end offset
 | 
			
		||||
	const int search_stop_pos = len - (N_SYNC_BITS*8);
 | 
			
		||||
	const int search_stop_pos = len - (N_SYNC_BITS * 8);
 | 
			
		||||
	auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
 | 
			
		||||
 | 
			
		||||
	return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen,
 | 
			
		||||
				 corr_max);
 | 
			
		||||
	return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
 | 
			
		||||
	       search_center * d_OSR;
 | 
			
		||||
}
 | 
			
		||||
@@ -42,6 +42,10 @@
 | 
			
		||||
#define MULTI_VER_TARGET_ATTR_CLANGONLY
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/* ancient gcc < 8 has no attribute, clang always pretends to be gcc 4 */
 | 
			
		||||
#if !defined(__clang__) && __GNUC__ < 8
 | 
			
		||||
#define NO_UBSAN __attribute__((no_sanitize_undefined))
 | 
			
		||||
#else
 | 
			
		||||
#if defined(__has_attribute)
 | 
			
		||||
#if __has_attribute(no_sanitize)
 | 
			
		||||
#define NO_UBSAN __attribute__((no_sanitize("undefined")))
 | 
			
		||||
@@ -49,6 +53,7 @@
 | 
			
		||||
#else
 | 
			
		||||
#define NO_UBSAN
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define SYNC_SEARCH_RANGE 30
 | 
			
		||||
const int d_OSR(4);
 | 
			
		||||
@@ -58,8 +63,11 @@ void initvita();
 | 
			
		||||
int process_vita_burst(gr_complex *input, int tsc, unsigned char *output_binary);
 | 
			
		||||
int process_vita_sc_burst(gr_complex *input, int tsc, unsigned char *output_binary, int *offset);
 | 
			
		||||
 | 
			
		||||
MULTI_VER_TARGET_ATTR_CLANGONLY
 | 
			
		||||
void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary);
 | 
			
		||||
void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary, int ss);
 | 
			
		||||
void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary, int ss);
 | 
			
		||||
void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary);
 | 
			
		||||
void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary);
 | 
			
		||||
 | 
			
		||||
void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output, gr_complex start_point);
 | 
			
		||||
gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_complex *input);
 | 
			
		||||
inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems);
 | 
			
		||||
@@ -67,6 +75,7 @@ inline void mafi(const gr_complex *input, int nitems, gr_complex *filter, int fi
 | 
			
		||||
int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp);
 | 
			
		||||
int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int bcc);
 | 
			
		||||
int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max);
 | 
			
		||||
int get_access_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int max_delay);
 | 
			
		||||
 | 
			
		||||
enum class btype { NB, SCH };
 | 
			
		||||
struct fdata {
 | 
			
		||||
 
 | 
			
		||||
@@ -39,14 +39,16 @@ const size_t NUM_TRANSFERS = 16 * 2;
 | 
			
		||||
const int SAMPLE_SCALE_FACTOR = 15; // actually 16 but sigproc complains about clipping..
 | 
			
		||||
 | 
			
		||||
// see https://en.cppreference.com/w/cpp/language/parameter_pack  "Brace-enclosed initializers" example
 | 
			
		||||
template <typename Arg, typename... Args> void expand_args(std::ostream &out, Arg &&arg, Args &&...args)
 | 
			
		||||
template <typename Arg, typename... Args>
 | 
			
		||||
void expand_args(std::ostream &out, Arg &&arg, Args &&...args)
 | 
			
		||||
{
 | 
			
		||||
	out << '(' << std::forward<Arg>(arg);
 | 
			
		||||
	(void)(int[]){ 0, (void((out << "," << std::forward<Args>(args))), 0)... };
 | 
			
		||||
	out << ')' << std::endl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <class R, class... Args> using RvalFunc = R (*)(Args...);
 | 
			
		||||
template <class R, class... Args>
 | 
			
		||||
using RvalFunc = R (*)(Args...);
 | 
			
		||||
 | 
			
		||||
template <class R, class... Args>
 | 
			
		||||
R exec_and_check(RvalFunc<R, Args...> func, const char *fname, const char *finame, const char *funcname, int line,
 | 
			
		||||
@@ -67,7 +69,8 @@ R exec_and_check(RvalFunc<R, Args...> func, const char *fname, const char *finam
 | 
			
		||||
#pragma pack(push, 1)
 | 
			
		||||
using blade_sample_type = std::complex<int16_t>;
 | 
			
		||||
enum class blade_speed_buffer_type { HS, SS };
 | 
			
		||||
template <blade_speed_buffer_type T> struct blade_usb_message {
 | 
			
		||||
template <blade_speed_buffer_type T>
 | 
			
		||||
struct blade_usb_message {
 | 
			
		||||
	uint32_t reserved;
 | 
			
		||||
	uint64_t ts;
 | 
			
		||||
	uint32_t meta_flags;
 | 
			
		||||
@@ -76,7 +79,8 @@ template <blade_speed_buffer_type T> struct blade_usb_message {
 | 
			
		||||
 | 
			
		||||
static_assert(sizeof(blade_usb_message<blade_speed_buffer_type::SS>) == 2048, "blade buffer mismatch!");
 | 
			
		||||
static_assert(sizeof(blade_usb_message<blade_speed_buffer_type::HS>) == 1024, "blade buffer mismatch!");
 | 
			
		||||
template <unsigned int SZ, blade_speed_buffer_type T> struct blade_otw_buffer {
 | 
			
		||||
template <unsigned int SZ, blade_speed_buffer_type T>
 | 
			
		||||
struct blade_otw_buffer {
 | 
			
		||||
	static_assert((SZ >= 2 && !(SZ % 2)), "min size is 2x usb buffer!");
 | 
			
		||||
	blade_usb_message<T> m[SZ];
 | 
			
		||||
	int actual_samples_per_msg()
 | 
			
		||||
@@ -172,7 +176,8 @@ template <unsigned int SZ, blade_speed_buffer_type T> struct blade_otw_buffer {
 | 
			
		||||
};
 | 
			
		||||
#pragma pack(pop)
 | 
			
		||||
 | 
			
		||||
template <unsigned int SZ, blade_speed_buffer_type T> struct blade_otw_buffer_helper {
 | 
			
		||||
template <unsigned int SZ, blade_speed_buffer_type T>
 | 
			
		||||
struct blade_otw_buffer_helper {
 | 
			
		||||
	static_assert((SZ >= 1024 && ((SZ & (SZ - 1)) == 0)), "only buffer size multiples of 1024 allowed!");
 | 
			
		||||
	static blade_otw_buffer<SZ / 512, T> x;
 | 
			
		||||
};
 | 
			
		||||
@@ -181,17 +186,19 @@ using dev_buf_t = typeof(blade_otw_buffer_helper<BLADE_BUFFER_SIZE, blade_speed_
 | 
			
		||||
// using buf_in_use = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
 | 
			
		||||
using bh_fn_t = std::function<int(dev_buf_t *)>;
 | 
			
		||||
 | 
			
		||||
template <typename T> struct blade_hw {
 | 
			
		||||
template <typename T>
 | 
			
		||||
struct blade_hw {
 | 
			
		||||
	struct bladerf *dev;
 | 
			
		||||
	struct bladerf_stream *rx_stream;
 | 
			
		||||
	struct bladerf_stream *tx_stream;
 | 
			
		||||
	// using pkt2buf = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
 | 
			
		||||
	using tx_buf_q_type = spsc_cond<BLADE_NUM_BUFFERS, dev_buf_t *, true, false>;
 | 
			
		||||
	using tx_buf_q_type = spsc_cond_timeout<BLADE_NUM_BUFFERS, dev_buf_t *, true, false>;
 | 
			
		||||
	const unsigned int rxFullScale, txFullScale;
 | 
			
		||||
	const int rxtxdelay;
 | 
			
		||||
 | 
			
		||||
	float rxgain, txgain;
 | 
			
		||||
	static std::atomic<bool> stop_me_flag;
 | 
			
		||||
	static std::atomic<bool> stop_lower_threads_flag;
 | 
			
		||||
	double rxfreq_cache, txfreq_cache;
 | 
			
		||||
 | 
			
		||||
	struct ms_trx_config {
 | 
			
		||||
		int tx_freq;
 | 
			
		||||
@@ -216,7 +223,9 @@ template <typename T> struct blade_hw {
 | 
			
		||||
	{
 | 
			
		||||
		close_device();
 | 
			
		||||
	}
 | 
			
		||||
	blade_hw() : rxFullScale(2047), txFullScale(2047), rxtxdelay(-60)
 | 
			
		||||
	blade_hw()
 | 
			
		||||
		: rxFullScale(2047), txFullScale(2047), rxtxdelay(-60), rxgain(30), txgain(30), rxfreq_cache(0),
 | 
			
		||||
		  txfreq_cache(0)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -245,6 +254,7 @@ template <typename T> struct blade_hw {
 | 
			
		||||
 | 
			
		||||
		bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_DEBUG);
 | 
			
		||||
		bladerf_set_usb_reset_on_open(true);
 | 
			
		||||
 | 
			
		||||
		blade_check(bladerf_open, &dev, "");
 | 
			
		||||
		if (!dev) {
 | 
			
		||||
			std::cerr << "open failed, device missing?" << std::endl;
 | 
			
		||||
@@ -259,7 +269,8 @@ template <typename T> struct blade_hw {
 | 
			
		||||
 | 
			
		||||
		bool is_locked;
 | 
			
		||||
		blade_check(bladerf_set_pll_enable, dev, true);
 | 
			
		||||
		blade_check(bladerf_set_pll_refclk, dev, 10000000UL);
 | 
			
		||||
		uint64_t refclock = 10000000UL;
 | 
			
		||||
		blade_check(bladerf_set_pll_refclk, dev, refclock);
 | 
			
		||||
		for (int i = 0; i < 20; i++) {
 | 
			
		||||
			usleep(50 * 1000);
 | 
			
		||||
			bladerf_get_pll_lock_state(dev, &is_locked);
 | 
			
		||||
@@ -284,13 +295,13 @@ template <typename T> struct blade_hw {
 | 
			
		||||
			    (bladerf_bandwidth *)NULL);
 | 
			
		||||
 | 
			
		||||
		blade_check(bladerf_set_gain_mode, dev, BLADERF_CHANNEL_RX(0), BLADERF_GAIN_MGC);
 | 
			
		||||
		blade_check(bladerf_set_gain, dev, BLADERF_CHANNEL_RX(0), (bladerf_gain)30);
 | 
			
		||||
		blade_check(bladerf_set_gain, dev, BLADERF_CHANNEL_TX(0), (bladerf_gain)30);
 | 
			
		||||
		usleep(1000);
 | 
			
		||||
		blade_check(bladerf_enable_module, dev, BLADERF_MODULE_RX, true);
 | 
			
		||||
		usleep(1000);
 | 
			
		||||
		blade_check(bladerf_enable_module, dev, BLADERF_MODULE_TX, true);
 | 
			
		||||
		setRxGain(rxgain, 0);
 | 
			
		||||
		setTxGain(txgain, 0);
 | 
			
		||||
		usleep(1000);
 | 
			
		||||
 | 
			
		||||
		bladerf_set_stream_timeout(dev, BLADERF_TX, 10);
 | 
			
		||||
		bladerf_set_stream_timeout(dev, BLADERF_RX, 10);
 | 
			
		||||
 | 
			
		||||
		blade_check(bladerf_init_stream, &rx_stream, dev, getrxcb(rxh), &buf_mgmt.rx_samples, BLADE_NUM_BUFFERS,
 | 
			
		||||
			    BLADERF_FORMAT_SC16_Q11_META, BLADE_BUFFER_SIZE, NUM_TRANSFERS, (void *)this);
 | 
			
		||||
 | 
			
		||||
@@ -302,28 +313,33 @@ template <typename T> struct blade_hw {
 | 
			
		||||
			buf_mgmt.bufptrqueue.spsc_push(&cur_buffer[i]);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		setRxGain(20);
 | 
			
		||||
		setTxGain(30);
 | 
			
		||||
 | 
			
		||||
		usleep(1000);
 | 
			
		||||
 | 
			
		||||
		// bladerf_set_stream_timeout(dev, BLADERF_TX, 4);
 | 
			
		||||
		// bladerf_set_stream_timeout(dev, BLADERF_RX, 4);
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void actually_enable_streams()
 | 
			
		||||
	{
 | 
			
		||||
		blade_check(bladerf_enable_module, dev, BLADERF_MODULE_RX, true);
 | 
			
		||||
		usleep(1000);
 | 
			
		||||
		blade_check(bladerf_enable_module, dev, BLADERF_MODULE_TX, true);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bool tuneTx(double freq, size_t chan = 0)
 | 
			
		||||
	{
 | 
			
		||||
		if (txfreq_cache == freq)
 | 
			
		||||
			return true;
 | 
			
		||||
		msleep(15);
 | 
			
		||||
		blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_TX(0), (bladerf_frequency)freq);
 | 
			
		||||
		txfreq_cache = freq;
 | 
			
		||||
		msleep(15);
 | 
			
		||||
		return true;
 | 
			
		||||
	};
 | 
			
		||||
	bool tuneRx(double freq, size_t chan = 0)
 | 
			
		||||
	{
 | 
			
		||||
		if (rxfreq_cache == freq)
 | 
			
		||||
			return true;
 | 
			
		||||
		msleep(15);
 | 
			
		||||
		blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_RX(0), (bladerf_frequency)freq);
 | 
			
		||||
		rxfreq_cache = freq;
 | 
			
		||||
		msleep(15);
 | 
			
		||||
		return true;
 | 
			
		||||
	};
 | 
			
		||||
@@ -379,7 +395,7 @@ template <typename T> struct blade_hw {
 | 
			
		||||
			static int to_skip = 0;
 | 
			
		||||
			dev_buf_t *rcd = (dev_buf_t *)samples;
 | 
			
		||||
 | 
			
		||||
			if (stop_me_flag)
 | 
			
		||||
			if (stop_lower_threads_flag)
 | 
			
		||||
				return BLADERF_STREAM_SHUTDOWN;
 | 
			
		||||
 | 
			
		||||
			if (to_skip < 120) // prevents weird overflows on startup
 | 
			
		||||
@@ -404,7 +420,7 @@ template <typename T> struct blade_hw {
 | 
			
		||||
			if (samples) // put buffer address back into queue, ready to be reused
 | 
			
		||||
				trx->buf_mgmt.bufptrqueue.spsc_push(&ptr);
 | 
			
		||||
 | 
			
		||||
			if (stop_me_flag)
 | 
			
		||||
			if (stop_lower_threads_flag)
 | 
			
		||||
				return BLADERF_STREAM_SHUTDOWN;
 | 
			
		||||
 | 
			
		||||
			return BLADERF_STREAM_NO_DATA;
 | 
			
		||||
@@ -413,9 +429,12 @@ template <typename T> struct blade_hw {
 | 
			
		||||
 | 
			
		||||
	auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
 | 
			
		||||
	{
 | 
			
		||||
		auto fn = [this] {
 | 
			
		||||
			int status;
 | 
			
		||||
			status = bladerf_stream(rx_stream, BLADERF_RX_X1);
 | 
			
		||||
		using thist = decltype(this);
 | 
			
		||||
		auto fn = [](void *args) -> void * {
 | 
			
		||||
			thist t = reinterpret_cast<thist>(args);
 | 
			
		||||
			int status = 0;
 | 
			
		||||
			if (!stop_lower_threads_flag)
 | 
			
		||||
				status = bladerf_stream(t->rx_stream, BLADERF_RX_X1);
 | 
			
		||||
			if (status < 0)
 | 
			
		||||
				std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
 | 
			
		||||
 | 
			
		||||
@@ -425,9 +444,12 @@ template <typename T> struct blade_hw {
 | 
			
		||||
	}
 | 
			
		||||
	auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
 | 
			
		||||
	{
 | 
			
		||||
		auto fn = [this] {
 | 
			
		||||
			int status;
 | 
			
		||||
			status = bladerf_stream(tx_stream, BLADERF_TX_X1);
 | 
			
		||||
		using thist = decltype(this);
 | 
			
		||||
		auto fn = [](void *args) -> void * {
 | 
			
		||||
			thist t = reinterpret_cast<thist>(args);
 | 
			
		||||
			int status = 0;
 | 
			
		||||
			if (!stop_lower_threads_flag)
 | 
			
		||||
				status = bladerf_stream(t->tx_stream, BLADERF_TX_X1);
 | 
			
		||||
			if (status < 0)
 | 
			
		||||
				std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
 | 
			
		||||
 | 
			
		||||
@@ -438,9 +460,15 @@ template <typename T> struct blade_hw {
 | 
			
		||||
 | 
			
		||||
	void submit_burst_ts(blade_sample_type *buffer, int len, uint64_t ts)
 | 
			
		||||
	{
 | 
			
		||||
		//get empty bufer from list
 | 
			
		||||
		tx_buf_q_type::elem_t rcd;
 | 
			
		||||
 | 
			
		||||
		// exit by submitting a dummy buffer to assure the libbladerf stream mutex is happy (thread!)
 | 
			
		||||
		if (!buffer) {
 | 
			
		||||
			bladerf_submit_stream_buffer(tx_stream, (void *)BLADERF_STREAM_SHUTDOWN, 1000);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//get empty bufer from list
 | 
			
		||||
		while (!buf_mgmt.bufptrqueue.spsc_pop(&rcd))
 | 
			
		||||
			buf_mgmt.bufptrqueue.spsc_prep_pop();
 | 
			
		||||
		assert(rcd != nullptr);
 | 
			
		||||
 
 | 
			
		||||
@@ -29,7 +29,58 @@
 | 
			
		||||
 | 
			
		||||
namespace spsc_detail
 | 
			
		||||
{
 | 
			
		||||
template <bool block_read, bool block_write> class spsc_cond_detail {
 | 
			
		||||
template <bool block_read, bool block_write>
 | 
			
		||||
class spsc_cond_timeout_detail {
 | 
			
		||||
	std::condition_variable cond_r, cond_w;
 | 
			
		||||
	std::mutex lr, lw;
 | 
			
		||||
	std::atomic_int r_flag, w_flag;
 | 
			
		||||
	const int timeout_ms = 200;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
	explicit spsc_cond_timeout_detail() : r_flag(0), w_flag(0)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	~spsc_cond_timeout_detail()
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	ssize_t spsc_check_r()
 | 
			
		||||
	{
 | 
			
		||||
		std::unique_lock<std::mutex> lk(lr);
 | 
			
		||||
		if (cond_r.wait_for(lk, std::chrono::milliseconds(timeout_ms), [&] { return r_flag != 0; })) {
 | 
			
		||||
			r_flag--;
 | 
			
		||||
			return 1;
 | 
			
		||||
		} else {
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	ssize_t spsc_check_w()
 | 
			
		||||
	{
 | 
			
		||||
		std::unique_lock<std::mutex> lk(lw);
 | 
			
		||||
		if (cond_w.wait_for(lk, std::chrono::milliseconds(timeout_ms), [&] { return w_flag != 0; })) {
 | 
			
		||||
			w_flag--;
 | 
			
		||||
			return 1;
 | 
			
		||||
		} else {
 | 
			
		||||
			return 0;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	void spsc_notify_r()
 | 
			
		||||
	{
 | 
			
		||||
		std::unique_lock<std::mutex> lk(lr);
 | 
			
		||||
		r_flag++;
 | 
			
		||||
		cond_r.notify_one();
 | 
			
		||||
	}
 | 
			
		||||
	void spsc_notify_w()
 | 
			
		||||
	{
 | 
			
		||||
		std::unique_lock<std::mutex> lk(lw);
 | 
			
		||||
		w_flag++;
 | 
			
		||||
		cond_w.notify_one();
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <bool block_read, bool block_write>
 | 
			
		||||
class spsc_cond_detail {
 | 
			
		||||
	std::condition_variable cond_r, cond_w;
 | 
			
		||||
	std::mutex lr, lw;
 | 
			
		||||
	std::atomic_int r_flag, w_flag;
 | 
			
		||||
@@ -74,7 +125,8 @@ template <bool block_read, bool block_write> class spsc_cond_detail {
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// originally designed for select loop integration
 | 
			
		||||
template <bool block_read, bool block_write> class spsc_efd_detail {
 | 
			
		||||
template <bool block_read, bool block_write>
 | 
			
		||||
class spsc_efd_detail {
 | 
			
		||||
	int efd_r, efd_w; /* eventfds used to block/notify readers/writers */
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
@@ -191,4 +243,7 @@ class spsc : public T<block_read, block_write> {
 | 
			
		||||
template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
 | 
			
		||||
class spsc_evfd : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_efd_detail> {};
 | 
			
		||||
template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
 | 
			
		||||
class spsc_cond : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_detail> {};
 | 
			
		||||
class spsc_cond : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_detail> {};
 | 
			
		||||
template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
 | 
			
		||||
class spsc_cond_timeout
 | 
			
		||||
	: public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_timeout_detail> {};
 | 
			
		||||
@@ -24,13 +24,10 @@ extern "C" {
 | 
			
		||||
#include <osmocom/bb/trxcon/trxcon_fsm.h>
 | 
			
		||||
#include <osmocom/bb/trxcon/l1ctl_server.h>
 | 
			
		||||
}
 | 
			
		||||
#include "ms_trxcon_if.h"
 | 
			
		||||
 | 
			
		||||
static struct l1ctl_server_cfg server_cfg;
 | 
			
		||||
static struct l1ctl_server *server = NULL;
 | 
			
		||||
namespace trxcon
 | 
			
		||||
{
 | 
			
		||||
extern struct trxcon_inst *g_trxcon;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
@@ -41,9 +38,9 @@ static int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg)
 | 
			
		||||
 | 
			
		||||
static void l1ctl_conn_accept_cb(struct l1ctl_client *l1c)
 | 
			
		||||
{
 | 
			
		||||
	l1c->log_prefix = talloc_strdup(l1c, trxcon::g_trxcon->log_prefix);
 | 
			
		||||
	l1c->priv = trxcon::g_trxcon;
 | 
			
		||||
	trxcon::g_trxcon->l2if = l1c;
 | 
			
		||||
	l1c->log_prefix = talloc_strdup(l1c, g_trxcon->log_prefix);
 | 
			
		||||
	l1c->priv = g_trxcon;
 | 
			
		||||
	g_trxcon->l2if = l1c;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void l1ctl_conn_close_cb(struct l1ctl_client *l1c)
 | 
			
		||||
@@ -56,17 +53,13 @@ static void l1ctl_conn_close_cb(struct l1ctl_client *l1c)
 | 
			
		||||
	osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_L2IF_FAILURE, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace trxcon
 | 
			
		||||
{
 | 
			
		||||
bool trxc_l1ctl_init(void *tallctx)
 | 
			
		||||
{
 | 
			
		||||
	/* Start the L1CTL server */
 | 
			
		||||
	server_cfg = (struct l1ctl_server_cfg){
 | 
			
		||||
		/* TODO: make path configurable */
 | 
			
		||||
		.sock_path = "/tmp/osmocom_l2",
 | 
			
		||||
		.num_clients_max = 1,
 | 
			
		||||
		.conn_read_cb = &l1ctl_rx_cb,
 | 
			
		||||
		.conn_accept_cb = &l1ctl_conn_accept_cb,
 | 
			
		||||
		.sock_path = "/tmp/osmocom_l2",	       .num_clients_max = 1,
 | 
			
		||||
		.conn_read_cb = &l1ctl_rx_cb,	       .conn_accept_cb = &l1ctl_conn_accept_cb,
 | 
			
		||||
		.conn_close_cb = &l1ctl_conn_close_cb,
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
@@ -76,4 +69,3 @@ bool trxc_l1ctl_init(void *tallctx)
 | 
			
		||||
	}
 | 
			
		||||
	return true;
 | 
			
		||||
}
 | 
			
		||||
} // namespace trxcon
 | 
			
		||||
@@ -1,5 +1,7 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2016-2022 by Vadim Yanitskiy <axilirator@gmail.com>
 | 
			
		||||
 * OsmocomBB <-> SDR connection bridge
 | 
			
		||||
 *
 | 
			
		||||
 * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
 | 
			
		||||
 *
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
@@ -19,48 +21,61 @@
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/core/utils.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/bb/trxcon/logging.h>
 | 
			
		||||
#include <osmocom/bb/trxcon/trxcon.h>
 | 
			
		||||
#include <osmocom/bb/trxcon/logging.h>
 | 
			
		||||
 | 
			
		||||
static struct log_info_cat trxcon_log_info_cat[] = {
 | 
			
		||||
	[DAPP] = {
 | 
			
		||||
		.name = "DAPP",
 | 
			
		||||
		.color = "\033[1;35m",
 | 
			
		||||
		.description = "Application",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
		.color = "\033[1;35m",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DL1C] = {
 | 
			
		||||
		.name = "DL1C",
 | 
			
		||||
		.color = "\033[1;31m",
 | 
			
		||||
		.description = "Layer 1 control interface",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
		.color = "\033[1;31m",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DL1D] = {
 | 
			
		||||
		.name = "DL1D",
 | 
			
		||||
		.color = "\033[1;31m",
 | 
			
		||||
		.description = "Layer 1 data",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 1,
 | 
			
		||||
		.color = "\033[1;31m",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DTRXC] = {
 | 
			
		||||
		.name = "DTRXC",
 | 
			
		||||
		.description = "Transceiver control interface",
 | 
			
		||||
		.color = "\033[1;33m",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DTRXD] = {
 | 
			
		||||
		.name = "DTRXD",
 | 
			
		||||
		.description = "Transceiver data interface",
 | 
			
		||||
		.color = "\033[1;33m",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DSCH] = {
 | 
			
		||||
		.name = "DSCH",
 | 
			
		||||
		.color = "\033[1;36m",
 | 
			
		||||
		.description = "Scheduler management",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 0,
 | 
			
		||||
		.color = "\033[1;36m",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DSCHD] = {
 | 
			
		||||
		.name = "DSCHD",
 | 
			
		||||
		.color = "\033[1;36m",
 | 
			
		||||
		.description = "Scheduler data",
 | 
			
		||||
		.loglevel = LOGL_NOTICE,
 | 
			
		||||
		.enabled = 0,
 | 
			
		||||
		.color = "\033[1;36m",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
	[DGPRS] = {
 | 
			
		||||
		.name = "DGPRS",
 | 
			
		||||
		.description = "L1 GPRS (MAC layer)",
 | 
			
		||||
		.color = "\033[1;36m",
 | 
			
		||||
		.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static struct log_info trxcon_log_info = {
 | 
			
		||||
static const struct log_info trxcon_log_info = {
 | 
			
		||||
	.cat = trxcon_log_info_cat,
 | 
			
		||||
	.num_cat = ARRAY_SIZE(trxcon_log_info_cat),
 | 
			
		||||
};
 | 
			
		||||
@@ -71,11 +86,13 @@ static const int trxcon_log_cfg[] = {
 | 
			
		||||
	[TRXCON_LOGC_L1D] = DL1D,
 | 
			
		||||
	[TRXCON_LOGC_SCHC] = DSCH,
 | 
			
		||||
	[TRXCON_LOGC_SCHD] = DSCHD,
 | 
			
		||||
	[TRXCON_LOGC_GPRS] = DGPRS,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void trxc_log_init(void *tallctx)
 | 
			
		||||
{
 | 
			
		||||
	osmo_init_logging2(tallctx, &trxcon_log_info);
 | 
			
		||||
	log_target_file_switch_to_wqueue(osmo_stderr_target);
 | 
			
		||||
 | 
			
		||||
	trxcon_set_log_cfg(&trxcon_log_cfg[0], ARRAY_SIZE(trxcon_log_cfg));
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -29,18 +29,14 @@
 | 
			
		||||
#include <thread>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
 | 
			
		||||
#include "sigProcLib.h"
 | 
			
		||||
 | 
			
		||||
#include "ms.h"
 | 
			
		||||
#include "ms_rx_burst.h"
 | 
			
		||||
#include "grgsm_vitac/grgsm_vitac.h"
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include "sch.h"
 | 
			
		||||
#include "convolve.h"
 | 
			
		||||
#include "convert.h"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include "threadsched.h"
 | 
			
		||||
 | 
			
		||||
dummylog ms_trx::dummy_log;
 | 
			
		||||
 | 
			
		||||
#ifdef DBGXX
 | 
			
		||||
@@ -49,199 +45,13 @@ const int offset_start = -15;
 | 
			
		||||
static int offset_ctr = 0;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
template <> std::atomic<bool> ms_trx::base::stop_me_flag(false);
 | 
			
		||||
 | 
			
		||||
void tx_test(ms_trx *t, ts_hitter_q_t *q, unsigned int *tsc)
 | 
			
		||||
{
 | 
			
		||||
	sched_param sch_params;
 | 
			
		||||
	sch_params.sched_priority = sched_get_priority_max(SCHED_FIFO);
 | 
			
		||||
	pthread_setschedparam(pthread_self(), SCHED_FIFO, &sch_params);
 | 
			
		||||
 | 
			
		||||
	auto burst = genRandAccessBurst(0, 4, 0);
 | 
			
		||||
	scaleVector(*burst, t->txFullScale * 0.7);
 | 
			
		||||
 | 
			
		||||
	// float -> int16
 | 
			
		||||
	blade_sample_type burst_buf[burst->size()];
 | 
			
		||||
	convert_and_scale<int16_t, float>(burst_buf, burst->begin(), burst->size() * 2, 1);
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		GSM::Time target;
 | 
			
		||||
		while (!q->spsc_pop(&target)) {
 | 
			
		||||
			q->spsc_prep_pop();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		std::cerr << std::endl << "\x1B[32m hitting " << target.FN() << "\033[0m" << std::endl;
 | 
			
		||||
 | 
			
		||||
		int timing_advance = 0;
 | 
			
		||||
		int64_t now_ts;
 | 
			
		||||
		GSM::Time now_time;
 | 
			
		||||
		target.incTN(3); // ul dl offset
 | 
			
		||||
		int target_fn = target.FN();
 | 
			
		||||
		int target_tn = target.TN();
 | 
			
		||||
		t->timekeeper.get_both(&now_time, &now_ts);
 | 
			
		||||
 | 
			
		||||
		auto diff_fn = GSM::FNDelta(target_fn, now_time.FN());
 | 
			
		||||
		int diff_tn = (target_tn - (int)now_time.TN()) % 8;
 | 
			
		||||
		auto tosend = GSM::Time(diff_fn, 0);
 | 
			
		||||
 | 
			
		||||
		if (diff_tn > 0)
 | 
			
		||||
			tosend.incTN(diff_tn);
 | 
			
		||||
		else if (diff_tn < 0)
 | 
			
		||||
			tosend.decTN(-diff_tn);
 | 
			
		||||
 | 
			
		||||
		// in thory fn equal and tn+3 equal is also a problem...
 | 
			
		||||
		if (diff_fn < 0 || (diff_fn == 0 && (now_time.TN() - target_tn < 1))) {
 | 
			
		||||
			std::cerr << "## TX too late?! fn DIFF:" << diff_fn << " tn LOCAL: " << now_time.TN()
 | 
			
		||||
				  << " tn OTHER: " << target_tn << std::endl;
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto check = now_time + tosend;
 | 
			
		||||
		int64_t send_ts =
 | 
			
		||||
			now_ts + tosend.FN() * 8 * ONE_TS_BURST_LEN + tosend.TN() * ONE_TS_BURST_LEN - timing_advance;
 | 
			
		||||
 | 
			
		||||
#ifdef DBGXX
 | 
			
		||||
		std::cerr << "## fn DIFF: " << diff_fn << " ## tn DIFF: " << diff_tn << " tn LOCAL: " << now_time.TN()
 | 
			
		||||
			  << " tn OTHER: " << target_tn << " tndiff" << diff_tn << " tosend:" << tosend.FN() << ":"
 | 
			
		||||
			  << tosend.TN() << " calc: " << check.FN() << ":" << check.TN() << " target: " << target.FN()
 | 
			
		||||
			  << ":" << target.TN() << " ts now: " << now_ts << " target ts:" << send_ts << std::endl;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		unsigned int pad = 4 * 25;
 | 
			
		||||
		blade_sample_type buf2[burst->size() + pad];
 | 
			
		||||
		std::fill(buf2, buf2 + pad, 0);
 | 
			
		||||
 | 
			
		||||
		memcpy(&buf2[pad], burst_buf, burst->size() * sizeof(blade_sample_type));
 | 
			
		||||
 | 
			
		||||
		assert(target.FN() == check.FN());
 | 
			
		||||
		assert(target.TN() == check.TN());
 | 
			
		||||
		assert(target.FN() % 51 == 21);
 | 
			
		||||
#ifdef DBGXX
 | 
			
		||||
		auto this_offset = offset_start + (offset_ctr++ % offsetrange);
 | 
			
		||||
		std::cerr << "-- O " << this_offset << std::endl;
 | 
			
		||||
		send_ts = now_ts - timing_advance +
 | 
			
		||||
			  ((target.FN() * 8 + (int)target.TN()) - (now_time.FN() * 8 + (int)now_time.TN())) *
 | 
			
		||||
				  ONE_TS_BURST_LEN;
 | 
			
		||||
#endif
 | 
			
		||||
		t->submit_burst_ts(buf2, burst->size() + pad, send_ts - pad);
 | 
			
		||||
#ifdef DBGXX
 | 
			
		||||
		signalVector test(burst->size() + pad);
 | 
			
		||||
		convert_and_scale<float, int16_t>(test.begin(), buf2, burst->size() * 2 + pad, 1.f / float(scale));
 | 
			
		||||
		estim_burst_params ebp;
 | 
			
		||||
		auto det = detectAnyBurst(test, 0, 4, 4, CorrType::RACH, 40, &ebp);
 | 
			
		||||
		if (det > 0)
 | 
			
		||||
			std::cerr << "## Y " << ebp.toa << std::endl;
 | 
			
		||||
		else
 | 
			
		||||
			std::cerr << "## NOOOOOOOOO " << ebp.toa << std::endl;
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
#ifdef SYNCTHINGONLY
 | 
			
		||||
template <typename A> auto parsec(std::vector<std::string> &v, A &itr, std::string arg, bool *rv)
 | 
			
		||||
{
 | 
			
		||||
	if (*itr == arg) {
 | 
			
		||||
		*rv = true;
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename A, typename B, typename C>
 | 
			
		||||
bool parsec(std::vector<std::string> &v, A &itr, std::string arg, B f, C *rv)
 | 
			
		||||
{
 | 
			
		||||
	if (*itr == arg) {
 | 
			
		||||
		itr++;
 | 
			
		||||
		if (itr != v.end()) {
 | 
			
		||||
			*rv = f(itr->c_str());
 | 
			
		||||
			return true;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
template <typename A> bool parsec(std::vector<std::string> &v, A &itr, std::string arg, int scale, int *rv)
 | 
			
		||||
{
 | 
			
		||||
	return parsec(
 | 
			
		||||
		v, itr, arg, [scale](const char *v) -> auto{ return atoi(v) * scale; }, rv);
 | 
			
		||||
}
 | 
			
		||||
template <typename A> bool parsec(std::vector<std::string> &v, A &itr, std::string arg, int scale, unsigned int *rv)
 | 
			
		||||
{
 | 
			
		||||
	return parsec(
 | 
			
		||||
		v, itr, arg, [scale](const char *v) -> auto{ return atoi(v) * scale; }, rv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	cpu_set_t cpuset;
 | 
			
		||||
 | 
			
		||||
	CPU_ZERO(&cpuset);
 | 
			
		||||
	CPU_SET(2, &cpuset);
 | 
			
		||||
 | 
			
		||||
	auto rv = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
 | 
			
		||||
	if (rv < 0) {
 | 
			
		||||
		std::cerr << "affinity: errreur! " << std::strerror(errno);
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	unsigned int default_tx_freq(881000 * 1000), default_rx_freq(926000 * 1000);
 | 
			
		||||
	unsigned int grx = 20, gtx = 20;
 | 
			
		||||
	bool tx_flag = false;
 | 
			
		||||
	pthread_setname_np(pthread_self(), "main");
 | 
			
		||||
	convolve_init();
 | 
			
		||||
	convert_init();
 | 
			
		||||
	sigProcLibSetup();
 | 
			
		||||
	initvita();
 | 
			
		||||
 | 
			
		||||
	int status = 0;
 | 
			
		||||
	auto trx = new ms_trx();
 | 
			
		||||
	trx->do_auto_gain = true;
 | 
			
		||||
 | 
			
		||||
	std::vector<std::string> args(argv + 1, argv + argc);
 | 
			
		||||
	for (auto i = args.begin(); i != args.end(); ++i) {
 | 
			
		||||
		parsec(args, i, "-r", 1000, &default_rx_freq);
 | 
			
		||||
		parsec(args, i, "-t", 1000, &default_tx_freq);
 | 
			
		||||
		parsec(args, i, "-gr", 1, &grx);
 | 
			
		||||
		parsec(args, i, "-gt", 1, >x);
 | 
			
		||||
		parsec(args, i, "-tx", &tx_flag);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	std::cerr << "usage: " << argv[0] << " <rxfreq in khz, i.e. 926000> [txfreq in khz, i.e. 881000] [TX]"
 | 
			
		||||
		  << std::endl
 | 
			
		||||
		  << "rx" << (argc == 1 ? " (default) " : " ") << default_rx_freq << "hz, tx " << default_tx_freq
 | 
			
		||||
		  << "hz" << std::endl
 | 
			
		||||
		  << "gain rx " << grx << " gain tx " << gtx << std::endl
 | 
			
		||||
		  << (tx_flag ? "##!!## RACH TX ACTIVE ##!!##" : "-- no rach tx --") << std::endl;
 | 
			
		||||
 | 
			
		||||
	status = trx->init_dev_and_streams();
 | 
			
		||||
	if (status < 0)
 | 
			
		||||
		return status;
 | 
			
		||||
	trx->tuneRx(default_rx_freq);
 | 
			
		||||
	trx->tuneTx(default_tx_freq);
 | 
			
		||||
	trx->setRxGain(grx);
 | 
			
		||||
	trx->setTxGain(gtx);
 | 
			
		||||
 | 
			
		||||
	if (status == 0) {
 | 
			
		||||
		// FIXME: hacks! needs exit flag for detached threads!
 | 
			
		||||
 | 
			
		||||
		std::thread(rcv_bursts_test, &trx->rxqueue, &trx->mTSC, trx->rxFullScale).detach();
 | 
			
		||||
		if (tx_flag)
 | 
			
		||||
			std::thread(tx_test, trx, &trx->ts_hitter_q, &trx->mTSC).detach();
 | 
			
		||||
		trx->start();
 | 
			
		||||
		do {
 | 
			
		||||
			sleep(1);
 | 
			
		||||
		} while (1);
 | 
			
		||||
 | 
			
		||||
		trx->stop_threads();
 | 
			
		||||
	}
 | 
			
		||||
	delete trx;
 | 
			
		||||
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
template <>
 | 
			
		||||
std::atomic<bool> ms_trx::base::stop_lower_threads_flag(false);
 | 
			
		||||
 | 
			
		||||
int ms_trx::init_dev_and_streams()
 | 
			
		||||
{
 | 
			
		||||
	int status = 0;
 | 
			
		||||
	status = base::init_device(rx_bh(), tx_bh());
 | 
			
		||||
	status = init_device(rx_bh(), tx_bh());
 | 
			
		||||
	if (status < 0) {
 | 
			
		||||
		std::cerr << "failed to init dev!" << std::endl;
 | 
			
		||||
		return -1;
 | 
			
		||||
@@ -270,16 +80,18 @@ bh_fn_t ms_trx::tx_bh()
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ms_trx::start()
 | 
			
		||||
void ms_trx::start_lower_ms()
 | 
			
		||||
{
 | 
			
		||||
	if (stop_lower_threads_flag)
 | 
			
		||||
		return;
 | 
			
		||||
	auto fn = get_rx_burst_handler_fn(rx_bh());
 | 
			
		||||
	rx_task = std::thread(fn);
 | 
			
		||||
	set_name_aff_sched(rx_task.native_handle(), "rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2);
 | 
			
		||||
	lower_rx_task = spawn_worker_thread(sched_params::thread_names::RXRUN, fn, this);
 | 
			
		||||
 | 
			
		||||
	usleep(1000);
 | 
			
		||||
	auto fn2 = get_tx_burst_handler_fn(tx_bh());
 | 
			
		||||
	tx_task = std::thread(fn2);
 | 
			
		||||
	set_name_aff_sched(tx_task.native_handle(), "txrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1);
 | 
			
		||||
	lower_tx_task = spawn_worker_thread(sched_params::thread_names::TXRUN, fn2, this);
 | 
			
		||||
 | 
			
		||||
	actually_enable_streams();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ms_trx::set_upper_ready(bool is_ready)
 | 
			
		||||
@@ -289,11 +101,14 @@ void ms_trx::set_upper_ready(bool is_ready)
 | 
			
		||||
 | 
			
		||||
void ms_trx::stop_threads()
 | 
			
		||||
{
 | 
			
		||||
	std::cerr << "killing threads...\r\n" << std::endl;
 | 
			
		||||
	stop_me_flag = true;
 | 
			
		||||
	std::cerr << "killing threads..." << std::endl;
 | 
			
		||||
	stop_lower_threads_flag = true;
 | 
			
		||||
	close_device();
 | 
			
		||||
	rx_task.join();
 | 
			
		||||
	tx_task.join();
 | 
			
		||||
	std::cerr << "dev closed..." << std::endl;
 | 
			
		||||
	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)
 | 
			
		||||
@@ -315,21 +130,22 @@ void ms_trx::submit_burst(blade_sample_type *buffer, int len, GSM::Time target)
 | 
			
		||||
		tosend.decTN(-diff_tn);
 | 
			
		||||
 | 
			
		||||
	// in theory fn equal and tn+3 equal is also a problem...
 | 
			
		||||
	if (diff_fn < 0 || (diff_fn == 0 && (now_time.TN() - target_tn < 1))) {
 | 
			
		||||
	if (diff_fn < 0 || (diff_fn == 0 && (target_tn-now_time.TN() < 3))) {
 | 
			
		||||
		std::cerr << "## TX too late?! fn DIFF:" << diff_fn << " tn LOCAL: " << now_time.TN()
 | 
			
		||||
			  << " tn OTHER: " << target_tn << std::endl;
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto check = now_time + tosend;
 | 
			
		||||
	int64_t send_ts = now_ts + tosend.FN() * 8 * ONE_TS_BURST_LEN + tosend.TN() * ONE_TS_BURST_LEN - timing_advance;
 | 
			
		||||
#ifdef DBGXX
 | 
			
		||||
	auto check = now_time + tosend;
 | 
			
		||||
	std::cerr << "## fn DIFF: " << diff_fn << " ## tn DIFF: " << diff_tn << " tn LOCAL/OTHER: " << now_time.TN()
 | 
			
		||||
		  << "/" << target_tn << " tndiff" << diff_tn << " tosend:" << tosend.FN() << ":" << tosend.TN()
 | 
			
		||||
		  << " check: " << check.FN() << ":" << check.TN() << " target: " << target.FN() << ":" << target.TN()
 | 
			
		||||
		  << " ts now: " << now_ts << " target ts:" << send_ts << std::endl;
 | 
			
		||||
#endif
 | 
			
		||||
#if 1
 | 
			
		||||
#if 0
 | 
			
		||||
	auto check = now_time + tosend;
 | 
			
		||||
	unsigned int pad = 4 * 4;
 | 
			
		||||
	blade_sample_type buf2[len + pad];
 | 
			
		||||
	std::fill(buf2, buf2 + pad, 0);
 | 
			
		||||
 
 | 
			
		||||
@@ -26,7 +26,7 @@
 | 
			
		||||
#include <cstdint>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <thread>
 | 
			
		||||
// #include <thread>
 | 
			
		||||
 | 
			
		||||
#if defined(BUILDBLADE)
 | 
			
		||||
#include "bladerf_specific.h"
 | 
			
		||||
@@ -38,30 +38,88 @@
 | 
			
		||||
#error wat? no device..
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include "Complex.h"
 | 
			
		||||
#include "GSMCommon.h"
 | 
			
		||||
#include "itrq.h"
 | 
			
		||||
#include "threadpool.h"
 | 
			
		||||
#include "threadsched.h"
 | 
			
		||||
 | 
			
		||||
const unsigned int ONE_TS_BURST_LEN = (3 + 58 + 26 + 58 + 3 + 8.25) * 4 /*sps*/;
 | 
			
		||||
const unsigned int NUM_RXQ_FRAMES = 1; // rx thread <-> upper rx queue
 | 
			
		||||
const unsigned int SCH_LEN_SPS = (ONE_TS_BURST_LEN * 8 /*ts*/ * 12 /*frames*/);
 | 
			
		||||
 | 
			
		||||
template <typename T> void clamp_array(T *start2, unsigned int len, T max)
 | 
			
		||||
template <typename T>
 | 
			
		||||
void clamp_array(T *start2, unsigned int len, T max)
 | 
			
		||||
{
 | 
			
		||||
	for (int i = 0; i < len; i++) {
 | 
			
		||||
	for (unsigned int i = 0; i < len; i++) {
 | 
			
		||||
		const T t1 = start2[i] < -max ? -max : start2[i];
 | 
			
		||||
		const T t2 = t1 > max ? max : t1;
 | 
			
		||||
		start2[i] = t2;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
template <typename DST_T, typename SRC_T, typename ST>
 | 
			
		||||
void convert_and_scale(void *dst, void *src, unsigned int src_len, ST scale)
 | 
			
		||||
 | 
			
		||||
namespace cvt_internal
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
template <typename SRC_T, typename ST>
 | 
			
		||||
void convert_and_scale_i(float *dst, const SRC_T *src, unsigned int src_len, ST scale)
 | 
			
		||||
{
 | 
			
		||||
	for (unsigned int i = 0; i < src_len; i++)
 | 
			
		||||
		reinterpret_cast<DST_T *>(dst)[i] = static_cast<DST_T>((reinterpret_cast<SRC_T *>(src)[i])) * scale;
 | 
			
		||||
		dst[i] = static_cast<float>(src[i]) * scale;
 | 
			
		||||
}
 | 
			
		||||
template <typename DST_T, typename SRC_T> void convert_and_scale_default(void *dst, void *src, unsigned int src_len)
 | 
			
		||||
 | 
			
		||||
template <typename DST_T, typename ST>
 | 
			
		||||
void convert_and_scale_i(DST_T *dst, const float *src, unsigned int src_len, ST scale)
 | 
			
		||||
{
 | 
			
		||||
	return convert_and_scale<DST_T, SRC_T>(dst, src, src_len, SAMPLE_SCALE_FACTOR);
 | 
			
		||||
	for (unsigned int i = 0; i < src_len; i++)
 | 
			
		||||
		dst[i] = static_cast<DST_T>(src[i] * scale);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename ST>
 | 
			
		||||
void convert_and_scale_i(float *dst, const float *src, unsigned int src_len, ST scale)
 | 
			
		||||
{
 | 
			
		||||
	for (unsigned int i = 0; i < src_len; i++)
 | 
			
		||||
		dst[i] = src[i] * scale;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
struct is_complex : std::false_type {
 | 
			
		||||
	using baset = T;
 | 
			
		||||
	static const unsigned int len_mul = 1;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
struct is_complex<std::complex<T>> : std::true_type {
 | 
			
		||||
	using baset = typename std::complex<T>::value_type;
 | 
			
		||||
	static const unsigned int len_mul = 2;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
struct is_complex<Complex<T>> : std::true_type {
 | 
			
		||||
	using baset = typename Complex<T>::value_type;
 | 
			
		||||
	static const unsigned int len_mul = 2;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace cvt_internal
 | 
			
		||||
 | 
			
		||||
template <typename DST_T, typename SRC_T, typename ST>
 | 
			
		||||
void convert_and_scale(DST_T *dst, const SRC_T *src, unsigned int src_len, ST scale)
 | 
			
		||||
{
 | 
			
		||||
	using vd = typename cvt_internal::is_complex<DST_T>::baset;
 | 
			
		||||
	using vs = typename cvt_internal::is_complex<SRC_T>::baset;
 | 
			
		||||
	return cvt_internal::convert_and_scale_i((vd *)dst, (vs *)src, src_len, scale);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename array_t>
 | 
			
		||||
float normed_abs_sum(array_t *src, int len)
 | 
			
		||||
{
 | 
			
		||||
	using vd = typename cvt_internal::is_complex<array_t>::baset;
 | 
			
		||||
	auto len_mul = cvt_internal::is_complex<array_t>::len_mul;
 | 
			
		||||
	auto ptr = reinterpret_cast<const vd *>(src);
 | 
			
		||||
	float sum = 0;
 | 
			
		||||
	for (unsigned int i = 0; i < len * len_mul; i++)
 | 
			
		||||
		sum += std::abs(ptr[i]);
 | 
			
		||||
	sum /= len * len_mul;
 | 
			
		||||
	return sum;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct one_burst {
 | 
			
		||||
@@ -75,7 +133,7 @@ struct one_burst {
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using rx_queue_t = spsc_cond<8 * NUM_RXQ_FRAMES, one_burst, true, true>;
 | 
			
		||||
using rx_queue_t = spsc_cond_timeout<4, one_burst, true, false>;
 | 
			
		||||
 | 
			
		||||
enum class SCH_STATE { SEARCHING, FOUND };
 | 
			
		||||
 | 
			
		||||
@@ -161,7 +219,21 @@ class time_keeper {
 | 
			
		||||
 | 
			
		||||
using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>;
 | 
			
		||||
 | 
			
		||||
struct ms_trx : public BASET {
 | 
			
		||||
// used to globally initialize the sched/hw information
 | 
			
		||||
struct sched_hw_info {
 | 
			
		||||
	int hw_cpus;
 | 
			
		||||
	sched_params::target hw_target;
 | 
			
		||||
 | 
			
		||||
	sched_hw_info()
 | 
			
		||||
	{
 | 
			
		||||
		hw_cpus = std::thread::hardware_concurrency();
 | 
			
		||||
		hw_target = hw_cpus > 4 ? sched_params::target::ODROID : sched_params::target::PI4;
 | 
			
		||||
		set_sched_target(hw_target);
 | 
			
		||||
		std::cerr << "scheduling for: " << (hw_cpus > 4 ? "odroid" : "pi4") << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ms_trx : public BASET, public sched_hw_info {
 | 
			
		||||
	using base = BASET;
 | 
			
		||||
	static dummylog dummy_log;
 | 
			
		||||
	unsigned int mTSC;
 | 
			
		||||
@@ -169,15 +241,12 @@ struct ms_trx : public BASET {
 | 
			
		||||
	int timing_advance;
 | 
			
		||||
	bool do_auto_gain;
 | 
			
		||||
 | 
			
		||||
	std::thread rx_task;
 | 
			
		||||
	std::thread tx_task;
 | 
			
		||||
	std::thread *calcrval_task;
 | 
			
		||||
	pthread_t lower_rx_task;
 | 
			
		||||
	pthread_t lower_tx_task;
 | 
			
		||||
 | 
			
		||||
	// provides bursts to upper rx thread
 | 
			
		||||
	rx_queue_t rxqueue;
 | 
			
		||||
#ifdef SYNCTHINGONLY
 | 
			
		||||
	ts_hitter_q_t ts_hitter_q;
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	blade_sample_type *first_sch_buf;
 | 
			
		||||
	blade_sample_type *burst_copy_buffer;
 | 
			
		||||
 | 
			
		||||
@@ -189,18 +258,18 @@ struct ms_trx : public BASET {
 | 
			
		||||
	int64_t first_sch_ts_start = -1;
 | 
			
		||||
 | 
			
		||||
	time_keeper timekeeper;
 | 
			
		||||
	single_thread_pool worker_thread; // uses base class sched target hw info
 | 
			
		||||
 | 
			
		||||
	void start();
 | 
			
		||||
	void start_lower_ms();
 | 
			
		||||
	std::atomic<bool> upper_is_ready;
 | 
			
		||||
	void set_upper_ready(bool is_ready);
 | 
			
		||||
 | 
			
		||||
	bool handle_sch_or_nb();
 | 
			
		||||
	bool handle_sch(bool first = false);
 | 
			
		||||
	bool decode_sch(float *bits, bool update_global_clock);
 | 
			
		||||
	bool decode_sch(char *bits, bool update_global_clock);
 | 
			
		||||
	SCH_STATE search_for_sch(dev_buf_t *rcd);
 | 
			
		||||
	void grab_bursts(dev_buf_t *rcd);
 | 
			
		||||
 | 
			
		||||
	int init_device();
 | 
			
		||||
	int init_dev_and_streams();
 | 
			
		||||
	void stop_threads();
 | 
			
		||||
	void *rx_cb(ms_trx *t);
 | 
			
		||||
@@ -208,8 +277,10 @@ struct ms_trx : public BASET {
 | 
			
		||||
	void maybe_update_gain(one_burst &brst);
 | 
			
		||||
 | 
			
		||||
	ms_trx()
 | 
			
		||||
		: timing_advance(0), do_auto_gain(false), rxqueue(), first_sch_buf(new blade_sample_type[SCH_LEN_SPS]),
 | 
			
		||||
		  burst_copy_buffer(new blade_sample_type[ONE_TS_BURST_LEN]), rcv_done{ false }, sch_thread_done{ false }
 | 
			
		||||
		: mTSC(0), mBSIC(0), timing_advance(0), do_auto_gain(false), rxqueue(),
 | 
			
		||||
		  first_sch_buf(new blade_sample_type[SCH_LEN_SPS]),
 | 
			
		||||
		  burst_copy_buffer(new blade_sample_type[ONE_TS_BURST_LEN]), first_sch_buf_rcv_ts(0),
 | 
			
		||||
		  rcv_done{ false }, sch_thread_done{ false }, upper_is_ready(false)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -227,34 +298,4 @@ struct ms_trx : public BASET {
 | 
			
		||||
		assert(val > -127 && val < 128);
 | 
			
		||||
		timing_advance = val * 4;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void set_name_aff_sched(const char *name, int cpunum, int schedtype, int prio)
 | 
			
		||||
	{
 | 
			
		||||
		set_name_aff_sched(pthread_self(), name, cpunum, schedtype, prio);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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);
 | 
			
		||||
 | 
			
		||||
		auto rv = pthread_setaffinity_np(h, 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(h, schedtype, &sch_params);
 | 
			
		||||
		if (rv < 0) {
 | 
			
		||||
			std::cerr << name << " sched: errreur! " << std::strerror(errno);
 | 
			
		||||
			return exit(0);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -1,207 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Eric Wild <ewild@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
#include "ms.h"
 | 
			
		||||
#include "sigProcLib.h"
 | 
			
		||||
#include "signalVector.h"
 | 
			
		||||
#include "grgsm_vitac/grgsm_vitac.h"
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include "sch.h"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if !defined(SYNCTHINGONLY) || !defined(NODAMNLOG)
 | 
			
		||||
#define DBGLG(...) ms_trx::dummy_log()
 | 
			
		||||
#else
 | 
			
		||||
#define DBGLG(...) std::cerr
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(SYNCTHINGONLY)
 | 
			
		||||
#define DBGLG2(...) ms_trx::dummy_log()
 | 
			
		||||
#else
 | 
			
		||||
#define DBGLG2(...) std::cerr
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
static bool decode_sch(float *bits, bool update_global_clock)
 | 
			
		||||
{
 | 
			
		||||
	struct sch_info sch;
 | 
			
		||||
	ubit_t info[GSM_SCH_INFO_LEN];
 | 
			
		||||
	sbit_t data[GSM_SCH_CODED_LEN];
 | 
			
		||||
 | 
			
		||||
	float_to_sbit(&bits[3], &data[0], 62, 39);
 | 
			
		||||
	float_to_sbit(&bits[106], &data[39], 62, 39);
 | 
			
		||||
 | 
			
		||||
	if (!gsm_sch_decode(info, data)) {
 | 
			
		||||
		gsm_sch_parse(info, &sch);
 | 
			
		||||
 | 
			
		||||
		DBGLG() << "SCH : Decoded values" << std::endl;
 | 
			
		||||
		DBGLG() << "    BSIC: " << sch.bsic << std::endl;
 | 
			
		||||
		DBGLG() << "    TSC: " << (sch.bsic & 0x7) << std::endl;
 | 
			
		||||
		DBGLG() << "    T1  : " << sch.t1 << std::endl;
 | 
			
		||||
		DBGLG() << "    T2  : " << sch.t2 << std::endl;
 | 
			
		||||
		DBGLG() << "    T3p : " << sch.t3p << std::endl;
 | 
			
		||||
		DBGLG() << "    FN  : " << gsm_sch_to_fn(&sch) << std::endl;
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void check_rcv_fn(GSM::Time t, bool first, unsigned int &lastfn, unsigned int &fnbm)
 | 
			
		||||
{
 | 
			
		||||
	if (first && t.TN() == 0) {
 | 
			
		||||
		lastfn = t.FN();
 | 
			
		||||
		fnbm = 1 << 0;
 | 
			
		||||
		first = false;
 | 
			
		||||
	}
 | 
			
		||||
	if (!first && t.FN() != (int)lastfn) {
 | 
			
		||||
		if (fnbm != 255)
 | 
			
		||||
			std::cerr << "rx " << lastfn << ":" << fnbm << " " << __builtin_popcount(fnbm) << std::endl;
 | 
			
		||||
		lastfn = t.FN();
 | 
			
		||||
		fnbm = 1 << t.TN();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fnbm |= 1 << t.TN();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void handle_it(one_burst &e, signalVector &burst, unsigned int tsc, int scale)
 | 
			
		||||
{
 | 
			
		||||
	std::fill(burst.begin(), burst.begin() + burst.size(), 0.0);
 | 
			
		||||
	const auto is_sch = gsm_sch_check_ts(e.gsmts.TN(), e.gsmts.FN());
 | 
			
		||||
	const auto is_fcch = gsm_fcch_check_ts(e.gsmts.TN(), e.gsmts.FN());
 | 
			
		||||
 | 
			
		||||
	if (is_fcch)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (is_sch) {
 | 
			
		||||
		char outbin[148];
 | 
			
		||||
		convert_and_scale_default<float, int16_t>(burst.begin(), e.burst, ONE_TS_BURST_LEN * 2);
 | 
			
		||||
		std::stringstream dbgout;
 | 
			
		||||
#if 0
 | 
			
		||||
		{
 | 
			
		||||
			struct estim_burst_params ebp;
 | 
			
		||||
			auto rv2 = detectSCHBurst(burst, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp);
 | 
			
		||||
			auto bits = demodAnyBurst(burst, SCH, 4, &ebp);
 | 
			
		||||
			// clamp_array(bits->begin(), 148, 1.5f);
 | 
			
		||||
			for (auto &i : *bits)
 | 
			
		||||
				i = (i > 0 ? 1 : -1);
 | 
			
		||||
 | 
			
		||||
			auto rv = decode_sch(bits->begin(), false);
 | 
			
		||||
			dbgout << "U DET@" << (rv2 ? "yes " : "   ") << "Timing offset     " << ebp.toa
 | 
			
		||||
			       << " symbols, DECODE: " << (rv ? "yes" : "---") << " ";
 | 
			
		||||
 | 
			
		||||
			delete bits;
 | 
			
		||||
		}
 | 
			
		||||
#endif
 | 
			
		||||
		{
 | 
			
		||||
			convert_and_scale<float, float>(burst.begin(), burst.begin(), ONE_TS_BURST_LEN * 2,
 | 
			
		||||
							1.f / float(scale));
 | 
			
		||||
 | 
			
		||||
			std::complex<float> channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
			auto ss = reinterpret_cast<std::complex<float> *>(burst.begin());
 | 
			
		||||
			int d_c0_burst_start = get_sch_chan_imp_resp(ss, &channel_imp_resp[0]);
 | 
			
		||||
			detect_burst(ss, &channel_imp_resp[0], d_c0_burst_start, outbin);
 | 
			
		||||
 | 
			
		||||
			SoftVector bits;
 | 
			
		||||
			bits.resize(148);
 | 
			
		||||
			for (int i = 0; i < 148; i++) {
 | 
			
		||||
				bits[i] = (!outbin[i]); // < 1 ? -1 : 1;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			auto rv = decode_sch(bits.begin(), false);
 | 
			
		||||
			dbgout << "U SCH@"
 | 
			
		||||
			       << " " << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << d_c0_burst_start
 | 
			
		||||
			       << " DECODE:" << (rv ? "yes" : "---") << std::endl;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		DBGLG() << dbgout.str();
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
#if 1
 | 
			
		||||
	convert_and_scale<float, int16_t>(burst.begin(), e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(scale));
 | 
			
		||||
	// std::cerr << "@" << tsc << " " << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << ebp.toa << " "
 | 
			
		||||
	// 	  << std::endl;
 | 
			
		||||
 | 
			
		||||
	char outbin[148];
 | 
			
		||||
	auto ss = reinterpret_cast<std::complex<float> *>(burst.begin());
 | 
			
		||||
	float ncmax, dcmax;
 | 
			
		||||
	std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR], chan_imp_resp2[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
	auto normal_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp[0], &ncmax, tsc);
 | 
			
		||||
	auto dummy_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp2[0], &dcmax, TS_DUMMY);
 | 
			
		||||
	auto is_nb = ncmax > dcmax;
 | 
			
		||||
 | 
			
		||||
	DBGLG() << " U " << (is_nb ? "NB" : "DB") << "@ o nb: " << normal_burst_start << " o db: " << dummy_burst_start
 | 
			
		||||
		<< std::endl;
 | 
			
		||||
 | 
			
		||||
	if (is_nb)
 | 
			
		||||
		detect_burst(ss, &chan_imp_resp[0], normal_burst_start, outbin);
 | 
			
		||||
	else
 | 
			
		||||
		detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
 | 
			
		||||
	;
 | 
			
		||||
#ifdef DBGXX
 | 
			
		||||
	// auto bits = SoftVector(148);
 | 
			
		||||
	// for (int i = 0; i < 148; i++)
 | 
			
		||||
	// 	(bits)[i] = outbin[i] < 1 ? -1 : 1;
 | 
			
		||||
#endif
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void rcv_bursts_test(rx_queue_t *q, unsigned int *tsc, int scale)
 | 
			
		||||
{
 | 
			
		||||
	static bool first = true;
 | 
			
		||||
	unsigned int lastfn = 0;
 | 
			
		||||
	unsigned int fnbm = 0;
 | 
			
		||||
	signalVector burst(ONE_TS_BURST_LEN, 100, 100);
 | 
			
		||||
 | 
			
		||||
	cpu_set_t cpuset;
 | 
			
		||||
 | 
			
		||||
	CPU_ZERO(&cpuset);
 | 
			
		||||
	CPU_SET(1, &cpuset);
 | 
			
		||||
 | 
			
		||||
	auto rv = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
 | 
			
		||||
	if (rv < 0) {
 | 
			
		||||
		std::cerr << "affinity: errreur! " << std::strerror(errno);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	int prio = sched_get_priority_max(SCHED_RR);
 | 
			
		||||
	struct sched_param param;
 | 
			
		||||
	param.sched_priority = prio;
 | 
			
		||||
	rv = sched_setscheduler(0, SCHED_RR, ¶m);
 | 
			
		||||
	if (rv < 0) {
 | 
			
		||||
		std::cerr << "scheduler: errreur! " << std::strerror(errno);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	while (1) {
 | 
			
		||||
		one_burst e;
 | 
			
		||||
		while (!q->spsc_pop(&e)) {
 | 
			
		||||
			q->spsc_prep_pop();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		check_rcv_fn(e.gsmts, first, lastfn, fnbm);
 | 
			
		||||
 | 
			
		||||
		handle_it(e, burst, *tsc, scale);
 | 
			
		||||
#ifdef DBGXX
 | 
			
		||||
		rv = detectSCHBurst(*burst, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp);
 | 
			
		||||
		if (rv > 0)
 | 
			
		||||
			std::cerr << "#" << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << ebp.toa << std::endl;
 | 
			
		||||
		sched_yield();
 | 
			
		||||
#endif
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -30,6 +30,8 @@
 | 
			
		||||
#include "ms.h"
 | 
			
		||||
#include "grgsm_vitac/grgsm_vitac.h"
 | 
			
		||||
 | 
			
		||||
#include "threadpool.h"
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include "sch.h"
 | 
			
		||||
}
 | 
			
		||||
@@ -38,28 +40,29 @@ extern "C" {
 | 
			
		||||
#undef LOG
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(SYNCTHINGONLY) //|| !defined(NODAMNLOG)
 | 
			
		||||
#if !defined(NODAMNLOG)
 | 
			
		||||
#define DBGLG(...) ms_trx::dummy_log()
 | 
			
		||||
#else
 | 
			
		||||
#define DBGLG(...) std::cerr
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(SYNCTHINGONLY) || !defined(NODAMNLOG)
 | 
			
		||||
#if !defined(NODAMNLOG)
 | 
			
		||||
#define DBGLG2(...) ms_trx::dummy_log()
 | 
			
		||||
#else
 | 
			
		||||
#define DBGLG2(...) std::cerr
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define PRINT_Q_OVERFLOW
 | 
			
		||||
bool ms_trx::decode_sch(float *bits, bool update_global_clock)
 | 
			
		||||
 | 
			
		||||
bool ms_trx::decode_sch(char *bits, bool update_global_clock)
 | 
			
		||||
{
 | 
			
		||||
	int fn;
 | 
			
		||||
	struct sch_info sch;
 | 
			
		||||
	ubit_t info[GSM_SCH_INFO_LEN];
 | 
			
		||||
	sbit_t data[GSM_SCH_CODED_LEN];
 | 
			
		||||
 | 
			
		||||
	float_to_sbit(&bits[3], &data[0], 1, 39);
 | 
			
		||||
	float_to_sbit(&bits[106], &data[39], 1, 39);
 | 
			
		||||
	memcpy(&data[0], &bits[3], 39);
 | 
			
		||||
	memcpy(&data[39], &bits[106], 39);
 | 
			
		||||
 | 
			
		||||
	if (!gsm_sch_decode(info, data)) {
 | 
			
		||||
		gsm_sch_parse(info, &sch);
 | 
			
		||||
@@ -87,18 +90,6 @@ bool ms_trx::decode_sch(float *bits, bool update_global_clock)
 | 
			
		||||
			// global_time_keeper.FN(fn);
 | 
			
		||||
			// global_time_keeper.TN(0);
 | 
			
		||||
		}
 | 
			
		||||
#ifdef SYNCTHINGONLY
 | 
			
		||||
		else {
 | 
			
		||||
			int t3 = sch.t3p * 10 + 1;
 | 
			
		||||
			if (t3 == 11) {
 | 
			
		||||
				// timeslot hitter attempt @ fn 21 in mf
 | 
			
		||||
				DBGLG2() << "sch @ " << t3 << std::endl;
 | 
			
		||||
				auto e = GSM::Time(fn, 0);
 | 
			
		||||
				e += 10;
 | 
			
		||||
				ts_hitter_q.spsc_push(&e);
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
@@ -113,11 +104,7 @@ void ms_trx::maybe_update_gain(one_burst &brst)
 | 
			
		||||
	const unsigned int rx_max_cutoff = (rxFullScale * 2) / 3;
 | 
			
		||||
	static int gain_check = 0;
 | 
			
		||||
	static float runmean = 0;
 | 
			
		||||
	float sum = 0;
 | 
			
		||||
	for (auto i : brst.burst)
 | 
			
		||||
		sum += abs(i.real()) + abs(i.imag());
 | 
			
		||||
	sum /= ONE_TS_BURST_LEN * 2;
 | 
			
		||||
 | 
			
		||||
	float sum = normed_abs_sum(&brst.burst[0], ONE_TS_BURST_LEN);
 | 
			
		||||
	runmean = gain_check ? (runmean * (gain_check + 2) - 1 + sum) / (gain_check + 2) : sum;
 | 
			
		||||
 | 
			
		||||
	if (gain_check == avgburst_num - 1) {
 | 
			
		||||
@@ -126,8 +113,11 @@ void ms_trx::maybe_update_gain(one_burst &brst)
 | 
			
		||||
		gainoffset = runmean < (rxFullScale / 2 ? 2 : 1);
 | 
			
		||||
		float newgain = runmean < rx_max_cutoff ? rxgain + gainoffset : rxgain - gainoffset;
 | 
			
		||||
		// FIXME: gian cutoff
 | 
			
		||||
		if (newgain != rxgain && newgain <= 60)
 | 
			
		||||
			std::thread([this, newgain] { setRxGain(newgain); }).detach();
 | 
			
		||||
		if (newgain != rxgain && newgain <= 60) {
 | 
			
		||||
			auto gain_fun = [this, newgain] { setRxGain(newgain); };
 | 
			
		||||
			worker_thread.add_task(gain_fun);
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		runmean = 0;
 | 
			
		||||
	}
 | 
			
		||||
	gain_check = (gain_check + 1) % avgburst_num;
 | 
			
		||||
@@ -150,14 +140,9 @@ bool ms_trx::handle_sch_or_nb()
 | 
			
		||||
		handle_sch(false);
 | 
			
		||||
		memcpy(brst.sch_bits, sch_demod_bits, sizeof(sch_demod_bits));
 | 
			
		||||
	}
 | 
			
		||||
#ifndef SYNCTHINGONLY
 | 
			
		||||
	if (upper_is_ready) { // this is blocking, so only submit if there is a reader - only if upper exists!
 | 
			
		||||
#endif
 | 
			
		||||
		while (!rxqueue.spsc_push(&brst))
 | 
			
		||||
			;
 | 
			
		||||
#ifndef SYNCTHINGONLY
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	while (upper_is_ready && !rxqueue.spsc_push(&brst))
 | 
			
		||||
		;
 | 
			
		||||
 | 
			
		||||
	if (do_auto_gain)
 | 
			
		||||
		maybe_update_gain(brst);
 | 
			
		||||
@@ -172,36 +157,40 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
 | 
			
		||||
	auto current_gsm_time = timekeeper.gsmtime();
 | 
			
		||||
	const auto buf_len = is_first_sch_acq ? SCH_LEN_SPS : ONE_TS_BURST_LEN;
 | 
			
		||||
	const auto which_in_buffer = is_first_sch_acq ? first_sch_buf : burst_copy_buffer;
 | 
			
		||||
	memset((void *)&sch_acq_buffer[0], 0, sizeof(sch_acq_buffer));
 | 
			
		||||
#if 1
 | 
			
		||||
	const auto which_out_buffer = is_first_sch_acq ? sch_acq_buffer : &sch_acq_buffer[40 * 2];
 | 
			
		||||
	const auto ss = reinterpret_cast<std::complex<float> *>(which_out_buffer);
 | 
			
		||||
	std::complex<float> channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
 | 
			
		||||
	int start;
 | 
			
		||||
	memset((void *)&sch_acq_buffer[0], 0, sizeof(sch_acq_buffer));
 | 
			
		||||
	convert_and_scale(which_out_buffer, which_in_buffer, buf_len * 2, 1.f / float(rxFullScale));
 | 
			
		||||
	if (is_first_sch_acq) {
 | 
			
		||||
		float max_corr = 0;
 | 
			
		||||
		convert_and_scale<float, int16_t>(which_out_buffer, which_in_buffer, buf_len * 2,
 | 
			
		||||
						  1.f / float(rxFullScale));
 | 
			
		||||
		start = get_sch_buffer_chan_imp_resp(ss, &channel_imp_resp[0], buf_len, &max_corr);
 | 
			
		||||
		detect_burst(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
 | 
			
		||||
	} else {
 | 
			
		||||
		convert_and_scale<float, int16_t>(which_out_buffer, which_in_buffer, buf_len * 2,
 | 
			
		||||
						  1.f / float(rxFullScale));
 | 
			
		||||
		start = get_sch_chan_imp_resp(ss, &channel_imp_resp[0]);
 | 
			
		||||
		start = start < 39 ? start : 39;
 | 
			
		||||
		start = start > -39 ? start : -39;
 | 
			
		||||
		detect_burst(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
 | 
			
		||||
	}
 | 
			
		||||
	detect_burst_nb(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
 | 
			
		||||
 | 
			
		||||
	SoftVector bitss(148);
 | 
			
		||||
	for (int i = 0; i < 148; i++) {
 | 
			
		||||
		bitss[i] = (sch_demod_bits[i]);
 | 
			
		||||
	}
 | 
			
		||||
	auto sch_decode_success = decode_sch(sch_demod_bits, is_first_sch_acq);
 | 
			
		||||
#if 0
 | 
			
		||||
	auto burst = new signalVector(buf_len, 50);
 | 
			
		||||
	const auto corr_type = is_first_sch_acq ? sch_detect_type::SCH_DETECT_BUFFER : sch_detect_type::SCH_DETECT_FULL;
 | 
			
		||||
	struct estim_burst_params ebp;
 | 
			
		||||
 | 
			
		||||
	auto sch_decode_success = decode_sch(bitss.begin(), is_first_sch_acq);
 | 
			
		||||
	// scale like uhd, +-2k -> +-32k
 | 
			
		||||
	convert_and_scale(burst->begin(), which_in_buffer, buf_len * 2, SAMPLE_SCALE_FACTOR);
 | 
			
		||||
 | 
			
		||||
	auto rv = detectSCHBurst(*burst, 4, 4, corr_type, &ebp);
 | 
			
		||||
 | 
			
		||||
	int howmuchdelay = ebp.toa * 4;
 | 
			
		||||
	std::cerr << "ooffs: " << howmuchdelay << " " << std::endl;
 | 
			
		||||
	std::cerr << "voffs: " << start << " " << sch_decode_success << std::endl;
 | 
			
		||||
#endif
 | 
			
		||||
	if (sch_decode_success) {
 | 
			
		||||
		const auto ts_offset_symb = 0;
 | 
			
		||||
		const auto ts_offset_symb = 4;
 | 
			
		||||
		if (is_first_sch_acq) {
 | 
			
		||||
			// update ts to first sample in sch buffer, to allow delay calc for current ts
 | 
			
		||||
			first_sch_ts_start = first_sch_buf_rcv_ts + start - (ts_offset_symb * 4) - 1;
 | 
			
		||||
@@ -216,35 +205,132 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
 | 
			
		||||
		DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << start << " " << current_gsm_time.FN()
 | 
			
		||||
			 << ":" << current_gsm_time.TN() << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
#else
 | 
			
		||||
	const auto ts_offset_symb = 4;
 | 
			
		||||
	auto burst = new signalVector(buf_len, 50);
 | 
			
		||||
	const auto corr_type = is_first_sch_acq ? sch_detect_type::SCH_DETECT_BUFFER : sch_detect_type::SCH_DETECT_FULL;
 | 
			
		||||
	struct estim_burst_params ebp;
 | 
			
		||||
 | 
			
		||||
	// scale like uhd, +-2k -> +-32k
 | 
			
		||||
	convert_and_scale(burst->begin(), which_in_buffer, buf_len * 2, SAMPLE_SCALE_FACTOR);
 | 
			
		||||
 | 
			
		||||
	auto rv = detectSCHBurst(*burst, 4, 4, corr_type, &ebp);
 | 
			
		||||
 | 
			
		||||
	int howmuchdelay = ebp.toa * 4;
 | 
			
		||||
 | 
			
		||||
	if (!rv) {
 | 
			
		||||
		delete burst;
 | 
			
		||||
		DBGLG() << "SCH : \x1B[31m detect fail \033[0m NOOOOOOOOOOOOOOOOOO toa:" << ebp.toa << " "
 | 
			
		||||
			<< current_gsm_time.FN() << ":" << current_gsm_time.TN() << std::endl;
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	SoftVector *bits;
 | 
			
		||||
	if (is_first_sch_acq) {
 | 
			
		||||
		// can't be legit with a buf size spanning _at least_ one SCH but delay that implies partial sch burst
 | 
			
		||||
		if (howmuchdelay < 0 || (buf_len - howmuchdelay) < ONE_TS_BURST_LEN) {
 | 
			
		||||
			delete burst;
 | 
			
		||||
			return false;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		struct estim_burst_params ebp2;
 | 
			
		||||
		// auto sch_chunk = new signalVector(ONE_TS_BURST_LEN, 50);
 | 
			
		||||
		// auto sch_chunk_start = sch_chunk->begin();
 | 
			
		||||
		// memcpy(sch_chunk_start, sch_buf_f.data() + howmuchdelay, sizeof(std::complex<float>) * ONE_TS_BURST_LEN);
 | 
			
		||||
 | 
			
		||||
		auto delay = delayVector(burst, NULL, -howmuchdelay);
 | 
			
		||||
 | 
			
		||||
		scaleVector(*delay, (complex)1.0 / ebp.amp);
 | 
			
		||||
 | 
			
		||||
		auto rv2 = detectSCHBurst(*delay, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp2);
 | 
			
		||||
		DBGLG() << "FIRST SCH : " << (rv2 ? "yes " : "   ") << "Timing offset     " << ebp2.toa << " symbols"
 | 
			
		||||
			<< std::endl;
 | 
			
		||||
 | 
			
		||||
		bits = demodAnyBurst(*delay, SCH, 4, &ebp2);
 | 
			
		||||
		delete delay;
 | 
			
		||||
	} else {
 | 
			
		||||
		bits = demodAnyBurst(*burst, SCH, 4, &ebp);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete burst;
 | 
			
		||||
 | 
			
		||||
	// clamp to +-1.5 because +-127 softbits scaled by 64 after -0.5 can be at most +-1.5
 | 
			
		||||
	clamp_array(bits->begin(), 148, 1.5f);
 | 
			
		||||
 | 
			
		||||
	float_to_sbit(&bits->begin()[0], (signed char *)&sch_demod_bits[0], 62, 148);
 | 
			
		||||
	// float_to_sbit(&bits->begin()[106], &data[39], 62, 39);
 | 
			
		||||
 | 
			
		||||
	if (decode_sch((char *)sch_demod_bits, is_first_sch_acq)) {
 | 
			
		||||
		auto current_gsm_time_updated = timekeeper.gsmtime();
 | 
			
		||||
		if (is_first_sch_acq) {
 | 
			
		||||
			// update ts to first sample in sch buffer, to allow delay calc for current ts
 | 
			
		||||
			first_sch_ts_start = first_sch_buf_rcv_ts + howmuchdelay - (ts_offset_symb * 4);
 | 
			
		||||
		} else {
 | 
			
		||||
			// continuous sch tracking, only update if off too much
 | 
			
		||||
			auto diff = [](float x, float y) { return x > y ? x - y : y - x; };
 | 
			
		||||
 | 
			
		||||
			auto d = diff(ebp.toa, ts_offset_symb);
 | 
			
		||||
			if (abs(d) > 0.3) {
 | 
			
		||||
				if (ebp.toa < ts_offset_symb)
 | 
			
		||||
					ebp.toa = d;
 | 
			
		||||
				else
 | 
			
		||||
					ebp.toa = -d;
 | 
			
		||||
				temp_ts_corr_offset += ebp.toa * 4;
 | 
			
		||||
 | 
			
		||||
				DBGLG() << "offs: " << ebp.toa << " " << temp_ts_corr_offset << std::endl;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		auto a = gsm_sch_check_fn(current_gsm_time_updated.FN() - 1);
 | 
			
		||||
		auto b = gsm_sch_check_fn(current_gsm_time_updated.FN());
 | 
			
		||||
		auto c = gsm_sch_check_fn(current_gsm_time_updated.FN() + 1);
 | 
			
		||||
		DBGLG() << "L SCH : Timing offset     " << rv << " " << ebp.toa << " " << a << b << c << "fn "
 | 
			
		||||
			<< current_gsm_time_updated.FN() << ":" << current_gsm_time_updated.TN() << std::endl;
 | 
			
		||||
 | 
			
		||||
		delete bits;
 | 
			
		||||
		return true;
 | 
			
		||||
	} else {
 | 
			
		||||
		DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << ebp.toa << " " << current_gsm_time.FN()
 | 
			
		||||
			 << ":" << current_gsm_time.TN() << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	delete bits;
 | 
			
		||||
#endif
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
accumulates a full big buffer consisting of 8*12 timeslots, then:
 | 
			
		||||
either
 | 
			
		||||
1) adjusts gain if necessary and starts over
 | 
			
		||||
2) searches and finds SCH and is done
 | 
			
		||||
*/
 | 
			
		||||
SCH_STATE ms_trx::search_for_sch(dev_buf_t *rcd)
 | 
			
		||||
{
 | 
			
		||||
	static unsigned int sch_pos = 0;
 | 
			
		||||
	auto to_copy = SCH_LEN_SPS - sch_pos;
 | 
			
		||||
 | 
			
		||||
	if (sch_thread_done)
 | 
			
		||||
		return SCH_STATE::FOUND;
 | 
			
		||||
 | 
			
		||||
	if (rcv_done)
 | 
			
		||||
		return SCH_STATE::SEARCHING;
 | 
			
		||||
 | 
			
		||||
	auto to_copy = SCH_LEN_SPS - sch_pos;
 | 
			
		||||
 | 
			
		||||
	if (SCH_LEN_SPS == to_copy) // first time
 | 
			
		||||
	if (sch_pos == 0) // keep first ts for time delta calc
 | 
			
		||||
		first_sch_buf_rcv_ts = rcd->get_first_ts();
 | 
			
		||||
 | 
			
		||||
	if (!to_copy) {
 | 
			
		||||
	if (to_copy) {
 | 
			
		||||
		auto spsmax = rcd->actual_samples_per_buffer();
 | 
			
		||||
		if (to_copy > (unsigned int)spsmax)
 | 
			
		||||
			sch_pos += rcd->readall(first_sch_buf + sch_pos);
 | 
			
		||||
		else
 | 
			
		||||
			sch_pos += rcd->read_n(first_sch_buf + sch_pos, 0, to_copy);
 | 
			
		||||
	} else { // (!to_copy)
 | 
			
		||||
		sch_pos = 0;
 | 
			
		||||
		rcv_done = true;
 | 
			
		||||
		std::thread([this] {
 | 
			
		||||
			set_name_aff_sched("sch_search", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5);
 | 
			
		||||
 | 
			
		||||
			auto ptr = reinterpret_cast<const int16_t *>(first_sch_buf);
 | 
			
		||||
		auto sch_search_fun = [this] {
 | 
			
		||||
			const auto target_val = rxFullScale / 8;
 | 
			
		||||
			float sum = 0;
 | 
			
		||||
			for (unsigned int i = 0; i < SCH_LEN_SPS * 2; i++)
 | 
			
		||||
				sum += std::abs(ptr[i]);
 | 
			
		||||
			sum /= SCH_LEN_SPS * 2;
 | 
			
		||||
			float sum = normed_abs_sum(first_sch_buf, SCH_LEN_SPS);
 | 
			
		||||
 | 
			
		||||
			//FIXME: arbitrary value, gain cutoff
 | 
			
		||||
			if (sum > target_val || rxgain >= 60) // enough ?
 | 
			
		||||
@@ -257,16 +343,9 @@ SCH_STATE ms_trx::search_for_sch(dev_buf_t *rcd)
 | 
			
		||||
 | 
			
		||||
			if (!sch_thread_done)
 | 
			
		||||
				rcv_done = false; // retry!
 | 
			
		||||
			return (bool)sch_thread_done;
 | 
			
		||||
		}).detach();
 | 
			
		||||
		};
 | 
			
		||||
		worker_thread.add_task(sch_search_fun);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto spsmax = rcd->actual_samples_per_buffer();
 | 
			
		||||
	if (to_copy > (unsigned int)spsmax)
 | 
			
		||||
		sch_pos += rcd->readall(first_sch_buf + sch_pos);
 | 
			
		||||
	else
 | 
			
		||||
		sch_pos += rcd->read_n(first_sch_buf + sch_pos, 0, to_copy);
 | 
			
		||||
 | 
			
		||||
	return SCH_STATE::SEARCHING;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										78
									
								
								Transceiver52M/ms/ms_trxcon_if.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								Transceiver52M/ms/ms_trxcon_if.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Eric Wild <ewild@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include "ms_trxcon_if.h"
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/bb/trxcon/trxcon.h>
 | 
			
		||||
#include <osmocom/bb/trxcon/l1ctl_server.h>
 | 
			
		||||
#include <osmocom/core/panic.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern tx_queue_t txq;
 | 
			
		||||
extern cmd_queue_t cmdq_to_phy;
 | 
			
		||||
extern cmdr_queue_t cmdq_from_phy;
 | 
			
		||||
extern std::atomic<bool> g_exit_flag;
 | 
			
		||||
// trxcon C call(back) if
 | 
			
		||||
extern "C" {
 | 
			
		||||
int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon_phyif_burst_req *br)
 | 
			
		||||
{
 | 
			
		||||
	if (br->burst_len == 0) // dummy/nope
 | 
			
		||||
		return 0;
 | 
			
		||||
	OSMO_ASSERT(br->burst != 0);
 | 
			
		||||
 | 
			
		||||
	internal_q_tx_buf b(br);
 | 
			
		||||
	if (!g_exit_flag)
 | 
			
		||||
		txq.spsc_push(&b);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon_phyif_cmd *cmd)
 | 
			
		||||
{
 | 
			
		||||
#ifdef TXDEBUG
 | 
			
		||||
	DBGLG() << "TOP C: " << cmd2str(cmd->type) << std::endl;
 | 
			
		||||
#endif
 | 
			
		||||
	if (!g_exit_flag)
 | 
			
		||||
		cmdq_to_phy.spsc_push(cmd);
 | 
			
		||||
	// q for resp polling happens in main loop
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void trxcon_phyif_close(void *phyif)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void trxcon_l1ctl_close(struct trxcon_inst *trxcon)
 | 
			
		||||
{
 | 
			
		||||
	/* Avoid use-after-free: both *fi and *trxcon are children of
 | 
			
		||||
	 * the L2IF (L1CTL connection), so we need to re-parent *fi
 | 
			
		||||
	 * to NULL before calling l1ctl_client_conn_close(). */
 | 
			
		||||
	talloc_steal(NULL, trxcon->fi);
 | 
			
		||||
	l1ctl_client_conn_close((struct l1ctl_client *)trxcon->l2if);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int trxcon_l1ctl_send(struct trxcon_inst *trxcon, struct msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct l1ctl_client *l1c = (struct l1ctl_client *)trxcon->l2if;
 | 
			
		||||
 | 
			
		||||
	return l1ctl_client_send(l1c, msg);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -1,6 +1,6 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Eric Wild <ewild@sysmocom.de>
 | 
			
		||||
@@ -21,5 +21,22 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include "ms.h"
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/bb/trxcon/phyif.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void rcv_bursts_test(rx_queue_t *q, unsigned int *tsc, int scale);
 | 
			
		||||
extern struct trxcon_inst *g_trxcon;
 | 
			
		||||
struct internal_q_tx_buf {
 | 
			
		||||
	trxcon_phyif_burst_req r;
 | 
			
		||||
	uint8_t buf[148];
 | 
			
		||||
	internal_q_tx_buf() = default;
 | 
			
		||||
	internal_q_tx_buf(const internal_q_tx_buf &) = delete;
 | 
			
		||||
	internal_q_tx_buf &operator=(const internal_q_tx_buf &) = default;
 | 
			
		||||
	internal_q_tx_buf(const struct trxcon_phyif_burst_req *br) : r(*br)
 | 
			
		||||
	{
 | 
			
		||||
		memcpy(buf, (void *)br->burst, br->burst_len);
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
using tx_queue_t = spsc_cond<8 * 1, internal_q_tx_buf, true, false>;
 | 
			
		||||
using cmd_queue_t = spsc_cond_timeout<8 * 1, trxcon_phyif_cmd, true, false>;
 | 
			
		||||
using cmdr_queue_t = spsc_cond<8 * 1, trxcon_phyif_rsp, false, false>;
 | 
			
		||||
@@ -19,6 +19,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <csignal>
 | 
			
		||||
#include "sigProcLib.h"
 | 
			
		||||
#include "ms.h"
 | 
			
		||||
#include <signalVector.h>
 | 
			
		||||
@@ -26,64 +27,36 @@
 | 
			
		||||
#include <radioInterface.h>
 | 
			
		||||
#include <grgsm_vitac/grgsm_vitac.h>
 | 
			
		||||
 | 
			
		||||
// #define TXDEBUG
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <stdio.h>
 | 
			
		||||
#include <stdlib.h>
 | 
			
		||||
#include <stdint.h>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <getopt.h>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <signal.h>
 | 
			
		||||
#include <errno.h>
 | 
			
		||||
#include <time.h>
 | 
			
		||||
#include <fenv.h>
 | 
			
		||||
 | 
			
		||||
#include "sch.h"
 | 
			
		||||
#include "convolve.h"
 | 
			
		||||
#include "convert.h"
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/application.h>
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/bb/trxcon/trxcon.h>
 | 
			
		||||
#include <osmocom/bb/trxcon/trxcon_fsm.h>
 | 
			
		||||
#include <osmocom/bb/trxcon/l1ctl_server.h>
 | 
			
		||||
 | 
			
		||||
extern void trxc_log_init(void *tallctx);
 | 
			
		||||
#ifdef LSANDEBUG
 | 
			
		||||
void __lsan_do_recoverable_leak_check();
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include "ms_trxcon_if.h"
 | 
			
		||||
#include "ms_upper.h"
 | 
			
		||||
 | 
			
		||||
namespace trxcon
 | 
			
		||||
{
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/fsm.h>
 | 
			
		||||
#include <osmocom/core/msgb.h>
 | 
			
		||||
#include <osmocom/core/talloc.h>
 | 
			
		||||
#include <osmocom/core/signal.h>
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include <osmocom/gsm/gsm_utils.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/core/logging.h>
 | 
			
		||||
#include <osmocom/bb/trxcon/logging.h>
 | 
			
		||||
 | 
			
		||||
#include <osmocom/bb/trxcon/trxcon.h>
 | 
			
		||||
#include <osmocom/bb/trxcon/trxcon_fsm.h>
 | 
			
		||||
#include <osmocom/bb/trxcon/phyif.h>
 | 
			
		||||
#include <osmocom/bb/trxcon/l1ctl_server.h>
 | 
			
		||||
}
 | 
			
		||||
struct trxcon_inst *g_trxcon;
 | 
			
		||||
// trx_instance *trxcon_instance; // local handle
 | 
			
		||||
struct internal_q_tx_buf {
 | 
			
		||||
	trxcon_phyif_burst_req r;
 | 
			
		||||
	uint8_t buf[148];
 | 
			
		||||
};
 | 
			
		||||
using tx_queue_t = spsc_cond<8 * 1, internal_q_tx_buf, true, false>;
 | 
			
		||||
using cmd_queue_t = spsc_cond<8 * 1, trxcon_phyif_cmd, true, false>;
 | 
			
		||||
using cmdr_queue_t = spsc_cond<8 * 1, trxcon_phyif_rsp, false, false>;
 | 
			
		||||
static tx_queue_t txq;
 | 
			
		||||
static cmd_queue_t cmdq_to_phy;
 | 
			
		||||
static cmdr_queue_t cmdq_from_phy;
 | 
			
		||||
#include "threadsched.h"
 | 
			
		||||
 | 
			
		||||
extern bool trxc_l1ctl_init(void *tallctx);
 | 
			
		||||
 | 
			
		||||
} // namespace trxcon
 | 
			
		||||
extern "C" void trxc_log_init(void *tallctx);
 | 
			
		||||
struct trxcon_inst *g_trxcon;
 | 
			
		||||
tx_queue_t txq;
 | 
			
		||||
cmd_queue_t cmdq_to_phy;
 | 
			
		||||
cmdr_queue_t cmdq_from_phy;
 | 
			
		||||
 | 
			
		||||
#ifdef LOG
 | 
			
		||||
#undef LOG
 | 
			
		||||
@@ -94,41 +67,62 @@ extern "C" void trxc_log_init(void *tallctx);
 | 
			
		||||
 | 
			
		||||
std::atomic<bool> g_exit_flag;
 | 
			
		||||
 | 
			
		||||
void upper_trx::stop_upper_threads()
 | 
			
		||||
{
 | 
			
		||||
	g_exit_flag = true;
 | 
			
		||||
 | 
			
		||||
	pthread_join(thr_control, NULL);
 | 
			
		||||
	pthread_join(thr_tx, NULL);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void upper_trx::start_threads()
 | 
			
		||||
{
 | 
			
		||||
	thr_control = std::thread([this] {
 | 
			
		||||
		set_name_aff_sched("upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR));
 | 
			
		||||
		while (!g_exit_flag) {
 | 
			
		||||
			driveControl();
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	msleep(1);
 | 
			
		||||
	thr_tx = std::thread([this] {
 | 
			
		||||
		set_name_aff_sched("upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1);
 | 
			
		||||
		while (!g_exit_flag) {
 | 
			
		||||
			driveTx();
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	DBGLG(...) << "spawning threads.." << std::endl;
 | 
			
		||||
 | 
			
		||||
	// atomic ensures data is not written to q until loop reads
 | 
			
		||||
	start_lower_ms();
 | 
			
		||||
	thr_control = spawn_worker_thread(
 | 
			
		||||
		sched_params::thread_names::U_CTL,
 | 
			
		||||
		[](void *args) -> void * {
 | 
			
		||||
			upper_trx *t = reinterpret_cast<upper_trx *>(args);
 | 
			
		||||
#ifdef TXDEBUG
 | 
			
		||||
			struct sched_param param;
 | 
			
		||||
			int policy;
 | 
			
		||||
			pthread_getschedparam(pthread_self(), &policy, ¶m);
 | 
			
		||||
			printf("ID: %lu, CPU: %d policy = %d priority = %d\n", pthread_self(), sched_getcpu(), policy,
 | 
			
		||||
			       param.sched_priority);
 | 
			
		||||
#endif
 | 
			
		||||
			std::cerr << "started U control!" << std::endl;
 | 
			
		||||
			while (!g_exit_flag) {
 | 
			
		||||
				t->driveControl();
 | 
			
		||||
			}
 | 
			
		||||
			std::cerr << "exit U control!" << std::endl;
 | 
			
		||||
 | 
			
		||||
	set_name_aff_sched("upper_rx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_RR) - 5);
 | 
			
		||||
	while (!g_exit_flag) {
 | 
			
		||||
		// set_upper_ready(true);
 | 
			
		||||
		driveReceiveFIFO();
 | 
			
		||||
		trxcon::osmo_select_main(1);
 | 
			
		||||
			return 0;
 | 
			
		||||
		},
 | 
			
		||||
		this);
 | 
			
		||||
	thr_tx = spawn_worker_thread(
 | 
			
		||||
		sched_params::thread_names::U_TX,
 | 
			
		||||
		[](void *args) -> void * {
 | 
			
		||||
			upper_trx *t = reinterpret_cast<upper_trx *>(args);
 | 
			
		||||
#ifdef TXDEBUG
 | 
			
		||||
			struct sched_param param;
 | 
			
		||||
			int policy;
 | 
			
		||||
			pthread_getschedparam(pthread_self(), &policy, ¶m);
 | 
			
		||||
			printf("ID: %lu, CPU: %d policy = %d priority = %d\n", pthread_self(), sched_getcpu(), policy,
 | 
			
		||||
			       param.sched_priority);
 | 
			
		||||
#endif
 | 
			
		||||
			std::cerr << "started U tx!" << std::endl;
 | 
			
		||||
			while (!g_exit_flag) {
 | 
			
		||||
				t->driveTx();
 | 
			
		||||
			}
 | 
			
		||||
			std::cerr << "exit U tx!" << std::endl;
 | 
			
		||||
 | 
			
		||||
		trxcon::trxcon_phyif_rsp r;
 | 
			
		||||
		if (trxcon::cmdq_from_phy.spsc_pop(&r)) {
 | 
			
		||||
			DBGLG() << "HAVE RESP:" << r.type << std::endl;
 | 
			
		||||
			trxcon_phyif_handle_rsp(trxcon::g_trxcon, &r);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
			return 0;
 | 
			
		||||
		},
 | 
			
		||||
		this);
 | 
			
		||||
 | 
			
		||||
#ifdef LSANDEBUG
 | 
			
		||||
	std::thread([this] {
 | 
			
		||||
		set_name_aff_sched("leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10);
 | 
			
		||||
		set_name_aff_sched(sched_params::thread_names::LEAKCHECK);
 | 
			
		||||
 | 
			
		||||
		while (1) {
 | 
			
		||||
			std::this_thread::sleep_for(std::chrono::seconds{ 5 });
 | 
			
		||||
@@ -138,9 +132,23 @@ void upper_trx::start_threads()
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void upper_trx::start_lower_ms()
 | 
			
		||||
void upper_trx::main_loop()
 | 
			
		||||
{
 | 
			
		||||
	ms_trx::start();
 | 
			
		||||
	set_name_aff_sched(sched_params::thread_names::U_RX);
 | 
			
		||||
	set_upper_ready(true);
 | 
			
		||||
	while (!g_exit_flag) {
 | 
			
		||||
		driveReceiveFIFO();
 | 
			
		||||
		osmo_select_main(1);
 | 
			
		||||
 | 
			
		||||
		trxcon_phyif_rsp r;
 | 
			
		||||
		if (cmdq_from_phy.spsc_pop(&r)) {
 | 
			
		||||
			DBGLG() << "HAVE RESP:" << r.type << std::endl;
 | 
			
		||||
			trxcon_phyif_handle_rsp(g_trxcon, &r);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	set_upper_ready(false);
 | 
			
		||||
	std::cerr << "exit U rx!" << std::endl;
 | 
			
		||||
	mOn = false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// signalvector is owning despite claiming not to, but we can pretend, too..
 | 
			
		||||
@@ -172,9 +180,9 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
 | 
			
		||||
	const auto is_sch = gsm_sch_check_ts(wTime.TN(), wTime.FN());
 | 
			
		||||
	const auto is_fcch = gsm_fcch_check_ts(wTime.TN(), wTime.FN());
 | 
			
		||||
 | 
			
		||||
	trxcon::trxcon_phyif_rtr_ind i = { static_cast<uint32_t>(wTime.FN()), static_cast<uint8_t>(wTime.TN()) };
 | 
			
		||||
	trxcon::trxcon_phyif_rtr_rsp r = {};
 | 
			
		||||
	trxcon_phyif_handle_rtr_ind(trxcon::g_trxcon, &i, &r);
 | 
			
		||||
	trxcon_phyif_rtr_ind i = { static_cast<uint32_t>(wTime.FN()), static_cast<uint8_t>(wTime.TN()) };
 | 
			
		||||
	trxcon_phyif_rtr_rsp r = {};
 | 
			
		||||
	trxcon_phyif_handle_rtr_ind(g_trxcon, &i, &r);
 | 
			
		||||
	if (!(r.flags & TRXCON_PHYIF_RTR_F_ACTIVE))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
@@ -191,7 +199,8 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
 | 
			
		||||
		return true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	convert_and_scale<float, int16_t>(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
 | 
			
		||||
#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) {
 | 
			
		||||
@@ -218,12 +227,48 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
 | 
			
		||||
		// fprintf(stderr, "%s %d\n", (is_nb ? "N":"D"), burst_time.FN());
 | 
			
		||||
		// if (is_nb)
 | 
			
		||||
#endif
 | 
			
		||||
		detect_burst(ss, &chan_imp_resp[0], normal_burst_start, demodded_softbits);
 | 
			
		||||
		detect_burst_nb(ss, &chan_imp_resp[0], normal_burst_start, demodded_softbits);
 | 
			
		||||
#ifdef DBGXX
 | 
			
		||||
		// else
 | 
			
		||||
		// 	detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
 | 
			
		||||
#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);
 | 
			
		||||
@@ -241,40 +286,40 @@ void upper_trx::driveReceiveFIFO()
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	if (pullRadioVector(burstTime, RSSI, TOA)) {
 | 
			
		||||
		trxcon::trxcon_phyif_burst_ind bi;
 | 
			
		||||
		trxcon_phyif_burst_ind bi;
 | 
			
		||||
		bi.fn = burstTime.FN();
 | 
			
		||||
		bi.tn = burstTime.TN();
 | 
			
		||||
		bi.rssi = RSSI;
 | 
			
		||||
		bi.toa256 = TOA;
 | 
			
		||||
		bi.burst = (sbit_t *)demodded_softbits;
 | 
			
		||||
		bi.burst_len = sizeof(demodded_softbits);
 | 
			
		||||
		trxcon_phyif_handle_burst_ind(trxcon::g_trxcon, &bi);
 | 
			
		||||
		trxcon_phyif_handle_burst_ind(g_trxcon, &bi);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	struct trxcon::trxcon_phyif_rts_ind rts {
 | 
			
		||||
	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(trxcon::g_trxcon, &rts);
 | 
			
		||||
	trxcon_phyif_handle_rts_ind(g_trxcon, &rts);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void upper_trx::driveTx()
 | 
			
		||||
{
 | 
			
		||||
	trxcon::internal_q_tx_buf e;
 | 
			
		||||
	internal_q_tx_buf e;
 | 
			
		||||
	static BitVector newBurst(sizeof(e.buf));
 | 
			
		||||
	while (!trxcon::txq.spsc_pop(&e)) {
 | 
			
		||||
		trxcon::txq.spsc_prep_pop();
 | 
			
		||||
	while (!txq.spsc_pop(&e)) {
 | 
			
		||||
		txq.spsc_prep_pop();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// ensure our tx cb is tickled and can exit
 | 
			
		||||
	if (g_exit_flag) {
 | 
			
		||||
		blade_sample_type dummy[10] = {};
 | 
			
		||||
		submit_burst_ts(dummy, 10, 1);
 | 
			
		||||
		submit_burst_ts(0, 1337, 1);
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	trxcon::internal_q_tx_buf *burst = &e;
 | 
			
		||||
	internal_q_tx_buf *burst = &e;
 | 
			
		||||
 | 
			
		||||
#ifdef TXDEBUG
 | 
			
		||||
#ifdef TXDEBUG2
 | 
			
		||||
	DBGLG() << "got burst!" << burst->r.fn << ":" << burst->ts << " current: " << timekeeper.gsmtime().FN()
 | 
			
		||||
		<< " dff: " << (int64_t)((int64_t)timekeeper.gsmtime().FN() - (int64_t)burst->r.fn) << std::endl;
 | 
			
		||||
#endif
 | 
			
		||||
@@ -292,10 +337,10 @@ void upper_trx::driveTx()
 | 
			
		||||
 | 
			
		||||
	// float -> int16
 | 
			
		||||
	blade_sample_type burst_buf[txburst->size()];
 | 
			
		||||
	convert_and_scale<int16_t, float>(burst_buf, txburst->begin(), txburst->size() * 2, 1);
 | 
			
		||||
#ifdef TXDEBUG
 | 
			
		||||
	convert_and_scale(burst_buf, txburst->begin(), txburst->size() * 2, 1);
 | 
			
		||||
#ifdef TXDEBUG2
 | 
			
		||||
	auto check = signalVector(txburst->size(), 40);
 | 
			
		||||
	convert_and_scale<float, int16_t, 1>(check.begin(), burst_buf, txburst->size() * 2);
 | 
			
		||||
	convert_and_scale(check.begin(), burst_buf, txburst->size() * 2, 1);
 | 
			
		||||
	estim_burst_params ebp;
 | 
			
		||||
	auto d = detectAnyBurst(check, 2, 4, 4, CorrType::RACH, 40, &ebp);
 | 
			
		||||
	if (d)
 | 
			
		||||
@@ -310,42 +355,44 @@ void upper_trx::driveTx()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#ifdef TXDEBUG
 | 
			
		||||
static const char *cmd2str(trxcon::trxcon_phyif_cmd_type c)
 | 
			
		||||
static const char *cmd2str(trxcon_phyif_cmd_type c)
 | 
			
		||||
{
 | 
			
		||||
	switch (c) {
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_RESET:
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_RESET:
 | 
			
		||||
		return "TRXCON_PHYIF_CMDT_RESET";
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_POWERON:
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_POWERON:
 | 
			
		||||
		return "TRXCON_PHYIF_CMDT_POWERON";
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_POWEROFF:
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_POWEROFF:
 | 
			
		||||
		return "TRXCON_PHYIF_CMDT_POWEROFF";
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_MEASURE:
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_MEASURE:
 | 
			
		||||
		return "TRXCON_PHYIF_CMDT_MEASURE";
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H0:
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_SETFREQ_H0:
 | 
			
		||||
		return "TRXCON_PHYIF_CMDT_SETFREQ_H0";
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H1:
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_SETFREQ_H1:
 | 
			
		||||
		return "TRXCON_PHYIF_CMDT_SETFREQ_H1";
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_SETSLOT:
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_SETSLOT:
 | 
			
		||||
		return "TRXCON_PHYIF_CMDT_SETSLOT";
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_SETTA:
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_SETTA:
 | 
			
		||||
		return "TRXCON_PHYIF_CMDT_SETTA";
 | 
			
		||||
	default:
 | 
			
		||||
		return "UNKNOWN COMMAND!";
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void print_cmd(trxcon::trxcon_phyif_cmd_type c)
 | 
			
		||||
static void print_cmd(trxcon_phyif_cmd_type c)
 | 
			
		||||
{
 | 
			
		||||
	DBGLG() << cmd2str(c) << std::endl;
 | 
			
		||||
	DBGLG() << "handling " << cmd2str(c) << std::endl;
 | 
			
		||||
}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
bool upper_trx::driveControl()
 | 
			
		||||
{
 | 
			
		||||
	trxcon::trxcon_phyif_rsp r;
 | 
			
		||||
	trxcon::trxcon_phyif_cmd cmd;
 | 
			
		||||
	while (!trxcon::cmdq_to_phy.spsc_pop(&cmd)) {
 | 
			
		||||
		trxcon::cmdq_to_phy.spsc_prep_pop();
 | 
			
		||||
	trxcon_phyif_rsp r;
 | 
			
		||||
	trxcon_phyif_cmd cmd;
 | 
			
		||||
	while (!cmdq_to_phy.spsc_pop(&cmd)) {
 | 
			
		||||
		cmdq_to_phy.spsc_prep_pop();
 | 
			
		||||
		if (g_exit_flag)
 | 
			
		||||
			return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (g_exit_flag)
 | 
			
		||||
@@ -356,103 +403,54 @@ bool upper_trx::driveControl()
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	switch (cmd.type) {
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_RESET:
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_RESET:
 | 
			
		||||
		set_ta(0);
 | 
			
		||||
		break;
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_POWERON:
 | 
			
		||||
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_POWERON:
 | 
			
		||||
		if (!mOn) {
 | 
			
		||||
			set_upper_ready(true);
 | 
			
		||||
			mOn = true;
 | 
			
		||||
			start_lower_ms();
 | 
			
		||||
		}
 | 
			
		||||
		break;
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_POWEROFF:
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_POWEROFF:
 | 
			
		||||
		break;
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_MEASURE:
 | 
			
		||||
		r.type = trxcon::trxcon_phyif_cmd_type::TRXCON_PHYIF_CMDT_MEASURE;
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_MEASURE:
 | 
			
		||||
		r.type = trxcon_phyif_cmd_type::TRXCON_PHYIF_CMDT_MEASURE;
 | 
			
		||||
		r.param.measure.band_arfcn = cmd.param.measure.band_arfcn;
 | 
			
		||||
		// FIXME: do we want to measure anything, considering the transceiver just syncs by.. syncing?
 | 
			
		||||
		r.param.measure.dbm = -80;
 | 
			
		||||
		tuneRx(trxcon::gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 0) * 1000 * 100);
 | 
			
		||||
		tuneTx(trxcon::gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 1) * 1000 * 100);
 | 
			
		||||
		trxcon::cmdq_from_phy.spsc_push(&r);
 | 
			
		||||
		tuneRx(gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 0) * 1000 * 100);
 | 
			
		||||
		tuneTx(gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 1) * 1000 * 100);
 | 
			
		||||
		cmdq_from_phy.spsc_push(&r);
 | 
			
		||||
		break;
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H0:
 | 
			
		||||
		tuneRx(trxcon::gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 0) * 1000 * 100);
 | 
			
		||||
		tuneTx(trxcon::gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 1) * 1000 * 100);
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_SETFREQ_H0:
 | 
			
		||||
		tuneRx(gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 0) * 1000 * 100);
 | 
			
		||||
		tuneTx(gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 1) * 1000 * 100);
 | 
			
		||||
		break;
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H1:
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_SETFREQ_H1:
 | 
			
		||||
		break;
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_SETSLOT:
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_SETSLOT:
 | 
			
		||||
		break;
 | 
			
		||||
	case trxcon::TRXCON_PHYIF_CMDT_SETTA:
 | 
			
		||||
	case TRXCON_PHYIF_CMDT_SETTA:
 | 
			
		||||
		set_ta(cmd.param.setta.ta);
 | 
			
		||||
		break;
 | 
			
		||||
	}
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// trxcon C call(back) if
 | 
			
		||||
extern "C" {
 | 
			
		||||
int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon::trxcon_phyif_burst_req *br)
 | 
			
		||||
{
 | 
			
		||||
	if (br->burst_len == 0) // dummy/nope
 | 
			
		||||
		return 0;
 | 
			
		||||
	OSMO_ASSERT(br->burst != 0);
 | 
			
		||||
 | 
			
		||||
	trxcon::internal_q_tx_buf b;
 | 
			
		||||
	b.r = *br;
 | 
			
		||||
	memcpy(b.buf, (void *)br->burst, br->burst_len);
 | 
			
		||||
 | 
			
		||||
	if (!g_exit_flag)
 | 
			
		||||
		trxcon::txq.spsc_push(&b);
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon::trxcon_phyif_cmd *cmd)
 | 
			
		||||
{
 | 
			
		||||
#ifdef TXDEBUG
 | 
			
		||||
	DBGLG() << "TOP C: " << cmd2str(cmd->type) << std::endl;
 | 
			
		||||
#endif
 | 
			
		||||
	if (!g_exit_flag)
 | 
			
		||||
		trxcon::cmdq_to_phy.spsc_push(cmd);
 | 
			
		||||
	// q for resp polling happens in main loop
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void trxcon_phyif_close(void *phyif)
 | 
			
		||||
{
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void trxcon_l1ctl_close(struct trxcon::trxcon_inst *trxcon)
 | 
			
		||||
{
 | 
			
		||||
	/* Avoid use-after-free: both *fi and *trxcon are children of
 | 
			
		||||
	 * the L2IF (L1CTL connection), so we need to re-parent *fi
 | 
			
		||||
	 * to NULL before calling l1ctl_client_conn_close(). */
 | 
			
		||||
	talloc_steal(NULL, trxcon->fi);
 | 
			
		||||
	trxcon::l1ctl_client_conn_close((struct trxcon::l1ctl_client *)trxcon->l2if);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int trxcon_l1ctl_send(struct trxcon::trxcon_inst *trxcon, struct trxcon::msgb *msg)
 | 
			
		||||
{
 | 
			
		||||
	struct trxcon::l1ctl_client *l1c = (struct trxcon::l1ctl_client *)trxcon->l2if;
 | 
			
		||||
 | 
			
		||||
	return trxcon::l1ctl_client_send(l1c, msg);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void sighandler(int sigset)
 | 
			
		||||
{
 | 
			
		||||
	// we might get a sigpipe in case the l1ctl ud socket disconnects because mobile quits
 | 
			
		||||
	if (sigset == SIGPIPE) {
 | 
			
		||||
	if (sigset == SIGPIPE || sigset == SIGINT) {
 | 
			
		||||
		g_exit_flag = true;
 | 
			
		||||
 | 
			
		||||
		// we know the flag is atomic and it prevents the trxcon cb handlers from writing
 | 
			
		||||
		// to the queues, so submit some trash to unblock the threads & exit
 | 
			
		||||
		trxcon::trxcon_phyif_cmd cmd = {};
 | 
			
		||||
		trxcon::internal_q_tx_buf b = {};
 | 
			
		||||
		trxcon::txq.spsc_push(&b);
 | 
			
		||||
		trxcon::cmdq_to_phy.spsc_push(&cmd);
 | 
			
		||||
		trxcon_phyif_cmd cmd = {};
 | 
			
		||||
		internal_q_tx_buf b = {};
 | 
			
		||||
		txq.spsc_push(&b);
 | 
			
		||||
		cmdq_to_phy.spsc_push(&cmd);
 | 
			
		||||
		msleep(200);
 | 
			
		||||
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
@@ -462,15 +460,26 @@ int main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	auto tall_trxcon_ctx = talloc_init("trxcon context");
 | 
			
		||||
	signal(SIGPIPE, sighandler);
 | 
			
		||||
	fesetround(FE_TOWARDZERO);
 | 
			
		||||
	signal(SIGINT, sighandler);
 | 
			
		||||
 | 
			
		||||
	trxcon::msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
 | 
			
		||||
	msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
 | 
			
		||||
	trxc_log_init(tall_trxcon_ctx);
 | 
			
		||||
 | 
			
		||||
	trxcon::g_trxcon = trxcon::trxcon_inst_alloc(tall_trxcon_ctx, 0, 0);
 | 
			
		||||
	trxcon::g_trxcon->gsmtap = nullptr;
 | 
			
		||||
	trxcon::g_trxcon->phyif = nullptr;
 | 
			
		||||
	trxcon::g_trxcon->phy_quirks.fbsb_extend_fns = 866; // 4 seconds, known to work.
 | 
			
		||||
	/* Configure pretty logging */
 | 
			
		||||
	log_set_print_extended_timestamp(osmo_stderr_target, 1);
 | 
			
		||||
	log_set_print_category_hex(osmo_stderr_target, 0);
 | 
			
		||||
	log_set_print_category(osmo_stderr_target, 1);
 | 
			
		||||
	log_set_print_level(osmo_stderr_target, 1);
 | 
			
		||||
 | 
			
		||||
	log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
 | 
			
		||||
	log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
 | 
			
		||||
 | 
			
		||||
	osmo_fsm_log_timeouts(true);
 | 
			
		||||
 | 
			
		||||
	g_trxcon = trxcon_inst_alloc(tall_trxcon_ctx, 0);
 | 
			
		||||
	g_trxcon->gsmtap = nullptr;
 | 
			
		||||
	g_trxcon->phyif = nullptr;
 | 
			
		||||
	g_trxcon->phy_quirks.fbsb_extend_fns = 866; // 4 seconds, known to work.
 | 
			
		||||
 | 
			
		||||
	convolve_init();
 | 
			
		||||
	convert_init();
 | 
			
		||||
@@ -482,15 +491,22 @@ int main(int argc, char *argv[])
 | 
			
		||||
	trx->do_auto_gain = true;
 | 
			
		||||
 | 
			
		||||
	status = trx->init_dev_and_streams();
 | 
			
		||||
	trx->set_name_aff_sched("main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5);
 | 
			
		||||
	if (status < 0) {
 | 
			
		||||
		std::cerr << "Error initializing hardware, quitting.." << std::endl;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
	set_name_aff_sched(sched_params::thread_names::MAIN);
 | 
			
		||||
 | 
			
		||||
	if (!trxcon::trxc_l1ctl_init(tall_trxcon_ctx)) {
 | 
			
		||||
	if (!trxc_l1ctl_init(tall_trxcon_ctx)) {
 | 
			
		||||
		std::cerr << "Error initializing l1ctl, quitting.." << std::endl;
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// blocking, will return when global exit is requested
 | 
			
		||||
	trx->start_threads();
 | 
			
		||||
	trx->main_loop();
 | 
			
		||||
	trx->stop_threads();
 | 
			
		||||
	trx->stop_upper_threads();
 | 
			
		||||
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
@@ -24,25 +24,25 @@
 | 
			
		||||
#include <arpa/inet.h>
 | 
			
		||||
 | 
			
		||||
#include "GSMCommon.h"
 | 
			
		||||
#include "radioClock.h"
 | 
			
		||||
#include "ms.h"
 | 
			
		||||
 | 
			
		||||
class upper_trx : public ms_trx {
 | 
			
		||||
	bool mOn;
 | 
			
		||||
	volatile bool mOn;
 | 
			
		||||
	char demodded_softbits[444];
 | 
			
		||||
 | 
			
		||||
	// void driveControl();
 | 
			
		||||
	bool pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
 | 
			
		||||
 | 
			
		||||
	pthread_t thr_control, thr_tx;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
	void start_threads();
 | 
			
		||||
	void main_loop();
 | 
			
		||||
	void stop_upper_threads();
 | 
			
		||||
 | 
			
		||||
	bool driveControl();
 | 
			
		||||
	void driveReceiveFIFO();
 | 
			
		||||
	void driveTx();
 | 
			
		||||
 | 
			
		||||
	bool pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
 | 
			
		||||
 | 
			
		||||
	std::thread thr_control, thr_rx, thr_tx;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
	void start_threads();
 | 
			
		||||
	void start_lower_ms();
 | 
			
		||||
 | 
			
		||||
	upper_trx(){};
 | 
			
		||||
	upper_trx() : mOn(false){};
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										95
									
								
								Transceiver52M/ms/threadpool.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										95
									
								
								Transceiver52M/ms/threadpool.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,95 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Eric Wild <ewild@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <vector>
 | 
			
		||||
#include <future>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <queue>
 | 
			
		||||
#include "threadsched.h"
 | 
			
		||||
 | 
			
		||||
struct single_thread_pool {
 | 
			
		||||
	std::mutex m;
 | 
			
		||||
	std::condition_variable cv;
 | 
			
		||||
	std::atomic<bool> stop_flag;
 | 
			
		||||
	std::atomic<bool> is_ready;
 | 
			
		||||
	std::deque<std::function<void()>> wq;
 | 
			
		||||
	pthread_t worker_thread;
 | 
			
		||||
 | 
			
		||||
	template <class F>
 | 
			
		||||
	void add_task(F &&f)
 | 
			
		||||
	{
 | 
			
		||||
		std::unique_lock<std::mutex> l(m);
 | 
			
		||||
		wq.emplace_back(std::forward<F>(f));
 | 
			
		||||
		cv.notify_one();
 | 
			
		||||
		return;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	single_thread_pool() : stop_flag(false), is_ready(false)
 | 
			
		||||
	{
 | 
			
		||||
		worker_thread = spawn_worker_thread(
 | 
			
		||||
			sched_params::thread_names::SCH_SEARCH,
 | 
			
		||||
			[](void *args) -> void * {
 | 
			
		||||
				using thist = decltype(this);
 | 
			
		||||
				thist t = reinterpret_cast<thist>(args);
 | 
			
		||||
				t->thread_loop();
 | 
			
		||||
				return 0;
 | 
			
		||||
			},
 | 
			
		||||
			this);
 | 
			
		||||
	}
 | 
			
		||||
	~single_thread_pool()
 | 
			
		||||
	{
 | 
			
		||||
		stop();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    private:
 | 
			
		||||
	void stop()
 | 
			
		||||
	{
 | 
			
		||||
		{
 | 
			
		||||
			std::unique_lock<std::mutex> l(m);
 | 
			
		||||
			wq.clear();
 | 
			
		||||
			stop_flag = true;
 | 
			
		||||
			cv.notify_one();
 | 
			
		||||
		}
 | 
			
		||||
		pthread_join(worker_thread, nullptr);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void thread_loop()
 | 
			
		||||
	{
 | 
			
		||||
		while (true) {
 | 
			
		||||
			is_ready = true;
 | 
			
		||||
			std::function<void()> f;
 | 
			
		||||
			{
 | 
			
		||||
				std::unique_lock<std::mutex> l(m);
 | 
			
		||||
				if (wq.empty()) {
 | 
			
		||||
					cv.wait(l, [&] { return !wq.empty() || stop_flag; });
 | 
			
		||||
				}
 | 
			
		||||
				if (stop_flag)
 | 
			
		||||
					return;
 | 
			
		||||
				is_ready = false;
 | 
			
		||||
				f = std::move(wq.front());
 | 
			
		||||
				wq.pop_front();
 | 
			
		||||
			}
 | 
			
		||||
			f();
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
							
								
								
									
										104
									
								
								Transceiver52M/ms/threadsched.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										104
									
								
								Transceiver52M/ms/threadsched.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,104 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Eric Wild <ewild@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <cerrno>
 | 
			
		||||
#include <cstdlib>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <thread>
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#include "threadsched.h"
 | 
			
		||||
 | 
			
		||||
sched_params::target scheduling_target;
 | 
			
		||||
 | 
			
		||||
void set_sched_target(sched_params::target t)
 | 
			
		||||
{
 | 
			
		||||
	scheduling_target = t;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_name_aff_sched(std::thread::native_handle_type h, const char *name, int cpunum, int schedtype, int prio)
 | 
			
		||||
{
 | 
			
		||||
	pthread_setname_np(h, name);
 | 
			
		||||
 | 
			
		||||
	cpu_set_t cpuset;
 | 
			
		||||
 | 
			
		||||
	CPU_ZERO(&cpuset);
 | 
			
		||||
	CPU_SET(cpunum, &cpuset);
 | 
			
		||||
 | 
			
		||||
	if (pthread_setaffinity_np(h, sizeof(cpuset), &cpuset) < 0) {
 | 
			
		||||
		std::cerr << name << " affinity: errreur! " << std::strerror(errno);
 | 
			
		||||
		return exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	sched_param sch_params;
 | 
			
		||||
	sch_params.sched_priority = prio;
 | 
			
		||||
	if (pthread_setschedparam(h, schedtype, &sch_params) < 0) {
 | 
			
		||||
		std::cerr << name << " sched: errreur! " << std::strerror(errno);
 | 
			
		||||
		return exit(0);
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static pthread_t do_spawn_thr(const char *name, int cpunum, int schedtype, int prio, worker_func_sig fun, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	pthread_t thread;
 | 
			
		||||
 | 
			
		||||
	pthread_attr_t attr;
 | 
			
		||||
	pthread_attr_init(&attr);
 | 
			
		||||
 | 
			
		||||
	sched_param sch_params;
 | 
			
		||||
	sch_params.sched_priority = prio;
 | 
			
		||||
	cpu_set_t cpuset;
 | 
			
		||||
	CPU_ZERO(&cpuset);
 | 
			
		||||
	CPU_SET(cpunum, &cpuset);
 | 
			
		||||
	auto a = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
 | 
			
		||||
	a |= pthread_attr_setschedpolicy(&attr, schedtype);
 | 
			
		||||
	a |= pthread_attr_setschedparam(&attr, &sch_params);
 | 
			
		||||
	a |= pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
 | 
			
		||||
	if (a)
 | 
			
		||||
		std::cerr << "thread arg rc:" << a << std::endl;
 | 
			
		||||
	pthread_create(&thread, &attr, fun, arg);
 | 
			
		||||
	pthread_setname_np(thread, name);
 | 
			
		||||
	pthread_attr_destroy(&attr);
 | 
			
		||||
	return thread;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_name_aff_sched(std::thread::native_handle_type h, sched_params::thread_names name)
 | 
			
		||||
{
 | 
			
		||||
	auto tgt = schdp[scheduling_target][name];
 | 
			
		||||
	// std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << std::endl;
 | 
			
		||||
	set_name_aff_sched(h, tgt.name, tgt.core, tgt.schedtype, tgt.prio);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void set_name_aff_sched(sched_params::thread_names name)
 | 
			
		||||
{
 | 
			
		||||
	set_name_aff_sched(pthread_self(), name);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
pthread_t spawn_worker_thread(sched_params::thread_names name, worker_func_sig fun, void *arg)
 | 
			
		||||
{
 | 
			
		||||
	auto tgt = schdp[scheduling_target][name];
 | 
			
		||||
	// std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << " prio:" << tgt.prio << std::endl;
 | 
			
		||||
	return do_spawn_thr(tgt.name, tgt.core, tgt.schedtype, tgt.prio, fun, arg);
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										68
									
								
								Transceiver52M/ms/threadsched.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								Transceiver52M/ms/threadsched.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,68 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Eric Wild <ewild@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <pthread.h>
 | 
			
		||||
#include <sched.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static struct sched_params {
 | 
			
		||||
	enum thread_names { U_CTL = 0, U_RX, U_TX, SCH_SEARCH, MAIN, LEAKCHECK, RXRUN, TXRUN, _THRD_NAME_COUNT };
 | 
			
		||||
	enum target { ODROID = 0, PI4 };
 | 
			
		||||
	const char *name;
 | 
			
		||||
	int core;
 | 
			
		||||
	int schedtype;
 | 
			
		||||
	int prio;
 | 
			
		||||
} schdp[][sched_params::_THRD_NAME_COUNT]{
 | 
			
		||||
	{
 | 
			
		||||
		{ "upper_ctrl", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) },
 | 
			
		||||
		{ "upper_rx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
 | 
			
		||||
		{ "upper_tx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 1 },
 | 
			
		||||
 | 
			
		||||
		{ "sch_search", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
 | 
			
		||||
		{ "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
 | 
			
		||||
		{ "leakcheck", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
 | 
			
		||||
 | 
			
		||||
		{ "rxrun", 4, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
 | 
			
		||||
		{ "txrun", 5, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
 | 
			
		||||
	},
 | 
			
		||||
	{
 | 
			
		||||
		{ "upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) },
 | 
			
		||||
		{ "upper_rx", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
 | 
			
		||||
		{ "upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
 | 
			
		||||
 | 
			
		||||
		{ "sch_search", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
 | 
			
		||||
		{ "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
 | 
			
		||||
		{ "leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
 | 
			
		||||
 | 
			
		||||
		{ "rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
 | 
			
		||||
		{ "txrun", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
 | 
			
		||||
	},
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
void set_sched_target(sched_params::target t);
 | 
			
		||||
 | 
			
		||||
using worker_func_sig = void *(*)(void *);
 | 
			
		||||
 | 
			
		||||
void set_name_aff_sched(sched_params::thread_names name);
 | 
			
		||||
 | 
			
		||||
pthread_t spawn_worker_thread(sched_params::thread_names name, worker_func_sig fun, void *arg);
 | 
			
		||||
@@ -65,7 +65,8 @@ struct uhd_buf_wrap {
 | 
			
		||||
using dev_buf_t = uhd_buf_wrap;
 | 
			
		||||
using bh_fn_t = std::function<int(dev_buf_t *)>;
 | 
			
		||||
 | 
			
		||||
template <typename T> struct uhd_hw {
 | 
			
		||||
template <typename T>
 | 
			
		||||
struct uhd_hw {
 | 
			
		||||
	uhd::usrp::multi_usrp::sptr dev;
 | 
			
		||||
	uhd::rx_streamer::sptr rx_stream;
 | 
			
		||||
	uhd::tx_streamer::sptr tx_stream;
 | 
			
		||||
@@ -76,13 +77,14 @@ template <typename T> struct uhd_hw {
 | 
			
		||||
	const unsigned int rxFullScale, txFullScale;
 | 
			
		||||
	const int rxtxdelay;
 | 
			
		||||
	float rxgain, txgain;
 | 
			
		||||
	static std::atomic<bool> stop_me_flag;
 | 
			
		||||
	static std::atomic<bool> stop_lower_threads_flag;
 | 
			
		||||
	double rxfreq_cache, txfreq_cache;
 | 
			
		||||
 | 
			
		||||
	virtual ~uhd_hw()
 | 
			
		||||
	{
 | 
			
		||||
		delete[] one_pkt_buf;
 | 
			
		||||
	}
 | 
			
		||||
	uhd_hw() : rxFullScale(32767), txFullScale(32767), rxtxdelay(-67)
 | 
			
		||||
	uhd_hw() : rxFullScale(32767), txFullScale(32767 * 0.3), rxtxdelay(-67), rxfreq_cache(0), txfreq_cache(0)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -92,15 +94,21 @@ template <typename T> struct uhd_hw {
 | 
			
		||||
 | 
			
		||||
	bool tuneTx(double freq, size_t chan = 0)
 | 
			
		||||
	{
 | 
			
		||||
		if (txfreq_cache == freq)
 | 
			
		||||
			return true;
 | 
			
		||||
		msleep(25);
 | 
			
		||||
		dev->set_tx_freq(freq, chan);
 | 
			
		||||
		txfreq_cache = freq;
 | 
			
		||||
		msleep(25);
 | 
			
		||||
		return true;
 | 
			
		||||
	};
 | 
			
		||||
	bool tuneRx(double freq, size_t chan = 0)
 | 
			
		||||
	{
 | 
			
		||||
		if (rxfreq_cache == freq)
 | 
			
		||||
			return true;
 | 
			
		||||
		msleep(25);
 | 
			
		||||
		dev->set_rx_freq(freq, chan);
 | 
			
		||||
		rxfreq_cache = freq;
 | 
			
		||||
		msleep(25);
 | 
			
		||||
		return true;
 | 
			
		||||
	};
 | 
			
		||||
@@ -133,6 +141,7 @@ template <typename T> struct uhd_hw {
 | 
			
		||||
	int init_device(bh_fn_t rxh, bh_fn_t txh)
 | 
			
		||||
	{
 | 
			
		||||
		auto const lock_delay_ms = 500;
 | 
			
		||||
		auto clock_lock_attempts = 15; // x lock_delay_ms
 | 
			
		||||
		auto const mcr = 26e6;
 | 
			
		||||
		auto const rate = (1625e3 / 6) * 4;
 | 
			
		||||
		auto const ref = "external";
 | 
			
		||||
@@ -157,8 +166,18 @@ template <typename T> struct uhd_hw {
 | 
			
		||||
		dev->set_tx_bandwidth(bw, channel);
 | 
			
		||||
 | 
			
		||||
		while (!(dev->get_rx_sensor("lo_locked", channel).to_bool() &&
 | 
			
		||||
			 dev->get_mboard_sensor("ref_locked").to_bool()))
 | 
			
		||||
			 dev->get_mboard_sensor("ref_locked").to_bool()) &&
 | 
			
		||||
		       clock_lock_attempts > 0) {
 | 
			
		||||
			std::cerr << "clock source lock attempts remaining: " << clock_lock_attempts << ".."
 | 
			
		||||
				  << std::endl;
 | 
			
		||||
			std::this_thread::sleep_for(std::chrono::milliseconds(lock_delay_ms));
 | 
			
		||||
			clock_lock_attempts--;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if (clock_lock_attempts <= 0) {
 | 
			
		||||
			std::cerr << "Error locking clock, gpsdo missing? quitting.." << std::endl;
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		uhd::stream_args_t stream_args("sc16", "sc16");
 | 
			
		||||
		rx_stream = dev->get_rx_stream(stream_args);
 | 
			
		||||
@@ -173,6 +192,11 @@ template <typename T> struct uhd_hw {
 | 
			
		||||
		return 0;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void actually_enable_streams()
 | 
			
		||||
	{
 | 
			
		||||
		// nop: stream cmd in handler
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void *rx_cb(bh_fn_t burst_handler)
 | 
			
		||||
	{
 | 
			
		||||
		void *ret = nullptr;
 | 
			
		||||
@@ -207,24 +231,29 @@ template <typename T> struct uhd_hw {
 | 
			
		||||
 | 
			
		||||
	auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
 | 
			
		||||
	{
 | 
			
		||||
		auto fn = [this, burst_handler] {
 | 
			
		||||
		// C cb -> ghetto closure capture, which is fine, the args never change.
 | 
			
		||||
		static auto rx_burst_cap_this = this;
 | 
			
		||||
		static auto rx_burst_cap_bh = burst_handler;
 | 
			
		||||
		auto fn = [](void *args) -> void * {
 | 
			
		||||
			pthread_setname_np(pthread_self(), "rxrun");
 | 
			
		||||
 | 
			
		||||
			uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
 | 
			
		||||
			stream_cmd.stream_now = true;
 | 
			
		||||
			stream_cmd.time_spec = uhd::time_spec_t();
 | 
			
		||||
			rx_stream->issue_stream_cmd(stream_cmd);
 | 
			
		||||
			rx_burst_cap_this->rx_stream->issue_stream_cmd(stream_cmd);
 | 
			
		||||
 | 
			
		||||
			while (!stop_me_flag) {
 | 
			
		||||
				rx_cb(burst_handler);
 | 
			
		||||
			while (!rx_burst_cap_this->stop_lower_threads_flag) {
 | 
			
		||||
				rx_burst_cap_this->rx_cb(rx_burst_cap_bh);
 | 
			
		||||
			}
 | 
			
		||||
			return 0;
 | 
			
		||||
		};
 | 
			
		||||
		return fn;
 | 
			
		||||
	}
 | 
			
		||||
	auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
 | 
			
		||||
	{
 | 
			
		||||
		auto fn = [] {
 | 
			
		||||
		auto fn = [](void *args) -> void * {
 | 
			
		||||
			// dummy
 | 
			
		||||
			return 0;
 | 
			
		||||
		};
 | 
			
		||||
		return fn;
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -77,6 +77,24 @@ static struct ctrl_handle *g_ctrlh;
 | 
			
		||||
static RadioDevice *usrp;
 | 
			
		||||
static RadioInterface *radio;
 | 
			
		||||
 | 
			
		||||
/* adjusts read timestamp offset to make the viterbi equalizer happy by including the start tail bits */
 | 
			
		||||
template <typename B>
 | 
			
		||||
class rif_va_wrapper : public B {
 | 
			
		||||
	bool use_va;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
	template <typename... Args>
 | 
			
		||||
	rif_va_wrapper(bool use_va, Args &&...args) : B(std::forward<Args>(args)...), use_va(use_va)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
	bool start() override
 | 
			
		||||
	{
 | 
			
		||||
		auto rv = B::start();
 | 
			
		||||
		B::readTimestamp -= use_va ? 20 : 0;
 | 
			
		||||
		return rv;
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
/* Create radio interface
 | 
			
		||||
 *     The interface consists of sample rate changes, frequency shifts,
 | 
			
		||||
 *     channel multiplexing, and other conversions. The transceiver core
 | 
			
		||||
@@ -91,17 +109,17 @@ RadioInterface *makeRadioInterface(struct trx_ctx *trx,
 | 
			
		||||
 | 
			
		||||
	switch (type) {
 | 
			
		||||
	case RadioDevice::NORMAL:
 | 
			
		||||
		radio = new RadioInterface(usrp, trx->cfg.tx_sps,
 | 
			
		||||
					   trx->cfg.rx_sps, trx->cfg.num_chans);
 | 
			
		||||
		radio = new rif_va_wrapper<RadioInterface>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps,
 | 
			
		||||
							   trx->cfg.num_chans);
 | 
			
		||||
		break;
 | 
			
		||||
	case RadioDevice::RESAMP_64M:
 | 
			
		||||
	case RadioDevice::RESAMP_100M:
 | 
			
		||||
		radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
 | 
			
		||||
						 trx->cfg.rx_sps);
 | 
			
		||||
		radio = new rif_va_wrapper<RadioInterfaceResamp>(trx->cfg.use_va, usrp, trx->cfg.tx_sps,
 | 
			
		||||
								 trx->cfg.rx_sps);
 | 
			
		||||
		break;
 | 
			
		||||
	case RadioDevice::MULTI_ARFCN:
 | 
			
		||||
		radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
 | 
			
		||||
						trx->cfg.rx_sps, trx->cfg.num_chans);
 | 
			
		||||
		radio = new rif_va_wrapper<RadioInterfaceMulti>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps,
 | 
			
		||||
								trx->cfg.num_chans);
 | 
			
		||||
		break;
 | 
			
		||||
	default:
 | 
			
		||||
		LOG(ALERT) << "Unsupported radio interface configuration";
 | 
			
		||||
@@ -465,6 +483,12 @@ int trx_validate_config(struct trx_ctx *trx)
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (trx->cfg.use_va &&
 | 
			
		||||
	    (trx->cfg.egprs || trx->cfg.multi_arfcn || trx->cfg.tx_sps != 4 || trx->cfg.rx_sps != 4)) {
 | 
			
		||||
		LOG(ERROR) << "Viterbi equalizer only works for gmsk with 4 tx/rx samples per symbol!";
 | 
			
		||||
		return -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -571,24 +595,14 @@ static void trx_stop()
 | 
			
		||||
static int trx_start(struct trx_ctx *trx)
 | 
			
		||||
{
 | 
			
		||||
	int type, chans;
 | 
			
		||||
	unsigned int i;
 | 
			
		||||
	std::vector<std::string> rx_paths, tx_paths;
 | 
			
		||||
	RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
 | 
			
		||||
 | 
			
		||||
	/* Create the low level device object */
 | 
			
		||||
	if (trx->cfg.multi_arfcn)
 | 
			
		||||
		iface = RadioDevice::MULTI_ARFCN;
 | 
			
		||||
 | 
			
		||||
	/* Generate vector of rx/tx_path: */
 | 
			
		||||
	for (i = 0; i < trx->cfg.num_chans; i++) {
 | 
			
		||||
		rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path));
 | 
			
		||||
		tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path));
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	usrp = RadioDevice::make(trx->cfg.tx_sps, trx->cfg.rx_sps, iface,
 | 
			
		||||
				 trx->cfg.num_chans, trx->cfg.offset,
 | 
			
		||||
				 tx_paths, rx_paths);
 | 
			
		||||
	type = usrp->open(charp2str(trx->cfg.dev_args), trx->cfg.clock_ref, trx->cfg.swap_channels);
 | 
			
		||||
	usrp = RadioDevice::make(iface, &trx->cfg);
 | 
			
		||||
	type = usrp->open();
 | 
			
		||||
	if (type < 0) {
 | 
			
		||||
		LOG(ALERT) << "Failed to create radio device" << std::endl;
 | 
			
		||||
		goto shutdown;
 | 
			
		||||
@@ -631,6 +645,7 @@ int main(int argc, char *argv[])
 | 
			
		||||
 | 
			
		||||
	osmo_init_logging2(tall_trx_ctx, &log_info);
 | 
			
		||||
	log_enable_multithread();
 | 
			
		||||
	log_cache_enable();
 | 
			
		||||
	osmo_stats_init(tall_trx_ctx);
 | 
			
		||||
	vty_init(&g_vty_info);
 | 
			
		||||
	logging_vty_add_cmds();
 | 
			
		||||
@@ -651,7 +666,7 @@ int main(int argc, char *argv[])
 | 
			
		||||
		exit(2);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX);
 | 
			
		||||
	rc = telnet_init_default(tall_trx_ctx, NULL, OSMO_VTY_PORT_TRX);
 | 
			
		||||
	if (rc < 0)
 | 
			
		||||
		exit(1);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -48,9 +48,8 @@ struct trxd_hdr_common {
 | 
			
		||||
		reserved:1,
 | 
			
		||||
		version:4;
 | 
			
		||||
#elif OSMO_IS_BIG_ENDIAN
 | 
			
		||||
	uint8_t version:4,
 | 
			
		||||
		reserved:1,
 | 
			
		||||
		tn:3;
 | 
			
		||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
 | 
			
		||||
	uint8_t version:4, reserved:1, tn:3;
 | 
			
		||||
#endif
 | 
			
		||||
	uint32_t fn; /* big endian */
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
@@ -86,9 +85,8 @@ struct trxd_hdr_v1_specific {
 | 
			
		||||
		modulation:4,
 | 
			
		||||
		idle:1;
 | 
			
		||||
#elif OSMO_IS_BIG_ENDIAN
 | 
			
		||||
	uint8_t idle:1,
 | 
			
		||||
		modulation:4,
 | 
			
		||||
		tsc:3;
 | 
			
		||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
 | 
			
		||||
	uint8_t idle:1, modulation:4, tsc:3;
 | 
			
		||||
#endif
 | 
			
		||||
	int16_t ci;  /* big endian, in centiBels */
 | 
			
		||||
} __attribute__ ((packed));
 | 
			
		||||
 
 | 
			
		||||
@@ -75,7 +75,7 @@ private:
 | 
			
		||||
public:
 | 
			
		||||
 | 
			
		||||
  /** start the interface */
 | 
			
		||||
  bool start();
 | 
			
		||||
  virtual bool start();
 | 
			
		||||
  bool stop();
 | 
			
		||||
 | 
			
		||||
  /** initialization */
 | 
			
		||||
@@ -151,7 +151,7 @@ private:
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
  RadioInterfaceResamp(RadioDevice* wDevice, size_t tx_sps, size_t rx_sps);
 | 
			
		||||
  ~RadioInterfaceResamp();
 | 
			
		||||
  virtual ~RadioInterfaceResamp();
 | 
			
		||||
 | 
			
		||||
  bool init(int type);
 | 
			
		||||
  void close();
 | 
			
		||||
@@ -184,7 +184,7 @@ private:
 | 
			
		||||
public:
 | 
			
		||||
  RadioInterfaceMulti(RadioDevice* radio, size_t tx_sps,
 | 
			
		||||
                      size_t rx_sps, size_t chans = 1);
 | 
			
		||||
  ~RadioInterfaceMulti();
 | 
			
		||||
  virtual ~RadioInterfaceMulti();
 | 
			
		||||
 | 
			
		||||
  bool init(int type);
 | 
			
		||||
  void close();
 | 
			
		||||
 
 | 
			
		||||
@@ -153,8 +153,6 @@ bool RadioInterfaceMulti::init(int type)
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	close();
 | 
			
		||||
 | 
			
		||||
	convertSendBuffer.resize(1);
 | 
			
		||||
	convertRecvBuffer.resize(1);
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -98,8 +98,6 @@ bool RadioInterfaceResamp::init(int type)
 | 
			
		||||
{
 | 
			
		||||
	float cutoff = 1.0f;
 | 
			
		||||
 | 
			
		||||
	close();
 | 
			
		||||
 | 
			
		||||
	switch (type) {
 | 
			
		||||
	case RadioDevice::RESAMP_64M:
 | 
			
		||||
		resamp_inrate = RESAMP_64M_INRATE;
 | 
			
		||||
 
 | 
			
		||||
@@ -129,6 +129,8 @@ struct PulseSequence {
 | 
			
		||||
static CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
 | 
			
		||||
static CorrelationSequence *gEdgeMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
 | 
			
		||||
static CorrelationSequence *gRACHSequences[] = {NULL,NULL,NULL};
 | 
			
		||||
static CorrelationSequence *gSCHSequence = NULL;
 | 
			
		||||
static CorrelationSequence *gDummySequence = NULL;
 | 
			
		||||
static PulseSequence *GSMPulse1 = NULL;
 | 
			
		||||
static PulseSequence *GSMPulse4 = NULL;
 | 
			
		||||
 | 
			
		||||
@@ -151,6 +153,12 @@ void sigProcLibDestroy()
 | 
			
		||||
    gRACHSequences[i] = NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  delete gSCHSequence;
 | 
			
		||||
  gSCHSequence = NULL;
 | 
			
		||||
 | 
			
		||||
  delete gDummySequence;
 | 
			
		||||
  gDummySequence = NULL;
 | 
			
		||||
 | 
			
		||||
  delete GMSKRotation1;
 | 
			
		||||
  delete GMSKReverseRotation1;
 | 
			
		||||
  delete GMSKRotation4;
 | 
			
		||||
@@ -315,6 +323,7 @@ static signalVector *convolve(const signalVector *x, const signalVector *h,
 | 
			
		||||
    append = true;
 | 
			
		||||
    break;
 | 
			
		||||
  case CUSTOM:
 | 
			
		||||
  // FIXME: x->getstart?
 | 
			
		||||
    if (start < h->size() - 1) {
 | 
			
		||||
      head = h->size() - start;
 | 
			
		||||
      append = true;
 | 
			
		||||
@@ -1289,6 +1298,77 @@ release:
 | 
			
		||||
  return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static bool generateDummyMidamble(int sps)
 | 
			
		||||
{
 | 
			
		||||
  bool status = true;
 | 
			
		||||
  float toa;
 | 
			
		||||
  complex *data = NULL;
 | 
			
		||||
  signalVector *autocorr = NULL, *midamble = NULL;
 | 
			
		||||
  signalVector *midMidamble = NULL, *_midMidamble = NULL;
 | 
			
		||||
 | 
			
		||||
  delete gDummySequence;
 | 
			
		||||
 | 
			
		||||
  /* Use middle 16 bits of each TSC. Correlation sequence is not pulse shaped */
 | 
			
		||||
  midMidamble = modulateBurst(gDummyBurstTSC.segment(5,16), 0, sps, true);
 | 
			
		||||
  if (!midMidamble)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  /* Simulated receive sequence is pulse shaped */
 | 
			
		||||
  midamble = modulateBurst(gDummyBurstTSC, 0, sps, false);
 | 
			
		||||
  if (!midamble) {
 | 
			
		||||
    status = false;
 | 
			
		||||
    goto release;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  // NOTE: Because ideal TSC 16-bit midamble is 66 symbols into burst,
 | 
			
		||||
  //       the ideal TSC has an + 180 degree phase shift,
 | 
			
		||||
  //       due to the pi/2 frequency shift, that
 | 
			
		||||
  //       needs to be accounted for.
 | 
			
		||||
  //       26-midamble is 61 symbols into burst, has +90 degree phase shift.
 | 
			
		||||
  scaleVector(*midMidamble, complex(-1.0, 0.0));
 | 
			
		||||
  scaleVector(*midamble, complex(0.0, 1.0));
 | 
			
		||||
 | 
			
		||||
  conjugateVector(*midMidamble);
 | 
			
		||||
 | 
			
		||||
  /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
 | 
			
		||||
  data = (complex *) convolve_h_alloc(midMidamble->size());
 | 
			
		||||
  _midMidamble = new signalVector(data, 0, midMidamble->size(), convolve_h_alloc, free);
 | 
			
		||||
  _midMidamble->setAligned(true);
 | 
			
		||||
  midMidamble->copyTo(*_midMidamble);
 | 
			
		||||
 | 
			
		||||
  autocorr = convolve(midamble, _midMidamble, NULL, NO_DELAY);
 | 
			
		||||
  if (!autocorr) {
 | 
			
		||||
    status = false;
 | 
			
		||||
    goto release;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  gDummySequence = new CorrelationSequence;
 | 
			
		||||
  gDummySequence->sequence = _midMidamble;
 | 
			
		||||
  gDummySequence->gain = peakDetect(*autocorr, &toa, NULL);
 | 
			
		||||
 | 
			
		||||
  /* For 1 sps only
 | 
			
		||||
   *     (Half of correlation length - 1) + midpoint of pulse shape + remainder
 | 
			
		||||
   *     13.5 = (16 / 2 - 1) + 1.5 + (26 - 10) / 2
 | 
			
		||||
   */
 | 
			
		||||
  if (sps == 1)
 | 
			
		||||
    gDummySequence->toa = toa - 13.5;
 | 
			
		||||
  else
 | 
			
		||||
    gDummySequence->toa = 0;
 | 
			
		||||
 | 
			
		||||
release:
 | 
			
		||||
  delete autocorr;
 | 
			
		||||
  delete midamble;
 | 
			
		||||
  delete midMidamble;
 | 
			
		||||
 | 
			
		||||
  if (!status) {
 | 
			
		||||
    delete _midMidamble;
 | 
			
		||||
    free(data);
 | 
			
		||||
    gDummySequence = NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static CorrelationSequence *generateEdgeMidamble(int tsc)
 | 
			
		||||
{
 | 
			
		||||
  complex *data = NULL;
 | 
			
		||||
@@ -1384,6 +1464,69 @@ release:
 | 
			
		||||
  return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool generateSCHSequence(int sps)
 | 
			
		||||
{
 | 
			
		||||
  bool status = true;
 | 
			
		||||
  float toa;
 | 
			
		||||
  complex *data = NULL;
 | 
			
		||||
  signalVector *autocorr = NULL;
 | 
			
		||||
  signalVector *seq0 = NULL, *seq1 = NULL, *_seq1 = NULL;
 | 
			
		||||
 | 
			
		||||
  delete gSCHSequence;
 | 
			
		||||
 | 
			
		||||
  seq0 = modulateBurst(gSCHSynchSequence, 0, sps, false);
 | 
			
		||||
  if (!seq0)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  seq1 = modulateBurst(gSCHSynchSequence, 0, sps, true);
 | 
			
		||||
  if (!seq1) {
 | 
			
		||||
    status = false;
 | 
			
		||||
    goto release;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  conjugateVector(*seq1);
 | 
			
		||||
 | 
			
		||||
  /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
 | 
			
		||||
  data = (complex *) convolve_h_alloc(seq1->size());
 | 
			
		||||
  _seq1 = new signalVector(data, 0, seq1->size(), convolve_h_alloc, free);
 | 
			
		||||
  _seq1->setAligned(true);
 | 
			
		||||
  seq1->copyTo(*_seq1);
 | 
			
		||||
 | 
			
		||||
  autocorr = convolve(seq0, _seq1, autocorr, NO_DELAY);
 | 
			
		||||
  if (!autocorr) {
 | 
			
		||||
    status = false;
 | 
			
		||||
    goto release;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  gSCHSequence = new CorrelationSequence;
 | 
			
		||||
  gSCHSequence->sequence = _seq1;
 | 
			
		||||
  gSCHSequence->buffer = data;
 | 
			
		||||
  gSCHSequence->gain = peakDetect(*autocorr, &toa, NULL);
 | 
			
		||||
 | 
			
		||||
  /* For 1 sps only
 | 
			
		||||
   *     (Half of correlation length - 1) + midpoint of pulse shaping filer
 | 
			
		||||
   *     20.5 = (64 / 2 - 1) + 1.5
 | 
			
		||||
   */
 | 
			
		||||
  if (sps == 1)
 | 
			
		||||
    gSCHSequence->toa = toa - 32.5;
 | 
			
		||||
  else
 | 
			
		||||
    gSCHSequence->toa = 0.0;
 | 
			
		||||
 | 
			
		||||
release:
 | 
			
		||||
  delete autocorr;
 | 
			
		||||
  delete seq0;
 | 
			
		||||
  delete seq1;
 | 
			
		||||
 | 
			
		||||
  if (!status) {
 | 
			
		||||
    delete _seq1;
 | 
			
		||||
    free(data);
 | 
			
		||||
    gSCHSequence = NULL;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Peak-to-average computation +/- range from peak in symbols
 | 
			
		||||
 */
 | 
			
		||||
@@ -1441,14 +1584,15 @@ float energyDetect(const signalVector &rxBurst, unsigned windowLength)
 | 
			
		||||
  return energy/windowLength;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static signalVector *downsampleBurst(const signalVector &burst)
 | 
			
		||||
static signalVector *downsampleBurst(const signalVector &burst, int in_len = DOWNSAMPLE_IN_LEN,
 | 
			
		||||
				     int out_len = DOWNSAMPLE_OUT_LEN)
 | 
			
		||||
{
 | 
			
		||||
  signalVector in(DOWNSAMPLE_IN_LEN, dnsampler->len());
 | 
			
		||||
  signalVector *out = new signalVector(DOWNSAMPLE_OUT_LEN);
 | 
			
		||||
  burst.copyToSegment(in, 0, DOWNSAMPLE_IN_LEN);
 | 
			
		||||
  signalVector in(in_len, dnsampler->len());
 | 
			
		||||
  // gSCHSequence->sequence->size(), ensure next conv has no realloc
 | 
			
		||||
  signalVector *out = new signalVector(out_len, 64);
 | 
			
		||||
  burst.copyToSegment(in, 0, in_len);
 | 
			
		||||
 | 
			
		||||
  if (dnsampler->rotate((float *) in.begin(), DOWNSAMPLE_IN_LEN,
 | 
			
		||||
                        (float *) out->begin(), DOWNSAMPLE_OUT_LEN) < 0) {
 | 
			
		||||
  if (dnsampler->rotate((float *)in.begin(), in_len, (float *)out->begin(), out_len) < 0) {
 | 
			
		||||
    delete out;
 | 
			
		||||
    out = NULL;
 | 
			
		||||
  }
 | 
			
		||||
@@ -1469,6 +1613,12 @@ static float computeCI(const signalVector *burst, const CorrelationSequence *syn
 | 
			
		||||
  /* Integer position where the sequence starts */
 | 
			
		||||
  const int ps = start + 1 - N + (int)roundf(toa);
 | 
			
		||||
 | 
			
		||||
  if(ps < 0) // might be -22 for toa 40 with N=64, if off by a lot during sch ms sync
 | 
			
		||||
    return 0;
 | 
			
		||||
 | 
			
		||||
  if (ps + N > (int)burst->size())
 | 
			
		||||
	  return 0;
 | 
			
		||||
 | 
			
		||||
  /* Estimate Signal power */
 | 
			
		||||
  S = 0.0f;
 | 
			
		||||
  for (int i=0, j=ps; i<(int)N; i++,j++)
 | 
			
		||||
@@ -1652,6 +1802,80 @@ static int detectRACHBurst(const signalVector &burst, float threshold, int sps,
 | 
			
		||||
  return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int detectSCHBurst(signalVector &burst,
 | 
			
		||||
		    float thresh,
 | 
			
		||||
		    int sps,
 | 
			
		||||
        sch_detect_type state, struct estim_burst_params *ebp)
 | 
			
		||||
{
 | 
			
		||||
  int rc, start, target, head, tail, len;
 | 
			
		||||
  complex _amp;
 | 
			
		||||
  CorrelationSequence *sync;
 | 
			
		||||
 | 
			
		||||
  if ((sps != 1) && (sps != 4))
 | 
			
		||||
    return -1;
 | 
			
		||||
 | 
			
		||||
  target = 3 + 39 + 64;
 | 
			
		||||
 | 
			
		||||
  switch (state) {
 | 
			
		||||
  case sch_detect_type::SCH_DETECT_NARROW:
 | 
			
		||||
    head = 4;
 | 
			
		||||
    tail = 4;
 | 
			
		||||
    break;
 | 
			
		||||
  case sch_detect_type::SCH_DETECT_BUFFER:
 | 
			
		||||
	  target = 1;
 | 
			
		||||
	  head = 0;
 | 
			
		||||
	  tail = (12 * 8 * 625) / 4; // 12 frames, downsampled /4 to 1 sps
 | 
			
		||||
	  break;
 | 
			
		||||
  case sch_detect_type::SCH_DETECT_FULL:
 | 
			
		||||
  default:
 | 
			
		||||
    head = target - 1;
 | 
			
		||||
    tail = 39 + 3 + 9;
 | 
			
		||||
    break;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  start = (target - head) * 1 - 1;
 | 
			
		||||
  len = (head + tail) * 1;
 | 
			
		||||
  sync = gSCHSequence;
 | 
			
		||||
  signalVector corr(len);
 | 
			
		||||
 | 
			
		||||
  signalVector *dec = downsampleBurst(burst, len * 4, len);
 | 
			
		||||
  rc = detectBurst(*dec, corr, sync, thresh, 1, start, len, ebp);
 | 
			
		||||
  delete dec;
 | 
			
		||||
 | 
			
		||||
  if (rc < 0) {
 | 
			
		||||
    return -1;
 | 
			
		||||
  } else if (!rc) {
 | 
			
		||||
      ebp->amp = 0.0f;
 | 
			
		||||
      ebp->toa = 0.0f;
 | 
			
		||||
    return 0;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  if (state == sch_detect_type::SCH_DETECT_BUFFER)
 | 
			
		||||
	  ebp->toa = ebp->toa - (3 + 39 + 64);
 | 
			
		||||
  else {
 | 
			
		||||
	  /* Subtract forward search bits from delay */
 | 
			
		||||
	  ebp->toa = ebp->toa - head;
 | 
			
		||||
  }
 | 
			
		||||
 | 
			
		||||
  return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static int detectDummyBurst(const signalVector &burst, float threshold,
 | 
			
		||||
                               int sps, unsigned max_toa, struct estim_burst_params *ebp)
 | 
			
		||||
{
 | 
			
		||||
  int rc, target, head, tail;
 | 
			
		||||
  CorrelationSequence *sync;
 | 
			
		||||
 | 
			
		||||
  target = 3 + 58 + 16 + 5;
 | 
			
		||||
  head = 10;
 | 
			
		||||
  tail = 6 + max_toa;
 | 
			
		||||
  sync = gDummySequence;
 | 
			
		||||
 | 
			
		||||
  ebp->tsc = 0;
 | 
			
		||||
  rc = detectGeneralBurst(burst, threshold, sps, target, head, tail, sync, ebp);
 | 
			
		||||
  return rc;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
 * Normal burst detection
 | 
			
		||||
 *
 | 
			
		||||
@@ -1670,7 +1894,7 @@ static int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float th
 | 
			
		||||
    return -SIGERR_UNSUPPORTED;
 | 
			
		||||
 | 
			
		||||
  target = 3 + 58 + 16 + 5;
 | 
			
		||||
  head = 6;
 | 
			
		||||
  head = 10;
 | 
			
		||||
  tail = 6 + max_toa;
 | 
			
		||||
  sync = gMidambles[tsc];
 | 
			
		||||
 | 
			
		||||
@@ -1719,6 +1943,9 @@ int detectAnyBurst(const signalVector &burst, unsigned tsc, float threshold,
 | 
			
		||||
  case RACH:
 | 
			
		||||
    rc = detectRACHBurst(burst, threshold, sps, max_toa, type == EXT_RACH, ebp);
 | 
			
		||||
    break;
 | 
			
		||||
  case IDLE:
 | 
			
		||||
    rc = detectDummyBurst(burst, threshold, sps, max_toa, ebp);
 | 
			
		||||
    break;
 | 
			
		||||
  default:
 | 
			
		||||
    LOG(ERR) << "Invalid correlation type";
 | 
			
		||||
  }
 | 
			
		||||
@@ -1921,6 +2148,9 @@ bool sigProcLibSetup()
 | 
			
		||||
  generateRACHSequence(&gRACHSequences[1], gRACHSynchSequenceTS1, 1);
 | 
			
		||||
  generateRACHSequence(&gRACHSequences[2], gRACHSynchSequenceTS2, 1);
 | 
			
		||||
 | 
			
		||||
  generateSCHSequence(1);
 | 
			
		||||
  generateDummyMidamble(1);
 | 
			
		||||
 | 
			
		||||
  for (int tsc = 0; tsc < 8; tsc++) {
 | 
			
		||||
    generateMidamble(1, tsc);
 | 
			
		||||
    gEdgeMidambles[tsc] = generateEdgeMidamble(tsc);
 | 
			
		||||
 
 | 
			
		||||
@@ -31,6 +31,7 @@ enum CorrType{
 | 
			
		||||
  TSC,         ///< timeslot should contain a normal burst
 | 
			
		||||
  EXT_RACH,    ///< timeslot should contain an extended access burst
 | 
			
		||||
  RACH,        ///< timeslot should contain an access burst
 | 
			
		||||
  SCH,
 | 
			
		||||
  EDGE,        ///< timeslot should contain an EDGE burst
 | 
			
		||||
  IDLE         ///< timeslot is an idle (or dummy) burst
 | 
			
		||||
};
 | 
			
		||||
@@ -93,6 +94,8 @@ signalVector *generateDummyBurst(int sps, int tn);
 | 
			
		||||
void scaleVector(signalVector &x,
 | 
			
		||||
                 complex scale);
 | 
			
		||||
 | 
			
		||||
signalVector *delayVector(const signalVector *in, signalVector *out, float delay);
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
        Rough energy estimator.
 | 
			
		||||
        @param rxBurst A GSM burst.
 | 
			
		||||
@@ -133,6 +136,17 @@ int detectAnyBurst(const signalVector &burst,
 | 
			
		||||
                   unsigned max_toa,
 | 
			
		||||
                   struct estim_burst_params *ebp);
 | 
			
		||||
 | 
			
		||||
enum class sch_detect_type {
 | 
			
		||||
	SCH_DETECT_FULL,
 | 
			
		||||
	SCH_DETECT_NARROW,
 | 
			
		||||
	SCH_DETECT_BUFFER,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
int detectSCHBurst(signalVector &rxBurst,
 | 
			
		||||
                    float detectThreshold,
 | 
			
		||||
                    int sps,
 | 
			
		||||
                    sch_detect_type state, struct estim_burst_params *ebp);
 | 
			
		||||
 | 
			
		||||
/** Demodulate burst basde on type and output soft bits */
 | 
			
		||||
SoftVector *demodAnyBurst(const signalVector &burst, CorrType type,
 | 
			
		||||
                          int sps, struct estim_burst_params *ebp);
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										18
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										18
									
								
								configure.ac
									
									
									
									
									
								
							@@ -82,10 +82,10 @@ AC_TYPE_SIZE_T
 | 
			
		||||
AC_HEADER_TIME
 | 
			
		||||
AC_C_BIGENDIAN
 | 
			
		||||
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.6.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.10.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.10.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.10.0)
 | 
			
		||||
PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.10.0)
 | 
			
		||||
 | 
			
		||||
AC_ARG_ENABLE(sanitize,
 | 
			
		||||
	[AS_HELP_STRING(
 | 
			
		||||
@@ -212,7 +212,12 @@ AS_IF([test "x$with_bladerf" = "xyes"], [
 | 
			
		||||
AC_MSG_CHECKING([whether to enable building MS TRX])
 | 
			
		||||
AS_IF([test "x$with_mstrx" = "xyes"], [
 | 
			
		||||
    AC_CONFIG_SUBDIRS([osmocom-bb/src/host/trxcon])
 | 
			
		||||
    AC_SUBST(LIBTRXCON_DIR, "osmocom-bb/src/host/trxcon")
 | 
			
		||||
    LIBTRXCON_DIR="osmocom-bb/src/host/trxcon"
 | 
			
		||||
    if ! test -d "$srcdir/$LIBTRXCON_DIR"; then
 | 
			
		||||
        AC_MSG_RESULT([no])
 | 
			
		||||
        AC_MSG_ERROR([$LIBTRXCON_DIR does not exist])
 | 
			
		||||
    fi
 | 
			
		||||
    AC_SUBST(LIBTRXCON_DIR)
 | 
			
		||||
    AC_MSG_RESULT([yes])
 | 
			
		||||
], [
 | 
			
		||||
    # Despite LIBTRXCON_DIR is added to SUBDIRS conditionally,
 | 
			
		||||
@@ -235,7 +240,6 @@ AS_IF([test "x$with_sse" != "xno"], [
 | 
			
		||||
    AM_CONDITIONAL(HAVE_SSE4_1, false)
 | 
			
		||||
])
 | 
			
		||||
 | 
			
		||||
dnl Check if the compiler supports specified GCC's built-in function
 | 
			
		||||
AC_DEFUN([CHECK_BUILTIN_SUPPORT], [
 | 
			
		||||
  AC_CACHE_CHECK(
 | 
			
		||||
    [whether ${CC} has $1 built-in],
 | 
			
		||||
@@ -369,11 +373,11 @@ AC_CONFIG_FILES([\
 | 
			
		||||
    tests/CommonLibs/Makefile \
 | 
			
		||||
    tests/Transceiver52M/Makefile \
 | 
			
		||||
    utils/Makefile \
 | 
			
		||||
    utils/va-test/Makefile \
 | 
			
		||||
    doc/Makefile \
 | 
			
		||||
    doc/examples/Makefile \
 | 
			
		||||
    contrib/Makefile \
 | 
			
		||||
    contrib/systemd/Makefile \
 | 
			
		||||
    doc/manuals/Makefile \
 | 
			
		||||
    contrib/osmo-trx.spec \
 | 
			
		||||
])
 | 
			
		||||
AC_OUTPUT
 | 
			
		||||
 
 | 
			
		||||
@@ -9,60 +9,19 @@
 | 
			
		||||
#
 | 
			
		||||
set -ex
 | 
			
		||||
 | 
			
		||||
substr() { [ -z "${2##*$1*}" ]; }
 | 
			
		||||
 | 
			
		||||
#apt-get install qemu qemu-user-static qemu-system-arm debootstrap fakeroot proot
 | 
			
		||||
mychroot_nocwd() {
 | 
			
		||||
        # LC_ALL + LANGUAGE set to avoid lots of print errors due to locale not being set inside container
 | 
			
		||||
        # PATH is needed to be able to reach binaries like ldconfig without logging in to root, which adds the paths to PATH.
 | 
			
		||||
        # PROOT_NO_SECCOMP is required due to proot bug #106
 | 
			
		||||
        LC_ALL=C LANGUAGE=C PATH="$PATH:/usr/sbin:/sbin" PROOT_NO_SECCOMP=1 proot -r "$ROOTFS" -w / -b /proc --root-id -q qemu-arm-static "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
mychroot() {
 | 
			
		||||
        mychroot_nocwd -w / "$@"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if [ -z "${INSIDE_CHROOT}" ]; then
 | 
			
		||||
 | 
			
		||||
        # Only use ARM chroot if host is not ARM and the target is ARM:
 | 
			
		||||
        if ! $(substr "arm" "$(uname -m)") && [ "x${INSTR}" = "x--with-neon" -o "x${INSTR}" = "x--with-neon-vfpv4" ]; then
 | 
			
		||||
 | 
			
		||||
                OSMOTRX_DIR="$PWD" # we assume we are called as contrib/jenkins.sh
 | 
			
		||||
                ROOTFS_PREFIX="${ROOTFS_PREFIX:-$HOME}"
 | 
			
		||||
                ROOTFS="${ROOTFS_PREFIX}/qemu-img"
 | 
			
		||||
                mkdir -p "${ROOTFS_PREFIX}"
 | 
			
		||||
 | 
			
		||||
                # Prepare chroot:
 | 
			
		||||
                if [ ! -d "$ROOTFS" ]; then
 | 
			
		||||
                        mkdir -p "$ROOTFS"
 | 
			
		||||
                        if [ "x${USE_DEBOOTSTRAP}" = "x1" ]; then
 | 
			
		||||
                                fakeroot qemu-debootstrap --foreign --include="linux-image-armmp-lpae" --arch=armhf stretch "$ROOTFS" http://ftp.de.debian.org/debian/
 | 
			
		||||
                                # Hack to avoid debootstrap trying to mount /proc, as it will fail with "no permissions" and anyway proot takes care of it:
 | 
			
		||||
                                sed -i "s/setup_proc//g" "$ROOTFS/debootstrap/suite-script"
 | 
			
		||||
                                mychroot /debootstrap/debootstrap --second-stage --verbose http://ftp.de.debian.org/debian/
 | 
			
		||||
                        else
 | 
			
		||||
                                YESTERDAY=$(python -c 'import datetime ; print((datetime.datetime.now() - datetime.timedelta(days=1)).strftime("%Y%m%d"))')
 | 
			
		||||
                                wget -nc -q "https://uk.images.linuxcontainers.org/images/debian/stretch/armhf/default/${YESTERDAY}_22:42/rootfs.tar.xz"
 | 
			
		||||
                                tar -xf rootfs.tar.xz -C "$ROOTFS/" || true
 | 
			
		||||
                                echo "nameserver 8.8.8.8" > "$ROOTFS/etc/resolv.conf"
 | 
			
		||||
                        fi
 | 
			
		||||
                        mychroot -b /dev apt-get update
 | 
			
		||||
                        mychroot apt-get -y install build-essential dh-autoreconf pkg-config libuhd-dev libusb-1.0-0-dev libusb-dev git libtalloc-dev libgnutls28-dev stow
 | 
			
		||||
                fi
 | 
			
		||||
                # Run jenkins.sh inside the chroot:
 | 
			
		||||
                INSIDE_CHROOT=1 mychroot_nocwd \
 | 
			
		||||
                 -w /osmo-trx \
 | 
			
		||||
                 -b "$OSMOTRX_DIR:/osmo-trx" \
 | 
			
		||||
                 -b "$(which osmo-clean-workspace.sh):/usr/bin/osmo-clean-workspace.sh" \
 | 
			
		||||
                 -b "$(which osmo-build-dep.sh):/usr/bin/osmo-build-dep.sh" \
 | 
			
		||||
                 -b "$(which osmo-deps.sh):/usr/bin/osmo-deps.sh" \
 | 
			
		||||
                  ./contrib/jenkins.sh
 | 
			
		||||
                exit 0
 | 
			
		||||
        fi
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
set -ex
 | 
			
		||||
case "$INSTR" in
 | 
			
		||||
	"--with-neon"*)
 | 
			
		||||
		case "$(arch)" in
 | 
			
		||||
			arm*)
 | 
			
		||||
				;;
 | 
			
		||||
			*)
 | 
			
		||||
				set +x
 | 
			
		||||
				echo "ERROR: trying to build with INSTR=$INSTR but not running on a 32-bit arm machine! (arch=$(arch))"
 | 
			
		||||
				exit 1
 | 
			
		||||
				;;
 | 
			
		||||
		esac
 | 
			
		||||
		;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
if ! [ -x "$(command -v osmo-build-dep.sh)" ]; then
 | 
			
		||||
	echo "Error: We need to have scripts/osmo-deps.sh from http://git.osmocom.org/osmo-ci/ in PATH !"
 | 
			
		||||
@@ -85,7 +44,17 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
 | 
			
		||||
export LD_LIBRARY_PATH="$inst/lib"
 | 
			
		||||
export PATH="$inst/bin:$PATH"
 | 
			
		||||
 | 
			
		||||
CONFIG="--enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms --with-ipc --with-mstrx $INSTR"
 | 
			
		||||
CONFIG="
 | 
			
		||||
	--enable-sanitize
 | 
			
		||||
	--enable-werror
 | 
			
		||||
	--with-bladerf
 | 
			
		||||
	--with-ipc
 | 
			
		||||
	--with-lms
 | 
			
		||||
	--with-mstrx
 | 
			
		||||
	--with-uhd
 | 
			
		||||
	--with-usrp1
 | 
			
		||||
	$INSTR
 | 
			
		||||
"
 | 
			
		||||
 | 
			
		||||
# Additional configure options and depends
 | 
			
		||||
if [ "$WITH_MANUALS" = "1" ]; then
 | 
			
		||||
@@ -109,7 +78,7 @@ $MAKE check \
 | 
			
		||||
  || cat-testlogs.sh
 | 
			
		||||
 | 
			
		||||
if arch | grep -v -q arm; then
 | 
			
		||||
	DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck \
 | 
			
		||||
	DISTCHECK_CONFIGURE_FLAGS="$(echo $CONFIG | tr -d '\n')" $MAKE $PARALLEL_MAKE distcheck \
 | 
			
		||||
	  || cat-testlogs.sh
 | 
			
		||||
fi
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,254 +0,0 @@
 | 
			
		||||
#
 | 
			
		||||
# spec file for package osmo-trx
 | 
			
		||||
#
 | 
			
		||||
# Copyright (c) 2017, Martin Hauke <mardnh@gmx.de>
 | 
			
		||||
#
 | 
			
		||||
# All modifications and additions to the file contributed by third parties
 | 
			
		||||
# remain the property of their copyright owners, unless otherwise agreed
 | 
			
		||||
# upon. The license for this file, and modifications and additions to the
 | 
			
		||||
# file, is the same license as for the pristine package itself (unless the
 | 
			
		||||
# license for the pristine package is not an Open Source License, in which
 | 
			
		||||
# case the license is the MIT License). An "Open Source License" is a
 | 
			
		||||
# license that conforms to the Open Source Definition (Version 1.9)
 | 
			
		||||
# published by the Open Source Initiative.
 | 
			
		||||
 | 
			
		||||
Name:           osmo-trx
 | 
			
		||||
Version:        @VERSION@
 | 
			
		||||
Release:        0
 | 
			
		||||
Summary:        SDR transceiver that implements Layer 1 of a GSM BTS
 | 
			
		||||
License:        AGPL-3.0-or-later
 | 
			
		||||
Group:          Productivity/Telephony/Servers
 | 
			
		||||
URL:            https://osmocom.org/projects/osmotrx
 | 
			
		||||
Source:         %{name}-%{version}.tar.xz
 | 
			
		||||
BuildRequires:  autoconf
 | 
			
		||||
BuildRequires:  automake
 | 
			
		||||
BuildRequires:  fdupes
 | 
			
		||||
BuildRequires:  gcc-c++
 | 
			
		||||
BuildRequires:  libtool
 | 
			
		||||
BuildRequires:  pkgconfig >= 0.20
 | 
			
		||||
%if 0%{?suse_version}
 | 
			
		||||
BuildRequires:  systemd-rpm-macros
 | 
			
		||||
%endif
 | 
			
		||||
%if ! 0%{?centos_ver}
 | 
			
		||||
BuildRequires:  pkgconfig(LimeSuite)
 | 
			
		||||
BuildRequires:  pkgconfig(usrp) >= 3.3
 | 
			
		||||
%endif
 | 
			
		||||
BuildRequires:  pkgconfig(fftw3f)
 | 
			
		||||
BuildRequires:  pkgconfig(libosmocoding) >= 1.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
 | 
			
		||||
@@ -1,13 +1,18 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Osmocom SDR BTS L1 Transceiver (IPC Backend)
 | 
			
		||||
After=network-online.target
 | 
			
		||||
Wants=network-online.target
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
Restart=always
 | 
			
		||||
StateDirectory=osmocom
 | 
			
		||||
WorkingDirectory=%S/osmocom
 | 
			
		||||
User=osmocom
 | 
			
		||||
Group=osmocom
 | 
			
		||||
ExecStart=/usr/bin/osmo-trx-ipc -C /etc/osmocom/osmo-trx-ipc.cfg
 | 
			
		||||
RestartSec=2
 | 
			
		||||
AmbientCapabilities=CAP_SYS_NICE
 | 
			
		||||
# CPU scheduling policy:
 | 
			
		||||
CPUSchedulingPolicy=rr
 | 
			
		||||
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,18 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Osmocom SDR BTS L1 Transceiver (LimeSuite backend)
 | 
			
		||||
After=network-online.target
 | 
			
		||||
Wants=network-online.target
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
Restart=always
 | 
			
		||||
StateDirectory=osmocom
 | 
			
		||||
WorkingDirectory=%S/osmocom
 | 
			
		||||
User=osmocom
 | 
			
		||||
Group=osmocom
 | 
			
		||||
ExecStart=/usr/bin/osmo-trx-lms -C /etc/osmocom/osmo-trx-lms.cfg
 | 
			
		||||
RestartSec=2
 | 
			
		||||
AmbientCapabilities=CAP_SYS_NICE
 | 
			
		||||
# CPU scheduling policy:
 | 
			
		||||
CPUSchedulingPolicy=rr
 | 
			
		||||
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,19 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Osmocom SDR BTS L1 Transceiver (UHD Backend)
 | 
			
		||||
After=network-online.target
 | 
			
		||||
Wants=network-online.target
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
Restart=always
 | 
			
		||||
StateDirectory=osmocom
 | 
			
		||||
WorkingDirectory=%S/osmocom
 | 
			
		||||
Environment=HOME=%S/osmocom
 | 
			
		||||
User=osmocom
 | 
			
		||||
Group=osmocom
 | 
			
		||||
ExecStart=/usr/bin/osmo-trx-uhd -C /etc/osmocom/osmo-trx-uhd.cfg
 | 
			
		||||
RestartSec=2
 | 
			
		||||
AmbientCapabilities=CAP_SYS_NICE
 | 
			
		||||
# CPU scheduling policy:
 | 
			
		||||
CPUSchedulingPolicy=rr
 | 
			
		||||
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
 | 
			
		||||
 
 | 
			
		||||
@@ -1,13 +1,18 @@
 | 
			
		||||
[Unit]
 | 
			
		||||
Description=Osmocom SDR BTS L1 Transceiver (libusrp backend)
 | 
			
		||||
After=network-online.target
 | 
			
		||||
Wants=network-online.target
 | 
			
		||||
 | 
			
		||||
[Service]
 | 
			
		||||
Type=simple
 | 
			
		||||
Restart=always
 | 
			
		||||
StateDirectory=osmocom
 | 
			
		||||
WorkingDirectory=%S/osmocom
 | 
			
		||||
User=osmocom
 | 
			
		||||
Group=osmocom
 | 
			
		||||
ExecStart=/usr/bin/osmo-trx-usrp1 -C /etc/osmocom/osmo-trx-usrp1.cfg
 | 
			
		||||
RestartSec=2
 | 
			
		||||
AmbientCapabilities=CAP_SYS_NICE
 | 
			
		||||
# CPU scheduling policy:
 | 
			
		||||
CPUSchedulingPolicy=rr
 | 
			
		||||
# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										161
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										161
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,164 @@
 | 
			
		||||
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 ]
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							@@ -1 +1 @@
 | 
			
		||||
9
 | 
			
		||||
10
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										26
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										26
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -2,7 +2,7 @@ Source: osmo-trx
 | 
			
		||||
Section: net
 | 
			
		||||
Priority: optional
 | 
			
		||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
 | 
			
		||||
Build-Depends: debhelper (>= 9),
 | 
			
		||||
Build-Depends: debhelper (>= 10),
 | 
			
		||||
               autotools-dev,
 | 
			
		||||
               autoconf-archive,
 | 
			
		||||
               pkg-config,
 | 
			
		||||
@@ -14,8 +14,9 @@ Build-Depends: debhelper (>= 9),
 | 
			
		||||
               libtalloc-dev,
 | 
			
		||||
               libusrp-dev,
 | 
			
		||||
               liblimesuite-dev,
 | 
			
		||||
               libosmocore-dev (>= 1.6.0),
 | 
			
		||||
               osmo-gsm-manuals-dev
 | 
			
		||||
               libbladerf-dev,
 | 
			
		||||
               libosmocore-dev (>= 1.10.0),
 | 
			
		||||
               osmo-gsm-manuals-dev (>= 1.6.0)
 | 
			
		||||
Standards-Version: 3.9.6
 | 
			
		||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
 | 
			
		||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
 | 
			
		||||
@@ -110,6 +111,25 @@ Description: SDR transceiver that implements Layer 1 of a GSM BTS (generic IPC)
 | 
			
		||||
 between different telecommunication associations for developing new
 | 
			
		||||
 generations of mobile phone networks. (post-2G/GSM)
 | 
			
		||||
 | 
			
		||||
Package: osmo-trx-ms-blade
 | 
			
		||||
Architecture: any
 | 
			
		||||
Depends: ${shlibs:Depends}, ${misc:Depends}
 | 
			
		||||
Description: MS side transceiver (bladeRF)
 | 
			
		||||
 OsmoTRX is a software-defined radio transceiver that implements the Layer 1
 | 
			
		||||
 physical layer of a BTS comprising the following 3GPP specifications:
 | 
			
		||||
 .
 | 
			
		||||
 TS 05.01 "Physical layer on the radio path"
 | 
			
		||||
 TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
 | 
			
		||||
 TS 05.04 "Modulation"
 | 
			
		||||
 TS 05.10 "Radio subsystem synchronization"
 | 
			
		||||
 .
 | 
			
		||||
 In this context, BTS is "Base transceiver station". It's the stations that
 | 
			
		||||
 connect mobile phones to the mobile network.
 | 
			
		||||
 .
 | 
			
		||||
 3GPP is the "3rd Generation Partnership Project" which is the collaboration
 | 
			
		||||
 between different telecommunication associations for developing new
 | 
			
		||||
 generations of mobile phone networks. (post-2G/GSM)
 | 
			
		||||
 | 
			
		||||
Package: osmo-trx-doc
 | 
			
		||||
Architecture: all
 | 
			
		||||
Section: doc
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										38
									
								
								debian/osmo-trx-ipc.postinst
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										38
									
								
								debian/osmo-trx-ipc.postinst
									
									
									
									
										vendored
									
									
										Executable file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
#!/bin/sh -e
 | 
			
		||||
case "$1" in
 | 
			
		||||
	configure)
 | 
			
		||||
		# Create the osmocom group and user (if it doesn't exist yet)
 | 
			
		||||
		if ! getent group osmocom >/dev/null; then
 | 
			
		||||
			groupadd --system osmocom
 | 
			
		||||
		fi
 | 
			
		||||
		if ! getent passwd osmocom >/dev/null; then
 | 
			
		||||
			useradd \
 | 
			
		||||
				--system \
 | 
			
		||||
				--gid osmocom \
 | 
			
		||||
				--home-dir /var/lib/osmocom \
 | 
			
		||||
				--shell /sbin/nologin \
 | 
			
		||||
				--comment "Open Source Mobile Communications" \
 | 
			
		||||
				osmocom
 | 
			
		||||
		fi
 | 
			
		||||
 | 
			
		||||
		# Fix permissions of previous (root-owned) install (OS#4107)
 | 
			
		||||
		if dpkg --compare-versions "$2" le "1.13.0"; then
 | 
			
		||||
			if [ -e /etc/osmocom/osmo-trx-ipc.cfg ]; then
 | 
			
		||||
				chown -v osmocom:osmocom /etc/osmocom/osmo-trx-ipc.cfg
 | 
			
		||||
				chmod -v 0660 /etc/osmocom/osmo-trx-ipc.cfg
 | 
			
		||||
			fi
 | 
			
		||||
 | 
			
		||||
			if [ -d /etc/osmocom ]; then
 | 
			
		||||
				chown -v root:osmocom /etc/osmocom
 | 
			
		||||
				chmod -v 2775 /etc/osmocom
 | 
			
		||||
			fi
 | 
			
		||||
 | 
			
		||||
			mkdir -p /var/lib/osmocom
 | 
			
		||||
			chown -R -v osmocom:osmocom /var/lib/osmocom
 | 
			
		||||
		fi
 | 
			
		||||
		;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
# dh_installdeb(1) will replace this with shell code automatically
 | 
			
		||||
# generated by other debhelper scripts.
 | 
			
		||||
#DEBHELPER#
 | 
			
		||||
							
								
								
									
										38
									
								
								debian/osmo-trx-lms.postinst
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										38
									
								
								debian/osmo-trx-lms.postinst
									
									
									
									
										vendored
									
									
										Executable file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
#!/bin/sh -e
 | 
			
		||||
case "$1" in
 | 
			
		||||
	configure)
 | 
			
		||||
		# Create the osmocom group and user (if it doesn't exist yet)
 | 
			
		||||
		if ! getent group osmocom >/dev/null; then
 | 
			
		||||
			groupadd --system osmocom
 | 
			
		||||
		fi
 | 
			
		||||
		if ! getent passwd osmocom >/dev/null; then
 | 
			
		||||
			useradd \
 | 
			
		||||
				--system \
 | 
			
		||||
				--gid osmocom \
 | 
			
		||||
				--home-dir /var/lib/osmocom \
 | 
			
		||||
				--shell /sbin/nologin \
 | 
			
		||||
				--comment "Open Source Mobile Communications" \
 | 
			
		||||
				osmocom
 | 
			
		||||
		fi
 | 
			
		||||
 | 
			
		||||
		# Fix permissions of previous (root-owned) install (OS#4107)
 | 
			
		||||
		if dpkg --compare-versions "$2" le "1.13.0"; then
 | 
			
		||||
			if [ -e /etc/osmocom/osmo-trx-lms.cfg ]; then
 | 
			
		||||
				chown -v osmocom:osmocom /etc/osmocom/osmo-trx-lms.cfg
 | 
			
		||||
				chmod -v 0660 /etc/osmocom/osmo-trx-lms.cfg
 | 
			
		||||
			fi
 | 
			
		||||
 | 
			
		||||
			if [ -d /etc/osmocom ]; then
 | 
			
		||||
				chown -v root:osmocom /etc/osmocom
 | 
			
		||||
				chmod -v 2775 /etc/osmocom
 | 
			
		||||
			fi
 | 
			
		||||
 | 
			
		||||
			mkdir -p /var/lib/osmocom
 | 
			
		||||
			chown -R -v osmocom:osmocom /var/lib/osmocom
 | 
			
		||||
		fi
 | 
			
		||||
		;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
# dh_installdeb(1) will replace this with shell code automatically
 | 
			
		||||
# generated by other debhelper scripts.
 | 
			
		||||
#DEBHELPER#
 | 
			
		||||
							
								
								
									
										1
									
								
								debian/osmo-trx-ms-blade.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-trx-ms-blade.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1 @@
 | 
			
		||||
/usr/bin/osmo-trx-ms-blade
 | 
			
		||||
							
								
								
									
										38
									
								
								debian/osmo-trx-uhd.postinst
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										38
									
								
								debian/osmo-trx-uhd.postinst
									
									
									
									
										vendored
									
									
										Executable file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
#!/bin/sh -e
 | 
			
		||||
case "$1" in
 | 
			
		||||
	configure)
 | 
			
		||||
		# Create the osmocom group and user (if it doesn't exist yet)
 | 
			
		||||
		if ! getent group osmocom >/dev/null; then
 | 
			
		||||
			groupadd --system osmocom
 | 
			
		||||
		fi
 | 
			
		||||
		if ! getent passwd osmocom >/dev/null; then
 | 
			
		||||
			useradd \
 | 
			
		||||
				--system \
 | 
			
		||||
				--gid osmocom \
 | 
			
		||||
				--home-dir /var/lib/osmocom \
 | 
			
		||||
				--shell /sbin/nologin \
 | 
			
		||||
				--comment "Open Source Mobile Communications" \
 | 
			
		||||
				osmocom
 | 
			
		||||
		fi
 | 
			
		||||
 | 
			
		||||
		# Fix permissions of previous (root-owned) install (OS#4107)
 | 
			
		||||
		if dpkg --compare-versions "$2" le "1.13.0"; then
 | 
			
		||||
			if [ -e /etc/osmocom/osmo-trx-uhd.cfg ]; then
 | 
			
		||||
				chown -v osmocom:osmocom /etc/osmocom/osmo-trx-uhd.cfg
 | 
			
		||||
				chmod -v 0660 /etc/osmocom/osmo-trx-uhd.cfg
 | 
			
		||||
			fi
 | 
			
		||||
 | 
			
		||||
			if [ -d /etc/osmocom ]; then
 | 
			
		||||
				chown -v root:osmocom /etc/osmocom
 | 
			
		||||
				chmod -v 2775 /etc/osmocom
 | 
			
		||||
			fi
 | 
			
		||||
 | 
			
		||||
			mkdir -p /var/lib/osmocom
 | 
			
		||||
			chown -R -v osmocom:osmocom /var/lib/osmocom
 | 
			
		||||
		fi
 | 
			
		||||
		;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
# dh_installdeb(1) will replace this with shell code automatically
 | 
			
		||||
# generated by other debhelper scripts.
 | 
			
		||||
#DEBHELPER#
 | 
			
		||||
							
								
								
									
										38
									
								
								debian/osmo-trx-usrp1.postinst
									
									
									
									
										vendored
									
									
										Executable file
									
								
							
							
						
						
									
										38
									
								
								debian/osmo-trx-usrp1.postinst
									
									
									
									
										vendored
									
									
										Executable file
									
								
							@@ -0,0 +1,38 @@
 | 
			
		||||
#!/bin/sh -e
 | 
			
		||||
case "$1" in
 | 
			
		||||
	configure)
 | 
			
		||||
		# Create the osmocom group and user (if it doesn't exist yet)
 | 
			
		||||
		if ! getent group osmocom >/dev/null; then
 | 
			
		||||
			groupadd --system osmocom
 | 
			
		||||
		fi
 | 
			
		||||
		if ! getent passwd osmocom >/dev/null; then
 | 
			
		||||
			useradd \
 | 
			
		||||
				--system \
 | 
			
		||||
				--gid osmocom \
 | 
			
		||||
				--home-dir /var/lib/osmocom \
 | 
			
		||||
				--shell /sbin/nologin \
 | 
			
		||||
				--comment "Open Source Mobile Communications" \
 | 
			
		||||
				osmocom
 | 
			
		||||
		fi
 | 
			
		||||
 | 
			
		||||
		# Fix permissions of previous (root-owned) install (OS#4107)
 | 
			
		||||
		if dpkg --compare-versions "$2" le "1.13.0"; then
 | 
			
		||||
			if [ -e /etc/osmocom/osmo-trx-usrp1.cfg ]; then
 | 
			
		||||
				chown -v osmocom:osmocom /etc/osmocom/osmo-trx-usrp1.cfg
 | 
			
		||||
				chmod -v 0660 /etc/osmocom/osmo-trx-usrp1.cfg
 | 
			
		||||
			fi
 | 
			
		||||
 | 
			
		||||
			if [ -d /etc/osmocom ]; then
 | 
			
		||||
				chown -v root:osmocom /etc/osmocom
 | 
			
		||||
				chmod -v 2775 /etc/osmocom
 | 
			
		||||
			fi
 | 
			
		||||
 | 
			
		||||
			mkdir -p /var/lib/osmocom
 | 
			
		||||
			chown -R -v osmocom:osmocom /var/lib/osmocom
 | 
			
		||||
		fi
 | 
			
		||||
		;;
 | 
			
		||||
esac
 | 
			
		||||
 | 
			
		||||
# dh_installdeb(1) will replace this with shell code automatically
 | 
			
		||||
# generated by other debhelper scripts.
 | 
			
		||||
#DEBHELPER#
 | 
			
		||||
							
								
								
									
										11
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										11
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							@@ -9,7 +9,16 @@ override_dh_shlibdeps:
 | 
			
		||||
	dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
 | 
			
		||||
 | 
			
		||||
override_dh_auto_configure:
 | 
			
		||||
	dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-ipc --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
 | 
			
		||||
	dh_auto_configure -- \
 | 
			
		||||
		--enable-manuals \
 | 
			
		||||
		--with-systemdsystemunitdir=/lib/systemd/system \
 | 
			
		||||
		--with-bladerf \
 | 
			
		||||
		--with-ipc \
 | 
			
		||||
		--with-lms \
 | 
			
		||||
		--with-mstrx \
 | 
			
		||||
		--with-uhd \
 | 
			
		||||
		--with-usrp1 \
 | 
			
		||||
		$(NULL)
 | 
			
		||||
 | 
			
		||||
override_dh_strip:
 | 
			
		||||
	dh_strip --dbg-package=osmo-trx-dbg
 | 
			
		||||
 
 | 
			
		||||
@@ -1,3 +1,15 @@
 | 
			
		||||
# all config examples must be listed here unconditionally, so that
 | 
			
		||||
# all of them end up in the release tarball (see OS#6349)
 | 
			
		||||
EXTRA_DIST = \
 | 
			
		||||
	osmo-trx-uhd/osmo-trx-limesdr.cfg \
 | 
			
		||||
	osmo-trx-uhd/osmo-trx-usrp_b200.cfg \
 | 
			
		||||
	osmo-trx-uhd/osmo-trx-uhd.cfg \
 | 
			
		||||
	osmo-trx-uhd/osmo-trx-umtrx.cfg \
 | 
			
		||||
	osmo-trx-lms/osmo-trx-limesdr.cfg \
 | 
			
		||||
	osmo-trx-lms/osmo-trx-lms.cfg \
 | 
			
		||||
	osmo-trx-ipc/osmo-trx-ipc.cfg \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
OSMOCONF_FILES =
 | 
			
		||||
osmoconfdir = $(sysconfdir)/osmocom
 | 
			
		||||
 | 
			
		||||
@@ -19,7 +31,6 @@ OSMOCONF_FILES += osmo-trx-ipc/osmo-trx-ipc.cfg
 | 
			
		||||
endif
 | 
			
		||||
 | 
			
		||||
osmoconf_DATA = $(OSMOCONF_FILES)
 | 
			
		||||
EXTRA_DIST = $(OSMOCONF_FILES)
 | 
			
		||||
 | 
			
		||||
CFG_FILES = find $(srcdir) -type f -name '*.cfg*' | sed -e 's,^$(srcdir),,'
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
log stderr
 | 
			
		||||
 logging filter all 1
 | 
			
		||||
 logging color 1
 | 
			
		||||
 logging print category-hex 0
 | 
			
		||||
 logging print category 1
 | 
			
		||||
 logging timestamp 1
 | 
			
		||||
 logging print file basename
 | 
			
		||||
 logging timestamp 0
 | 
			
		||||
 logging print file basename last
 | 
			
		||||
 logging print level 1
 | 
			
		||||
 logging level set-all notice
 | 
			
		||||
!
 | 
			
		||||
line vty
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
log stderr
 | 
			
		||||
 logging filter all 1
 | 
			
		||||
 logging color 1
 | 
			
		||||
 logging print category-hex 0
 | 
			
		||||
 logging print category 1
 | 
			
		||||
 logging timestamp 1
 | 
			
		||||
 logging print file basename
 | 
			
		||||
 logging timestamp 0
 | 
			
		||||
 logging print file basename last
 | 
			
		||||
 logging print level 1
 | 
			
		||||
 logging level set-all notice
 | 
			
		||||
!
 | 
			
		||||
line vty
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
log stderr
 | 
			
		||||
 logging filter all 1
 | 
			
		||||
 logging color 1
 | 
			
		||||
 logging print category-hex 0
 | 
			
		||||
 logging print category 1
 | 
			
		||||
 logging timestamp 1
 | 
			
		||||
 logging print file basename
 | 
			
		||||
 logging timestamp 0
 | 
			
		||||
 logging print file basename last
 | 
			
		||||
 logging print level 1
 | 
			
		||||
 logging level set-all notice
 | 
			
		||||
!
 | 
			
		||||
line vty
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
log stderr
 | 
			
		||||
 logging filter all 1
 | 
			
		||||
 logging color 1
 | 
			
		||||
 logging print category-hex 0
 | 
			
		||||
 logging print category 1
 | 
			
		||||
 logging timestamp 1
 | 
			
		||||
 logging print file basename
 | 
			
		||||
 logging timestamp 0
 | 
			
		||||
 logging print file basename last
 | 
			
		||||
 logging print level 1
 | 
			
		||||
 logging level set-all notice
 | 
			
		||||
!
 | 
			
		||||
line vty
 | 
			
		||||
 
 | 
			
		||||
@@ -1,9 +1,11 @@
 | 
			
		||||
log stderr
 | 
			
		||||
 logging filter all 1
 | 
			
		||||
 logging color 1
 | 
			
		||||
 logging print category-hex 0
 | 
			
		||||
 logging print category 1
 | 
			
		||||
 logging timestamp 1
 | 
			
		||||
 logging print file basename
 | 
			
		||||
 logging timestamp 0
 | 
			
		||||
 logging print file basename last
 | 
			
		||||
 logging print level 1
 | 
			
		||||
 logging level set-all notice
 | 
			
		||||
!
 | 
			
		||||
line vty
 | 
			
		||||
 
 | 
			
		||||
@@ -9,7 +9,7 @@ digraph hierarchy {
 | 
			
		||||
node[shape=record,style=filled,fillcolor=gray95]
 | 
			
		||||
edge[dir=back, arrowtail=empty]
 | 
			
		||||
 | 
			
		||||
2[label = "{Transceiver|+ constructor()\l+ destructor()\l+ init()\l+ numChans()\l+ receiveFIFO()\l+ setSignalHandler()}"]
 | 
			
		||||
2[label = "{Transceiver|+ constructor()\l+ destructor()\l+ init()\l+ numChans()\l+ receiveFIFO()\l+ setSignalHandler()\l}"]
 | 
			
		||||
3[label = "{RadioInterface|...}"]
 | 
			
		||||
4[label = "{RadioInterfaceResamp|...}"]
 | 
			
		||||
5[label = "{RadioInterfaceMulti|...}"]
 | 
			
		||||
@@ -17,6 +17,7 @@ edge[dir=back, arrowtail=empty]
 | 
			
		||||
7[label = "{UHDDevice|...}"]
 | 
			
		||||
8[label = "{LMSDevice|...}"]
 | 
			
		||||
9[label = "{USRPDevice|...}"]
 | 
			
		||||
10[label = "{IPCDevice|...}"]
 | 
			
		||||
 | 
			
		||||
2->3[arrowtail=odiamond]
 | 
			
		||||
3->4[constraint=false]
 | 
			
		||||
@@ -25,6 +26,7 @@ edge[dir=back, arrowtail=empty]
 | 
			
		||||
6->7
 | 
			
		||||
6->8
 | 
			
		||||
6->9
 | 
			
		||||
6->10
 | 
			
		||||
}
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										301
									
								
								doc/manuals/chapters/ipc_if.adoc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										301
									
								
								doc/manuals/chapters/ipc_if.adoc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,301 @@
 | 
			
		||||
[[ipc_if]]
 | 
			
		||||
== osmo-trx-ipc IPC Interface
 | 
			
		||||
 | 
			
		||||
This interface is the one used by _osmo_trx_ipc_ backend to communicate to a
 | 
			
		||||
third party process in charge of driving the lowest layer device-specific bits
 | 
			
		||||
(from now on the Driver).
 | 
			
		||||
 | 
			
		||||
It consists of a set of Unix Domain (UD) sockets for the control plane, plus a
 | 
			
		||||
shared memory region for the data plane.
 | 
			
		||||
 | 
			
		||||
Related code can be found in the
 | 
			
		||||
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc[Transceiver52M/device/ipc/]
 | 
			
		||||
directory in _osmo-trx.git_.
 | 
			
		||||
 | 
			
		||||
If you are a potential driver implementator, the
 | 
			
		||||
various primitives and data structures are publicly available in header file
 | 
			
		||||
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/shm.h[Transceiver52M/device/ipc/shm.h].
 | 
			
		||||
 | 
			
		||||
=== Control plane
 | 
			
		||||
 | 
			
		||||
Control plane protocol is transmitted over Unix Domain (UD) sockets using
 | 
			
		||||
message based primitives. Each primitive has a type identified by an integer,
 | 
			
		||||
and each type of primitive has a number of extra attributes attached to it. The
 | 
			
		||||
IPC interface consists of 2 types of UD sockets:
 | 
			
		||||
 | 
			
		||||
* _Master_ UD socket: One per osmo-trx-ipc process.
 | 
			
		||||
 | 
			
		||||
* _Channel_ UD socket: One for each channel managed by osmo-trx-ipc process.
 | 
			
		||||
 | 
			
		||||
The _Driver_ is in all cases expected to take the server role when creating UD
 | 
			
		||||
sockets, while _osmo-trx-ipc_ takes the client role and connects to sockets
 | 
			
		||||
provided by the driver.
 | 
			
		||||
 | 
			
		||||
=== Master UD socket
 | 
			
		||||
 | 
			
		||||
During startup, _osmo-trx-ipc_ will try connecting to the _Driver_ Master UD
 | 
			
		||||
socket located in the path provided by its own (VTY) configuration. As a result,
 | 
			
		||||
it means the _Driver_ process must be running and listening on the Master UD
 | 
			
		||||
socket before _osmo-trx-ipc_ is started, otherwise _osmo-trx-ipc_ will fail and
 | 
			
		||||
exit.
 | 
			
		||||
 | 
			
		||||
Once connected, _osmo-trx-ipc_ will submit a `GREETING_REQ` message primitive
 | 
			
		||||
announcing the maximum supported protocol version (first version ever is `1`,
 | 
			
		||||
increasing over time).
 | 
			
		||||
 | 
			
		||||
The _Driver_ shall then answer in `GREETING_CNF` message primitive with its own
 | 
			
		||||
maximum supported version (`<=` version received), providing 0 if none is
 | 
			
		||||
supported.
 | 
			
		||||
 | 
			
		||||
If _osmo-trx-ipc_ receives back the requested version, then both sides agreed
 | 
			
		||||
on the protocol version to use.
 | 
			
		||||
If _osmo-trx-ipc_ receives back a lower version, it shall decide to continue
 | 
			
		||||
with version negotiation using a lower version, until a supported version or 0
 | 
			
		||||
is received. If finally 0 is received, _osmo-trx-ipc_ will disconnect and exit
 | 
			
		||||
with failure.
 | 
			
		||||
 | 
			
		||||
Once the version is negotiated (`v1` as of current date), _osmo-trx-ipc_ will
 | 
			
		||||
ask for device information and available characeristics to the _Driver_ using
 | 
			
		||||
the `INFO_REQ` message primitive.
 | 
			
		||||
 | 
			
		||||
The _Driver_ shall then answer with a `INFO_CNF` message
 | 
			
		||||
containing information, such as:
 | 
			
		||||
 | 
			
		||||
* String containing device description
 | 
			
		||||
 | 
			
		||||
* Available reference clocks,
 | 
			
		||||
 | 
			
		||||
* {rx,tx} I/Q scaling factors
 | 
			
		||||
 | 
			
		||||
* Maximum number of channels supported
 | 
			
		||||
 | 
			
		||||
* for each channel:
 | 
			
		||||
 | 
			
		||||
** List of available {rx,tx} paths/antennas.
 | 
			
		||||
 | 
			
		||||
** {min,max}{rx,tx} gains
 | 
			
		||||
 | 
			
		||||
** Nominal transmit power
 | 
			
		||||
 | 
			
		||||
All the information received from the _Driver_ during `INFO_CNF` will be used by
 | 
			
		||||
_osmo-trx-ipc_ to decide whether it can fullfil the requested configuration from
 | 
			
		||||
the user, and proceed to open the device, or exit with a failure (for instance
 | 
			
		||||
number of channels, referece clock or tx/rx antenna selected by the user cannot
 | 
			
		||||
be fullfilled).
 | 
			
		||||
 | 
			
		||||
_osmo-trx-ipc_ will then proceed to open the device and do an initial
 | 
			
		||||
configuration using an `OPEN_REQ` message, where it will provide the _Driver_
 | 
			
		||||
with the desired selected configuration (such as number of channels, rx/tx
 | 
			
		||||
paths, clock reference, bandwidth filters, etc.).
 | 
			
		||||
 | 
			
		||||
The _Driver_ shall then configure the device and send back a `OPEN_CNF` with:
 | 
			
		||||
 | 
			
		||||
* `return_code` integer attribute set to `0` on success or `!0` on error.
 | 
			
		||||
 | 
			
		||||
* Name of the Posix Shared Memory region where data plane is going to be
 | 
			
		||||
transmitted.
 | 
			
		||||
 | 
			
		||||
* One path for each channel, containing the just-created UD socket to manage
 | 
			
		||||
that channel (for instance by taking Master UD socket path and appending
 | 
			
		||||
`_$chan_idx`).
 | 
			
		||||
 | 
			
		||||
* Path Delay: this is the loopback path delay in samples (= used as a timestamp
 | 
			
		||||
offset internally by _osmo-trx-ipc_), this value contains the analog delay as
 | 
			
		||||
well as the delay introduced by the digital filters in the fpga in the sdr
 | 
			
		||||
devices, and is therefore device type and bandwidth/sample rate dependant. This
 | 
			
		||||
can not be omitted, wrong values will lead to a _osmo-trx-ipc_ that just doesn't
 | 
			
		||||
detect any bursts.
 | 
			
		||||
 | 
			
		||||
Finally, _osmo-trx-ipc_ will connect to each channel's UD socket (see next
 | 
			
		||||
section).
 | 
			
		||||
 | 
			
		||||
Upon _osmo-trx-ipc_ closing the UD master socket connection, the _Driver_ shall
 | 
			
		||||
go into _closed_ state: stop all processing and instruct the device to power
 | 
			
		||||
off.
 | 
			
		||||
 | 
			
		||||
TIP: See
 | 
			
		||||
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/shm.h[Transceiver52M/device/ipc/shm.h]
 | 
			
		||||
for the detailed definition of all the related message primitives and data
 | 
			
		||||
types for this socket.
 | 
			
		||||
 | 
			
		||||
=== Channel UD Socket
 | 
			
		||||
 | 
			
		||||
This socket can be used by _osmo-trx-ipc_ to start/stop data plane processing or
 | 
			
		||||
change channel's parameters such as Rx/Tx Frequency, Rx/Tx gains, etc.
 | 
			
		||||
 | 
			
		||||
A channel can be either in _started_ or _stopped_ state. When a channel is
 | 
			
		||||
created (during `OPEN_REQ` in the Master UD Socket), it's by default in
 | 
			
		||||
_stopped_ state. `START_REQ` and `STOP_REQ` messages control this state, and
 | 
			
		||||
eventual failures can be reported through `START_CNF` and `STOP_CNF` by the
 | 
			
		||||
_Driver_.
 | 
			
		||||
 | 
			
		||||
The message `START_REQ` instructs the _Driver_ to start processing data in the
 | 
			
		||||
data plane. Similary, `STOP_REQ` instructs the _Driver_ to stop processing data
 | 
			
		||||
in the data plane.
 | 
			
		||||
 | 
			
		||||
Some parameters are usually changed only when the channel is in stopped mode,
 | 
			
		||||
for instance Rx/Tx Frequency.
 | 
			
		||||
 | 
			
		||||
TIP: See
 | 
			
		||||
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/shm.h[Transceiver52M/device/ipc/shm.h]
 | 
			
		||||
for the detailed definition of all the related message primitives and data
 | 
			
		||||
types for this socket.
 | 
			
		||||
 | 
			
		||||
=== Data Plane
 | 
			
		||||
 | 
			
		||||
Data plane protocol is implemented by means of a ring buffer structure on top of
 | 
			
		||||
Posix Shared Memory (see `man 7 shm_overview`) between _osmo-trx-ipc_ process
 | 
			
		||||
and the _Driver_.
 | 
			
		||||
 | 
			
		||||
The Posix Shared Memory region is created and its memory structure prepared by
 | 
			
		||||
the _Driver_ and its name shared with _osmo-trx-ipc_ during _OPEN_CNF_ message
 | 
			
		||||
in the Master UD Socket from the Control Plane. Resource allocation for the
 | 
			
		||||
shared memory area and cleanup is up to the ipc server, as is mutex
 | 
			
		||||
initialization for the buffers.
 | 
			
		||||
 | 
			
		||||
==== Posix Shared Memory structure
 | 
			
		||||
 | 
			
		||||
[[fig-shm-structure]]
 | 
			
		||||
.General overview of Posix Shared Memory structure
 | 
			
		||||
[graphviz]
 | 
			
		||||
----
 | 
			
		||||
digraph hierarchy {
 | 
			
		||||
node[shape=record,style=filled,fillcolor=gray95]
 | 
			
		||||
edge[dir=back, arrowtail=empty]
 | 
			
		||||
 | 
			
		||||
SHM[label = "{Posix Shared Memory region|+ num_chans\l+ Channels[]\l}"]
 | 
			
		||||
CHAN0[label = "{Channel 0|...}"]
 | 
			
		||||
CHAN1[label = "{Channel 1|...}"]
 | 
			
		||||
CHANN[label = "{Channel ...|}"]
 | 
			
		||||
STREAM0_UL[label = "{UL Stream|+ semaphore\l+ read_next\l+ write_next\l+ buffer_size /* In samples */\l+ num_buffers\l+ sample_buffers[]\l}"]
 | 
			
		||||
STREAM0_DL[label = "{DL Stream|+ semaphore\l+ read_next\l+ write_next\l+ buffer_size /* In samples */\l+ num_buffers\l+ sample_buffers[]\l}"]
 | 
			
		||||
STREAM1_UL[label = "{UL Stream|...}"]
 | 
			
		||||
STREAM1_DL[label = "{DL Stream|...}"]
 | 
			
		||||
STREAMN_UL[label = "{UL Stream|...}"]
 | 
			
		||||
STREAMN_DL[label = "{DL Stream|...}"]
 | 
			
		||||
BUF_0DL0[label = "{DL Sample Buffer 0|+ timestamp\l+ buffer_size /* In samples */\l+ samples[] = [16bit I + 16bit Q,...]\l}"]
 | 
			
		||||
BUF_0DLN[label = "{DL Sample Buffer ....|...}"]
 | 
			
		||||
BUF_0UL0[label = "{UL Sample Buffer 0|+ timestamp\l+ buffer_size /* In samples */\l+ samples[] = [16bit I + 16bit Q,...]\l}"]
 | 
			
		||||
BUF_0ULN[label = "{UL Sample Buffer ...|...}"]
 | 
			
		||||
 | 
			
		||||
SHM->CHAN0
 | 
			
		||||
SHM->CHAN1
 | 
			
		||||
SHM->CHANN
 | 
			
		||||
 | 
			
		||||
CHAN0->STREAM0_DL
 | 
			
		||||
CHAN0->STREAM0_UL
 | 
			
		||||
STREAM0_DL->BUF_0DL0
 | 
			
		||||
STREAM0_DL->BUF_0DLN
 | 
			
		||||
STREAM0_UL->BUF_0UL0
 | 
			
		||||
STREAM0_UL->BUF_0ULN
 | 
			
		||||
 | 
			
		||||
CHAN1->STREAM1_UL
 | 
			
		||||
CHAN1->STREAM1_DL
 | 
			
		||||
 | 
			
		||||
CHANN->STREAMN_UL
 | 
			
		||||
CHANN->STREAMN_DL
 | 
			
		||||
}
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
The Posix Shared Memory region contains an array of _Channels_.
 | 
			
		||||
 | 
			
		||||
Each _Channel_ contains 2 Streams:
 | 
			
		||||
 | 
			
		||||
* Downlink _Stream_
 | 
			
		||||
 | 
			
		||||
* Uplink _Stream_
 | 
			
		||||
 | 
			
		||||
Each _Stream_ handles a ring buffer, which is implemented as:
 | 
			
		||||
 | 
			
		||||
* An array of pointers to _Sample Buffer_ structures.
 | 
			
		||||
 | 
			
		||||
* Variables containing the number of buffers in the array, as well as the
 | 
			
		||||
maximum size in samples for each Sample Buffer.
 | 
			
		||||
 | 
			
		||||
* Variables containing `next_read` and `next_write` _Sample Buffer_ (its index
 | 
			
		||||
in the array of pointers).
 | 
			
		||||
 | 
			
		||||
* Unnamed Posix semaphores to do the required locking while using the ring
 | 
			
		||||
buffer.
 | 
			
		||||
 | 
			
		||||
Each _Sample Buffer_ contains:
 | 
			
		||||
 | 
			
		||||
* A `timestamp` variable, containing the position in the stream of the first
 | 
			
		||||
sample in the buffer
 | 
			
		||||
 | 
			
		||||
* A `data_len` variable, containing the amount of samples available to process
 | 
			
		||||
in the buffer
 | 
			
		||||
 | 
			
		||||
* An array of samples of size specified by the stream struct it is part of.
 | 
			
		||||
 | 
			
		||||
==== Posix Shared Memory format
 | 
			
		||||
 | 
			
		||||
The Posix Shared memory region shall be formatted applying the following
 | 
			
		||||
considerations:
 | 
			
		||||
 | 
			
		||||
* All pointers in the memory region are encoded as offsets from the start
 | 
			
		||||
address of the region itself, to allow different processes with different
 | 
			
		||||
address spaces to decode them.
 | 
			
		||||
 | 
			
		||||
* All structs must be force-aligned to 8 bytes
 | 
			
		||||
 | 
			
		||||
* Number of buffers must be power of 2 (2,4,8,16,...) - 4 appears to be plenty
 | 
			
		||||
 | 
			
		||||
* IQ samples format: One (complex) sample consists of 16bit i + 16bit q, so the
 | 
			
		||||
buffer size is number of IQ pairs.
 | 
			
		||||
 | 
			
		||||
* A reasonable per-buffer size (in samples) is 2500, since this happens to be
 | 
			
		||||
the ususal TX (downlink) buffer size used by _osmo-trx-ipc_ with the b210 (rx
 | 
			
		||||
over-the-wire packet size for the b210 is 2040 samples, so the larger value of
 | 
			
		||||
both is convenient).
 | 
			
		||||
 | 
			
		||||
TIP: See
 | 
			
		||||
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/shm.h[Transceiver52M/device/ipc/shm.h]
 | 
			
		||||
for the detailed definition of all the objects being part of the Posix Shared
 | 
			
		||||
memory region structure
 | 
			
		||||
 | 
			
		||||
==== Posix Shared Memory procedures
 | 
			
		||||
 | 
			
		||||
The queue in the shared memory area is not supposed to be used for actual
 | 
			
		||||
buffering of data, only for exchange, so the general expectation is that it is
 | 
			
		||||
mostly empty. The only exception to that might be minor processing delays, and
 | 
			
		||||
during startup.
 | 
			
		||||
 | 
			
		||||
Care must be taken to ensure that only timed waits for the mutex protecting it
 | 
			
		||||
and the condition variables are used, in order to ensure that no deadlock occurs
 | 
			
		||||
should the other side die/quit.
 | 
			
		||||
 | 
			
		||||
Thread cancellation should be disabled during reads/writes from/to the queue. In
 | 
			
		||||
general a timeout can be considered a non recoverable error during regular
 | 
			
		||||
processing after startup, at least with the current timeout value of one second.
 | 
			
		||||
 | 
			
		||||
Should over- or underflows occur a corresponding message should be sent towards
 | 
			
		||||
_osmo-trx-ipc_.
 | 
			
		||||
 | 
			
		||||
Upon **read** of `N` samples, the reader does something like:
 | 
			
		||||
 | 
			
		||||
. Acquire the semaphore in the channel's stream object.
 | 
			
		||||
 | 
			
		||||
. Read `stream->next_read`, if `next_read==next_write`, become blocked in
 | 
			
		||||
another sempahore (unlocking the previous one) until writer signals us, then
 | 
			
		||||
`buff = stream->buffers[next_read]`
 | 
			
		||||
 | 
			
		||||
. Read `buff->data_len` samples, reset the buffer data (`data_len=0`),
 | 
			
		||||
increment `next_read` and if read samples is `<N`, continue with next buffer
 | 
			
		||||
until `next_read==next_write`, then block again or if timeout elapsed, then  we
 | 
			
		||||
reach conditon buffer underflow and `return len < N`.
 | 
			
		||||
 | 
			
		||||
. Release the semaphore
 | 
			
		||||
 | 
			
		||||
Upon **write** of `N` samples, the writer does something like:
 | 
			
		||||
 | 
			
		||||
. Acquire the semapore in the channel's stream object.
 | 
			
		||||
 | 
			
		||||
. Write samples to `buff = stream->buffers[next_write]`. If `data_len!=0`,
 | 
			
		||||
signal `buffer_overflow` (increase field in stream object) and probably
 | 
			
		||||
increase next_read`.
 | 
			
		||||
 | 
			
		||||
. Increase `next_write`.
 | 
			
		||||
 | 
			
		||||
. If `next_write` was `== next_read`, signal the reader through the other
 | 
			
		||||
semaphore that it can continue reading.
 | 
			
		||||
@@ -71,3 +71,103 @@ with a memory buffer. In this mode, data written to the USRP is actually stored
 | 
			
		||||
in a buffer, and read commands to the USRP simply pull data from this buffer.
 | 
			
		||||
This was very useful in early testing, and still may be useful in testing basic
 | 
			
		||||
Transceiver and radioInterface functionality.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
[[backend_ipc]]
 | 
			
		||||
=== `osmo-trx-ipc` Inter Process Communication backend
 | 
			
		||||
 | 
			
		||||
This OsmoTRX model provides its own Inter Process Communication (IPC) interface
 | 
			
		||||
to drive the radio device driver (from now on the Driver), allowing for third
 | 
			
		||||
party processes to implement the lowest layer device-specific bits without being
 | 
			
		||||
affected by copyleft licenses of OsmoTRX.
 | 
			
		||||
 | 
			
		||||
For more information on such interface, see section <<ipc_if>>.
 | 
			
		||||
 | 
			
		||||
[[fig-backend-ipc]]
 | 
			
		||||
.Architecture with _osmo-trx-ipc_ and its IPC _Driver_
 | 
			
		||||
[graphviz]
 | 
			
		||||
----
 | 
			
		||||
digraph G {
 | 
			
		||||
        rankdir=LR;
 | 
			
		||||
        MS0 [label="MS"];
 | 
			
		||||
        MS1 [label="MS"];
 | 
			
		||||
        OsmoTRX [label="osmo-trx-ipc", color=red];
 | 
			
		||||
        BTS;
 | 
			
		||||
 | 
			
		||||
        subgraph cluster_ipc_driver {
 | 
			
		||||
                label = "IPC Driver";
 | 
			
		||||
                color=red;
 | 
			
		||||
                RE [label = "Radio Equipment"];
 | 
			
		||||
                REC [label="Radio Equipment Controller"];
 | 
			
		||||
                RE->REC;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
        REC->OsmoTRX [label="IPC Interface", color=red];
 | 
			
		||||
 | 
			
		||||
        MS0->RE [label="Um"];
 | 
			
		||||
        MS1->RE [label="Um"];
 | 
			
		||||
        OsmoTRX->BTS [label="bursts over UDP"];
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
A sample config file for this OsmoTRX model can be found in _osmo-trx.git_ https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/doc/examples/osmo-trx-ipc/osmo-trx-ipc.cfg[doc/examples/osmo-trx-ipc/osmo-trx-ipc.cfg]
 | 
			
		||||
 | 
			
		||||
In the config file, the following VTY command can be used to set up the IPC UD Master Socket _osmo-trx-ipc_ will connect to at startup:
 | 
			
		||||
 | 
			
		||||
.Example: _osmo-trx-ipc_ will connect to UD Master Socket /tmp/ipc_sock0 upon startup
 | 
			
		||||
----
 | 
			
		||||
dev-args ipc_msock=/tmp/ipc_sock0
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
==== ipc-device-test
 | 
			
		||||
 | 
			
		||||
When built with `--with-ipc --with-uhd` configure options, _osmo-trx.git_ will
 | 
			
		||||
build the test program called _ipc-driver-test_. This program implements the
 | 
			
		||||
_Driver_ side of the osmo-trx-ipc interface (see <<ipc_if>> for more
 | 
			
		||||
information) on one side, and also interacts internally with UHD (eg B210 as
 | 
			
		||||
when using osmo-trx-uhd).
 | 
			
		||||
 | 
			
		||||
You can use this small program as a reference to:
 | 
			
		||||
 | 
			
		||||
* Test and experiment with _osmo-trx-ipc_.
 | 
			
		||||
 | 
			
		||||
* Write your own IPC _Driver_ connecting to osmo-trx-ipc.
 | 
			
		||||
 | 
			
		||||
[[fig-backend-ipc-device-test]]
 | 
			
		||||
.Architecture with _osmo-trx-ipc_ and ipc-device-test as IPC _Driver_
 | 
			
		||||
[graphviz]
 | 
			
		||||
----
 | 
			
		||||
digraph G {
 | 
			
		||||
        rankdir=LR;
 | 
			
		||||
        MS0 [label="MS"];
 | 
			
		||||
        MS1 [label="MS"];
 | 
			
		||||
        SDR;
 | 
			
		||||
        ipc_device_test[label = "ipc-device-test", color=red];
 | 
			
		||||
        OsmoTRX [label="osmo-trx-ipc", color=red];
 | 
			
		||||
        BTS;
 | 
			
		||||
 | 
			
		||||
        MS0->SDR [label="Um"];
 | 
			
		||||
        MS1->SDR [label="Um"];
 | 
			
		||||
        SDR->ipc_device_test [label="UHD"];
 | 
			
		||||
        ipc_device_test->OsmoTRX [label="IPC Interface", color=red];
 | 
			
		||||
        OsmoTRX->BTS [label="bursts over UDP"];
 | 
			
		||||
}
 | 
			
		||||
----
 | 
			
		||||
 | 
			
		||||
The code for this app is found here:
 | 
			
		||||
 | 
			
		||||
* https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/ipc-driver-test.h[Transceiver52M/device/ipc/ipc-driver-test.h]
 | 
			
		||||
 | 
			
		||||
* https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/ipc-driver-test.c[Transceiver52M/device/ipc/ipc-driver-test.c]
 | 
			
		||||
 | 
			
		||||
Those files use the server-side (_Driver_ side) code to operate the Posix Shared
 | 
			
		||||
Memory region implemented in files `shm.c`, `shm.h`, `ipc_shm.c` and `ipc_shm.h`
 | 
			
		||||
in the same directory.
 | 
			
		||||
 | 
			
		||||
Most of the code in that same directory is deliverately released under a BSD
 | 
			
		||||
license (unlike most of _osmo-trx.git_), allowing third parties to reuse/recycle
 | 
			
		||||
the code on their implemented _Driver_ program no matter it being proprietary or
 | 
			
		||||
under an open license. However, care must be taken with external dependencies,
 | 
			
		||||
as for instance shm.c uses the talloc memory allocator, which is GPL licensed
 | 
			
		||||
and hence cannot be used in a proprietary driver.
 | 
			
		||||
@@ -35,6 +35,8 @@ include::./common/chapters/vty_cpu_sched.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/trx_if.adoc[]
 | 
			
		||||
 | 
			
		||||
include::{srcdir}/chapters/ipc_if.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/port_numbers.adoc[]
 | 
			
		||||
 | 
			
		||||
include::./common/chapters/bibliography.adoc[]
 | 
			
		||||
 
 | 
			
		||||
 Submodule osmocom-bb updated: 040bf41028...f12b17dffb
									
								
							@@ -1,7 +1,25 @@
 | 
			
		||||
include $(top_srcdir)/Makefile.common
 | 
			
		||||
 | 
			
		||||
AM_CPPFLAGS = -Wall -I$(top_srcdir)/CommonLibs $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) -g
 | 
			
		||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	-I$(top_srcdir)/CommonLibs \
 | 
			
		||||
	$(STD_DEFINES_AND_INCLUDES) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CXXFLAGS = \
 | 
			
		||||
	-Wall -g \
 | 
			
		||||
	$(LIBOSMOCORE_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOCTRL_CFLAGS) \
 | 
			
		||||
	$(LIBOSMOVTY_CFLAGS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_LDFLAGS = -no-install
 | 
			
		||||
 | 
			
		||||
LDADD = \
 | 
			
		||||
	$(COMMON_LA) \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOCTRL_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = BitVectorTest.ok \
 | 
			
		||||
             PRBSTest.ok \
 | 
			
		||||
@@ -20,21 +38,16 @@ check_PROGRAMS = \
 | 
			
		||||
	LogTest
 | 
			
		||||
 | 
			
		||||
BitVectorTest_SOURCES = BitVectorTest.cpp
 | 
			
		||||
BitVectorTest_LDADD = $(COMMON_LA)
 | 
			
		||||
 | 
			
		||||
PRBSTest_SOURCES = PRBSTest.cpp
 | 
			
		||||
 | 
			
		||||
InterthreadTest_SOURCES = InterthreadTest.cpp
 | 
			
		||||
InterthreadTest_LDADD = $(COMMON_LA)
 | 
			
		||||
InterthreadTest_LDFLAGS = -lpthread $(AM_LDFLAGS)
 | 
			
		||||
InterthreadTest_LDADD = $(LDADD) -lpthread
 | 
			
		||||
 | 
			
		||||
TimevalTest_SOURCES = TimevalTest.cpp
 | 
			
		||||
TimevalTest_LDADD = $(COMMON_LA)
 | 
			
		||||
 | 
			
		||||
VectorTest_SOURCES = VectorTest.cpp
 | 
			
		||||
VectorTest_LDADD = $(COMMON_LA)
 | 
			
		||||
 | 
			
		||||
LogTest_SOURCES = LogTest.cpp
 | 
			
		||||
LogTest_LDADD = $(COMMON_LA)
 | 
			
		||||
 | 
			
		||||
MOSTLYCLEANFILES += testSource testDestination
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,13 @@
 | 
			
		||||
include $(top_srcdir)/Makefile.common
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = -Wall -I$(top_srcdir)/Transceiver52M -I$(top_srcdir)/Transceiver52M/arch/common $(STD_DEFINES_AND_INCLUDES) -g
 | 
			
		||||
AM_CPPFLAGS = \
 | 
			
		||||
	-I$(top_srcdir)/Transceiver52M \
 | 
			
		||||
	-I$(top_srcdir)/Transceiver52M/arch/common \
 | 
			
		||||
	$(STD_DEFINES_AND_INCLUDES) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
 | 
			
		||||
AM_CFLAGS = -Wall -g
 | 
			
		||||
AM_LDFLAGS = -no-install
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = convolve_test.ok convolve_test_golden.h
 | 
			
		||||
 | 
			
		||||
@@ -20,10 +27,11 @@ endif
 | 
			
		||||
if DEVICE_LMS
 | 
			
		||||
check_PROGRAMS += LMSDeviceTest
 | 
			
		||||
LMSDeviceTest_SOURCES = LMSDeviceTest.cpp
 | 
			
		||||
LMSDeviceTest_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LMS_LIBS)
 | 
			
		||||
LMSDeviceTest_LDADD = \
 | 
			
		||||
	$(top_builddir)/Transceiver52M/device/lms/libdevice.la \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(COMMON_LA) \
 | 
			
		||||
	$(LMS_LIBS)
 | 
			
		||||
LMSDeviceTest_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
 | 
			
		||||
	$(LMS_LIBS) \
 | 
			
		||||
	$(NULL)
 | 
			
		||||
LMSDeviceTest_CXXFLAGS = $(AM_CFLAGS) $(LMS_CFLAGS)
 | 
			
		||||
endif
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,8 @@
 | 
			
		||||
AM_CPPFLAGS = $(LIBOSMOCODING_CFLAGS)
 | 
			
		||||
AM_CFLAGS = -Wall
 | 
			
		||||
 | 
			
		||||
DIST_SUBDIRS = va-test
 | 
			
		||||
 | 
			
		||||
EXTRA_DIST = clockdump.sh matlab
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS = osmo-prbs-tool
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										17
									
								
								utils/va-test/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								utils/va-test/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,17 @@
 | 
			
		||||
include $(top_srcdir)/Makefile.common
 | 
			
		||||
 | 
			
		||||
noinst_PROGRAMS = osmo-burst-gen
 | 
			
		||||
 | 
			
		||||
osmo_burst_gen_SOURCES = burst-gen.cpp \
 | 
			
		||||
	${top_srcdir}/Transceiver52M/grgsm_vitac/grgsm_vitac.cpp \
 | 
			
		||||
	${top_srcdir}/Transceiver52M/grgsm_vitac/viterbi_detector.cc
 | 
			
		||||
osmo_burst_gen_LDADD = \
 | 
			
		||||
	${top_srcdir}/Transceiver52M/libtransceiver_common.la \
 | 
			
		||||
	$(ARCH_LA) \
 | 
			
		||||
	$(GSM_LA) \
 | 
			
		||||
	$(COMMON_LA)
 | 
			
		||||
osmo_burst_gen_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) \
 | 
			
		||||
	-I${top_srcdir}/Transceiver52M/arch/common \
 | 
			
		||||
	-I${top_srcdir}/Transceiver52M/device/common \
 | 
			
		||||
	-I${top_srcdir}/Transceiver52M
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										531
									
								
								utils/va-test/burst-gen.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										531
									
								
								utils/va-test/burst-gen.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,531 @@
 | 
			
		||||
/*
 | 
			
		||||
 * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
			
		||||
 * All Rights Reserved
 | 
			
		||||
 *
 | 
			
		||||
 * Author: Eric Wild <ewild@sysmocom.de>
 | 
			
		||||
 *
 | 
			
		||||
 * This program is free software; you can redistribute it and/or modify
 | 
			
		||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
			
		||||
 * the Free Software Foundation; either version 3 of the License, or
 | 
			
		||||
 * (at your option) any later version.
 | 
			
		||||
 *
 | 
			
		||||
 * This program is distributed in the hope that it will be useful,
 | 
			
		||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
			
		||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
			
		||||
 * GNU Affero General Public License for more details.
 | 
			
		||||
 *
 | 
			
		||||
 * You should have received a copy of the GNU Affero General Public License
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
// this allows messing with the demod to check the detecton offset impact,
 | 
			
		||||
// not intended for actual automated tests.
 | 
			
		||||
 | 
			
		||||
#include "sigProcLib.h"
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include "convert.h"
 | 
			
		||||
#include <convolve.h>
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#define _CRT_SECURE_NO_WARNINGS
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <iomanip>
 | 
			
		||||
#include <numeric>
 | 
			
		||||
#include <memory>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
 | 
			
		||||
#ifdef HAVE_CONFIG_H
 | 
			
		||||
#include "config.h"
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#include <grgsm_vitac/grgsm_vitac.h>
 | 
			
		||||
 | 
			
		||||
#define DO_RACH
 | 
			
		||||
 | 
			
		||||
const int SAMPLE_SCALE_FACTOR = 1;
 | 
			
		||||
 | 
			
		||||
template <typename DST_T, typename SRC_T, typename ST>
 | 
			
		||||
void convert_and_scale(void *dst, void *src, unsigned int src_len, ST scale)
 | 
			
		||||
{
 | 
			
		||||
	for (unsigned int i = 0; i < src_len; i++)
 | 
			
		||||
		reinterpret_cast<DST_T *>(dst)[i] = static_cast<DST_T>((reinterpret_cast<SRC_T *>(src)[i]) * scale);
 | 
			
		||||
}
 | 
			
		||||
template <typename DST_T, typename SRC_T>
 | 
			
		||||
void convert_and_scale_default(void *dst, void *src, unsigned int src_len)
 | 
			
		||||
{
 | 
			
		||||
	return convert_and_scale<DST_T, SRC_T>(dst, src, src_len, SAMPLE_SCALE_FACTOR);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const unsigned int txFullScale = (float)(1 << 14) - 1;
 | 
			
		||||
// static const unsigned int rxFullScale = (float)(1 << 14) - 1;
 | 
			
		||||
 | 
			
		||||
static const BitVector
 | 
			
		||||
	gRACHBurstx("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
 | 
			
		||||
 | 
			
		||||
static const BitVector gTrainingSequencex[] = {
 | 
			
		||||
	BitVector("00100101110000100010010111"), BitVector("00101101110111100010110111"),
 | 
			
		||||
	BitVector("01000011101110100100001110"), BitVector("01000111101101000100011110"),
 | 
			
		||||
	BitVector("00011010111001000001101011"), BitVector("01001110101100000100111010"),
 | 
			
		||||
	BitVector("10100111110110001010011111"), BitVector("11101111000100101110111100"),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct mrv {
 | 
			
		||||
	std::vector<char> bits;
 | 
			
		||||
	signalVector *rvbuf;
 | 
			
		||||
	std::unique_ptr<std::vector<std::complex<float>>> convolved;
 | 
			
		||||
	// mrv(): bits(), demod_bits() {}
 | 
			
		||||
	CorrType ct;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
static mrv genRandNormalBurstx(int tsc, int sps, int tn)
 | 
			
		||||
{
 | 
			
		||||
	mrv retstruct;
 | 
			
		||||
	int i = 0;
 | 
			
		||||
	BitVector bits(148);
 | 
			
		||||
 | 
			
		||||
	/* Tail bits */
 | 
			
		||||
	for (; i < 3; i++)
 | 
			
		||||
		bits[i] = 0;
 | 
			
		||||
 | 
			
		||||
	/* Random bits */
 | 
			
		||||
	for (int j = 0; i < 60; i++, j++)
 | 
			
		||||
		bits[i] = rand() % 2;
 | 
			
		||||
 | 
			
		||||
	/* Stealing bit */
 | 
			
		||||
	bits[i++] = 0;
 | 
			
		||||
 | 
			
		||||
	/* Training sequence */
 | 
			
		||||
	for (int n = 0; i < 87; i++, n++)
 | 
			
		||||
		bits[i] = gTrainingSequencex[tsc][n];
 | 
			
		||||
 | 
			
		||||
	/* Stealing bit */
 | 
			
		||||
	bits[i++] = 0;
 | 
			
		||||
 | 
			
		||||
	/* Random bits */
 | 
			
		||||
	for (; i < 145; i++)
 | 
			
		||||
		bits[i] = rand() % 2;
 | 
			
		||||
 | 
			
		||||
	/* Tail bits */
 | 
			
		||||
	for (; i < 148; i++)
 | 
			
		||||
		bits[i] = 0;
 | 
			
		||||
 | 
			
		||||
	int guard = 8 + !(tn % 4);
 | 
			
		||||
	auto r = modulateBurst(bits, guard, sps);
 | 
			
		||||
 | 
			
		||||
	retstruct.rvbuf = r;
 | 
			
		||||
	for (size_t i = 0; i < bits.size(); i++)
 | 
			
		||||
		retstruct.bits.push_back(bits.bit(i) ? 1 : 0);
 | 
			
		||||
	return retstruct;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static mrv genRandAccessBurstx(int delay, int sps, int tn)
 | 
			
		||||
{
 | 
			
		||||
	mrv retstruct;
 | 
			
		||||
	int i = 0;
 | 
			
		||||
	BitVector bits(88 + delay);
 | 
			
		||||
 | 
			
		||||
	/* delay */
 | 
			
		||||
	for (; i < delay; i++)
 | 
			
		||||
		bits[i] = 0;
 | 
			
		||||
 | 
			
		||||
	/* head and synch bits */
 | 
			
		||||
	for (int n = 0; i < 49 + delay; i++, n++)
 | 
			
		||||
		bits[i] = gRACHBurstx[n];
 | 
			
		||||
 | 
			
		||||
	/* Random bits */
 | 
			
		||||
	for (int j = 0; i < 85 + delay; i++, j++)
 | 
			
		||||
		bits[i] = rand() % 2;
 | 
			
		||||
 | 
			
		||||
	for (; i < 88 + delay; i++)
 | 
			
		||||
		bits[i] = 0;
 | 
			
		||||
 | 
			
		||||
	int guard = 68 - delay + !(tn % 4);
 | 
			
		||||
	auto r = modulateBurst(bits, guard, sps);
 | 
			
		||||
 | 
			
		||||
	retstruct.rvbuf = r;
 | 
			
		||||
	for (size_t i = 0; i < bits.size(); i++)
 | 
			
		||||
		retstruct.bits.push_back(bits.bit(i) ? 1 : 0);
 | 
			
		||||
	return retstruct;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern gr_complex d_acc_training_seq[N_ACCESS_BITS]; ///<encoded training sequence of a SCH burst
 | 
			
		||||
extern gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a SCH burst
 | 
			
		||||
extern gr_complex d_norm_training_seq[TRAIN_SEQ_NUM]
 | 
			
		||||
				     [N_TRAIN_BITS]; ///<encoded training sequences of a normal and dummy burst
 | 
			
		||||
 | 
			
		||||
void sv_write_helper(signalVector *burst, std::string fname)
 | 
			
		||||
{
 | 
			
		||||
	auto start = burst->begin();
 | 
			
		||||
	auto n = burst->bytes();
 | 
			
		||||
	char *data = reinterpret_cast<char *>(start);
 | 
			
		||||
 | 
			
		||||
	const int len_in_real = burst->size() * 2;
 | 
			
		||||
	auto cvrtbuf_tx_a = new int16_t[len_in_real];
 | 
			
		||||
	convert_float_short(cvrtbuf_tx_a, (float *)burst->begin(), float(txFullScale), len_in_real);
 | 
			
		||||
 | 
			
		||||
	std::ofstream fout;
 | 
			
		||||
	fout.open(fname + ".cfile", std::ios::binary | std::ios::out);
 | 
			
		||||
	fout.write(data, n);
 | 
			
		||||
	fout.close();
 | 
			
		||||
 | 
			
		||||
	fout.open(fname + ".cs16", std::ios::binary | std::ios::out);
 | 
			
		||||
	fout.write((char *)cvrtbuf_tx_a, len_in_real * sizeof(uint16_t));
 | 
			
		||||
	fout.close();
 | 
			
		||||
	delete[] cvrtbuf_tx_a;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// borrowed from a real world burst..
 | 
			
		||||
static std::vector<std::complex<float>> chan_im_resp = {
 | 
			
		||||
	{ 4.1588e-05 + -0.000361925 },	{ 0.000112728 + -0.000289796 }, { 0.000162952 + -0.000169028 },
 | 
			
		||||
	{ 0.000174185 + -2.54575e-05 }, { 0.000142947 + 0.000105992 },	{ 8.65919e-05 + 0.000187041 },
 | 
			
		||||
	{ 4.15799e-05 + 0.000184346 },	{ 5.30207e-05 + 7.84921e-05 },	{ 0.000158877 + -0.000128058 },
 | 
			
		||||
	{ 0.000373956 + -0.000407954 }, { 0.000680606 + -0.000712065 }, { 0.00102929 + -0.000979604 },
 | 
			
		||||
	{ 0.00135049 + -0.00115333 },	{ 0.00157434 + -0.0011948 },	{ 0.00165098 + -0.00109534 },
 | 
			
		||||
	{ 0.00156519 + -0.000878794 },	{ 0.0013399 + -0.000594285 },	{ 0.00102788 + -0.00030189 },
 | 
			
		||||
	{ 0.000694684 + -5.58912e-05 }, { 0.000399328 + 0.000109463 }
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// as above, downsampled to 1sps + just magnitude
 | 
			
		||||
static std::vector<float> chan_im_resp_trunc = { 1., 0.20513351, 0.10020305, 0.11490235 };
 | 
			
		||||
 | 
			
		||||
template <typename A, typename B>
 | 
			
		||||
auto conv(const std::vector<A> &a, const std::vector<B> &b) -> std::unique_ptr<std::vector<A>>
 | 
			
		||||
{
 | 
			
		||||
	int data_len = a.size();
 | 
			
		||||
	int conv_len = b.size();
 | 
			
		||||
	int conv_size = conv_len + data_len - 1;
 | 
			
		||||
	auto retv = std::make_unique<std::vector<A>>(conv_size);
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < data_len; ++i) {
 | 
			
		||||
		for (int j = 0; j < conv_len; ++j) {
 | 
			
		||||
			(*retv)[i + j] += a[i] * b[j];
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return retv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
template <typename A>
 | 
			
		||||
static auto conv(const A *a, int len, std::vector<float> &b)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<A> aa(len);
 | 
			
		||||
	std::copy_n(a, len, aa.begin());
 | 
			
		||||
	std::reverse(b.begin(), b.end());
 | 
			
		||||
	return conv(aa, b);
 | 
			
		||||
}
 | 
			
		||||
template <typename A>
 | 
			
		||||
static auto conv(const A *a, int len, std::vector<A> &b)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<A> aa(len);
 | 
			
		||||
	std::copy_n(a, len, aa.begin());
 | 
			
		||||
	std::reverse(b.begin(), b.end());
 | 
			
		||||
	return conv(aa, b);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// signalvector is owning despite claiming not to, but we can pretend, too..
 | 
			
		||||
static void dummy_free(void *wData){};
 | 
			
		||||
static void *dummy_alloc(size_t newSize)
 | 
			
		||||
{
 | 
			
		||||
	return 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename T>
 | 
			
		||||
size_t read_from_file(std::string path, std::vector<T> &outvec)
 | 
			
		||||
{
 | 
			
		||||
	std::ifstream infile;
 | 
			
		||||
	infile.open(path, std::ios::in | std::ios::binary);
 | 
			
		||||
	if (infile.fail()) {
 | 
			
		||||
		std::cout << " not found: " << path << std::endl;
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
	infile.seekg(0, std::ios_base::end);
 | 
			
		||||
	size_t fsize = infile.tellg();
 | 
			
		||||
	auto fsize_in_T = fsize / sizeof(T);
 | 
			
		||||
	infile.seekg(0, std::ios_base::beg);
 | 
			
		||||
 | 
			
		||||
	outvec.resize(fsize_in_T);
 | 
			
		||||
	infile.read(reinterpret_cast<char *>(&outvec[0]), fsize);
 | 
			
		||||
	infile.close();
 | 
			
		||||
	std::cout << "Read " << fsize << " from " << path << std::endl;
 | 
			
		||||
	return fsize;
 | 
			
		||||
}
 | 
			
		||||
void demod_real_burst(int num = 0)
 | 
			
		||||
{
 | 
			
		||||
	auto path = "./nb_chunk_tsc7.cfile";
 | 
			
		||||
	auto bitfile = "./demodbits_tsc7.s8";
 | 
			
		||||
 | 
			
		||||
	std::vector<std::complex<float>> burstdata;
 | 
			
		||||
	std::vector<char> bitdata;
 | 
			
		||||
	read_from_file(path, burstdata);
 | 
			
		||||
	read_from_file(bitfile, bitdata);
 | 
			
		||||
 | 
			
		||||
	// print "known good" burst bits
 | 
			
		||||
	std::cerr << "known bits:" << std::endl;
 | 
			
		||||
	std::cerr << std::setw(5) << 0 << " - ";
 | 
			
		||||
	for (auto i : bitdata)
 | 
			
		||||
		std::cout << (i > 0 ? "1" : "0");
 | 
			
		||||
	std::cerr << std::endl;
 | 
			
		||||
	std::cerr << "demod tests sigproclib:" << std::endl;
 | 
			
		||||
 | 
			
		||||
	auto ct = CorrType::TSC;
 | 
			
		||||
	auto delay = 0;
 | 
			
		||||
	auto tsc = 7;
 | 
			
		||||
	int offset = 0;
 | 
			
		||||
	auto cplx = reinterpret_cast<complex *>(&burstdata[offset]);
 | 
			
		||||
	auto stdcplx = reinterpret_cast<std::complex<float> *>(&burstdata[offset]);
 | 
			
		||||
	signalVector sv(&cplx[0], 0, burstdata.size() - offset, dummy_alloc, dummy_free);
 | 
			
		||||
 | 
			
		||||
	struct estim_burst_params ebp;
 | 
			
		||||
	auto rc = detectAnyBurst(sv, tsc, BURST_THRESH, 4, ct, 40, &ebp);
 | 
			
		||||
 | 
			
		||||
	auto rxBurst = std::unique_ptr<SoftVector>(demodAnyBurst(sv, (CorrType)rc, 4, &ebp));
 | 
			
		||||
	// print osmotrx sigproclib demod result
 | 
			
		||||
	std::cerr << std::setw(5) << int(ebp.toa) << " o ";
 | 
			
		||||
	for (ssize_t i = 0 + delay; i < 148 + delay; i++)
 | 
			
		||||
		std::cout << (rxBurst->bit(i) ? "1" : "0");
 | 
			
		||||
	std::cerr << std::endl;
 | 
			
		||||
 | 
			
		||||
	std::cerr << "demod test va:" << std::endl;
 | 
			
		||||
	std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
	float ncmax;
 | 
			
		||||
	char demodded_softbits[444];
 | 
			
		||||
 | 
			
		||||
	// demod at known offset
 | 
			
		||||
	{
 | 
			
		||||
		auto inp = &stdcplx[29]; // known offset
 | 
			
		||||
		auto normal_burst_startX = get_norm_chan_imp_resp(inp, &chan_imp_resp[0], &ncmax, tsc);
 | 
			
		||||
		detect_burst_nb(inp, &chan_imp_resp[0], normal_burst_startX, demodded_softbits);
 | 
			
		||||
 | 
			
		||||
		std::cerr << std::setw(5) << normal_burst_startX << " v ";
 | 
			
		||||
		for (size_t i = 0; i < 148; i++)
 | 
			
		||||
			std::cerr << (demodded_softbits[i] < 0 ? "1" : "0");
 | 
			
		||||
		std::cerr << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
	{
 | 
			
		||||
		std::cerr << "-- va start offset loop --" << std::endl;
 | 
			
		||||
		std::cerr << "offset/det offset/#errors/known^demod bits" << std::endl;
 | 
			
		||||
		for (int i = 0; i < 34; i++) {
 | 
			
		||||
			auto inp = &stdcplx[i];
 | 
			
		||||
			auto conved_beg = inp;
 | 
			
		||||
 | 
			
		||||
			auto me = get_norm_chan_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, tsc);
 | 
			
		||||
			detect_burst_nb(conved_beg, &chan_imp_resp[0], me, demodded_softbits);
 | 
			
		||||
			auto bitdiffarr = std::make_unique<char[]>(148);
 | 
			
		||||
			for (size_t i = 0; i < 148; i++)
 | 
			
		||||
				bitdiffarr.get()[i] = (demodded_softbits[i] < 0 ? 1 : 0) ^ (bitdata[i] > 0 ? 1 : 0);
 | 
			
		||||
			auto ber = std::accumulate(bitdiffarr.get(), bitdiffarr.get() + 148, 0);
 | 
			
		||||
 | 
			
		||||
			std::cerr << std::setw(3) << i << ": " << std::setw(3) << me << " v " << std::setw(3) << ber
 | 
			
		||||
				  << " ";
 | 
			
		||||
			for (size_t i = 0; i < 148; i++)
 | 
			
		||||
				std::cerr << (bitdiffarr[i] ? "1" : "0");
 | 
			
		||||
			std::cerr << std::endl;
 | 
			
		||||
			// std::cerr << std::setw(4) << i << " (" << std::setw(4) << 29 - i << "):" << std::setw(4) << org
 | 
			
		||||
			// 	  << " " << std::setw(4) << me << " y " << std::endl;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
auto gen_burst(CorrType t, int delay, int tsc)
 | 
			
		||||
{
 | 
			
		||||
	mrv rs;
 | 
			
		||||
	if (t == CorrType::RACH) {
 | 
			
		||||
		rs = genRandAccessBurstx(delay, 4, tsc);
 | 
			
		||||
 | 
			
		||||
	} else if (t == CorrType::TSC) {
 | 
			
		||||
		rs = genRandNormalBurstx(tsc, 4, 0);
 | 
			
		||||
	} else {
 | 
			
		||||
		std::cerr << "wtf?" << std::endl;
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
	rs.ct = t;
 | 
			
		||||
 | 
			
		||||
	signalVector *burst = rs.rvbuf;
 | 
			
		||||
	// sv_write_helper(burst, std::to_string(num));
 | 
			
		||||
	// scaleVector(*burst, {1, 0});
 | 
			
		||||
	const int len_in_real = burst->size() * 2;
 | 
			
		||||
	auto cvrtbuf_tx_a = std::make_unique<short[]>(len_in_real);
 | 
			
		||||
	auto cvrtbuf_rx_a = std::make_unique<float[]>(len_in_real);
 | 
			
		||||
	auto rx_cfloat = reinterpret_cast<std::complex<float> *>(&cvrtbuf_rx_a[0]);
 | 
			
		||||
 | 
			
		||||
	convert_float_short(cvrtbuf_tx_a.get(), (float *)burst->begin(), float(txFullScale), len_in_real);
 | 
			
		||||
	convert_short_float(cvrtbuf_rx_a.get(), cvrtbuf_tx_a.get(), len_in_real);
 | 
			
		||||
	for (int i = 0; i < len_in_real; i++) // scale properly!
 | 
			
		||||
		cvrtbuf_rx_a[i] *= 1. / txFullScale;
 | 
			
		||||
	auto conved = conv(rx_cfloat, burst->size(), chan_im_resp);
 | 
			
		||||
 | 
			
		||||
	std::cerr << "-- generated " << (t == CorrType::RACH ? "RACH" : "TSC") << " burst --" << std::endl;
 | 
			
		||||
	for (size_t i = 0; i < rs.bits.size(); i++)
 | 
			
		||||
		std::cerr << (rs.bits[i] ? "1" : "0");
 | 
			
		||||
	std::cerr << std::endl;
 | 
			
		||||
	delete burst;
 | 
			
		||||
	rs.convolved = std::move(conved);
 | 
			
		||||
	return rs;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void demod_generated_burst(CorrType t)
 | 
			
		||||
{
 | 
			
		||||
	int tsc = 0;
 | 
			
		||||
	int delay = 0;
 | 
			
		||||
	auto rs = gen_burst(t, delay, tsc);
 | 
			
		||||
	auto conved_beg = &(*rs.convolved)[0];
 | 
			
		||||
 | 
			
		||||
	if (rs.ct == CorrType::RACH) {
 | 
			
		||||
		std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
		float ncmax;
 | 
			
		||||
		char demodded_softbits[444];
 | 
			
		||||
		int normal_burst_start = 0;
 | 
			
		||||
		normal_burst_start = get_access_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, 0);
 | 
			
		||||
		normal_burst_start = std::max(normal_burst_start, 0);
 | 
			
		||||
		for (int j = 0; j < 4; j++) {
 | 
			
		||||
			for (int start_val = 0; start_val < 16; start_val++) {
 | 
			
		||||
				auto bitdiffarr = std::make_unique<char[]>(rs.bits.size());
 | 
			
		||||
				detect_burst_ab(conved_beg, &chan_imp_resp[0], normal_burst_start + j,
 | 
			
		||||
						demodded_softbits, start_val);
 | 
			
		||||
 | 
			
		||||
				for (size_t i = 0; i < rs.bits.size(); i++)
 | 
			
		||||
					bitdiffarr.get()[i] = (demodded_softbits[i] < 0 ? 1 : 0) ^ rs.bits[i];
 | 
			
		||||
				auto ber = std::accumulate(bitdiffarr.get(), bitdiffarr.get() + rs.bits.size(), 0);
 | 
			
		||||
 | 
			
		||||
				std::cerr << "ber " << std::setw(4) << ber << " bo:" << std::setw(4) << j
 | 
			
		||||
					  << " vas:" << std::setw(4) << start_val << " ";
 | 
			
		||||
				// for (size_t i = 0; i < rs.num_bits; i++)
 | 
			
		||||
				// 	std::cerr << (demodded_softbits[i] < 0 ? "1" : "0");
 | 
			
		||||
				// std::cerr << std::endl;
 | 
			
		||||
				// std::cerr << "d " << std::setw(4) << ber << " ";
 | 
			
		||||
				for (size_t i = 0; i < rs.bits.size(); i++)
 | 
			
		||||
					std::cerr << (bitdiffarr.get()[i] ? "1" : "0");
 | 
			
		||||
				std::cerr << std::endl;
 | 
			
		||||
 | 
			
		||||
				// std::cerr << "v " << std::setw(4) << j << std::setw(4) << start_val << " ";
 | 
			
		||||
				// for (size_t i = 0; i < rs.num_bits; i++)
 | 
			
		||||
				// 	std::cerr << (demodded_softbits[i] < 0 ? "1" : "0");
 | 
			
		||||
				// std::cerr << std::endl;
 | 
			
		||||
				// std::cerr << "d " << std::setw(4) << ber << " ";
 | 
			
		||||
				// for (size_t i = 0; i < rs.num_bits; i++)
 | 
			
		||||
				// 	std::cerr << (ptr.get()[i] ? "1" : "0");
 | 
			
		||||
				// std::cerr << std::endl;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
		std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
		float ncmax;
 | 
			
		||||
		char demodded_softbits[444];
 | 
			
		||||
 | 
			
		||||
		auto normal_burst_start = get_norm_chan_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, tsc);
 | 
			
		||||
		detect_burst_nb(conved_beg, &chan_imp_resp[0], normal_burst_start + 0, demodded_softbits);
 | 
			
		||||
		std::cerr << "toa " << std::setprecision(2) << normal_burst_start << std::endl;
 | 
			
		||||
 | 
			
		||||
		std::cerr << "vita ";
 | 
			
		||||
		for (size_t i = 0; i < rs.bits.size(); i++)
 | 
			
		||||
			std::cerr << (demodded_softbits[i] < 0 ? "1" : "0");
 | 
			
		||||
		std::cerr << std::endl;
 | 
			
		||||
		std::cerr << "diff ";
 | 
			
		||||
		for (size_t i = 0; i < rs.bits.size(); i++)
 | 
			
		||||
			std::cerr << ((demodded_softbits[i] < 0 ? 1 : 0) ^ rs.bits[i] ? "1" : "0");
 | 
			
		||||
		std::cerr << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	struct estim_burst_params ebp;
 | 
			
		||||
	char demodded_softbits[444];
 | 
			
		||||
	complex *rx_sigproc_cfloat = reinterpret_cast<complex *>(conved_beg);
 | 
			
		||||
	signalVector sv(rx_sigproc_cfloat, 0, rs.convolved->size(), dummy_alloc, dummy_free);
 | 
			
		||||
 | 
			
		||||
	auto rc = detectAnyBurst(sv, tsc, BURST_THRESH, 4, rs.ct, 40, &ebp);
 | 
			
		||||
	auto rxBurst = std::unique_ptr<SoftVector>(demodAnyBurst(sv, (CorrType)rc, 4, &ebp));
 | 
			
		||||
 | 
			
		||||
	std::cerr << "toa " << std::setprecision(2) << ebp.toa << std::endl;
 | 
			
		||||
 | 
			
		||||
	for (ssize_t i = 0; i < delay; i++) // maybe pad rach op?
 | 
			
		||||
		demodded_softbits[i] = 0;
 | 
			
		||||
	for (size_t i = 0 + delay; i < rs.bits.size() + delay; i++)
 | 
			
		||||
		demodded_softbits[i] = (rxBurst->bit(i) ? 1 : 0);
 | 
			
		||||
 | 
			
		||||
	std::cerr << "sigp ";
 | 
			
		||||
	for (size_t i = 0; i < rs.bits.size(); i++)
 | 
			
		||||
		std::cerr << (demodded_softbits[i] ? "1" : "0");
 | 
			
		||||
	std::cerr << std::endl;
 | 
			
		||||
 | 
			
		||||
	std::cerr << "diff ";
 | 
			
		||||
	for (size_t i = 0; i < rs.bits.size(); i++)
 | 
			
		||||
		std::cerr << (demodded_softbits[i] ^ rs.bits[i] ? "1" : "0");
 | 
			
		||||
	std::cerr << std::endl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void demod_test_offsets()
 | 
			
		||||
{
 | 
			
		||||
	const int tsc = 0;
 | 
			
		||||
	const int delaybuffer_realoffset = 100;
 | 
			
		||||
 | 
			
		||||
	{
 | 
			
		||||
		auto rs = gen_burst(CorrType::RACH, 0, tsc);
 | 
			
		||||
		typeof(*rs.convolved) delay_buffer(rs.convolved->size() * 2); // plenty of space..
 | 
			
		||||
 | 
			
		||||
		for (int delay = -10; delay < 60; delay++) {
 | 
			
		||||
			std::fill(delay_buffer.begin(), delay_buffer.end(), 0);
 | 
			
		||||
			std::copy(rs.convolved->begin(), rs.convolved->end(),
 | 
			
		||||
				  delay_buffer.begin() + delaybuffer_realoffset + delay);
 | 
			
		||||
 | 
			
		||||
			auto conved_beg = &delay_buffer[delaybuffer_realoffset];
 | 
			
		||||
 | 
			
		||||
			std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
			float ncmax;
 | 
			
		||||
			auto va_burst_start = get_access_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, 60);
 | 
			
		||||
 | 
			
		||||
			complex *rx_sigproc_cfloat = reinterpret_cast<complex *>(conved_beg);
 | 
			
		||||
			struct estim_burst_params ebp;
 | 
			
		||||
			signalVector sv(rx_sigproc_cfloat, 0, rs.convolved->size(), dummy_alloc, dummy_free);
 | 
			
		||||
			detectAnyBurst(sv, tsc, BURST_THRESH, 4, rs.ct, 60, &ebp);
 | 
			
		||||
			std::cerr << "delay:" << std::setw(3) << std::setprecision(2) << delay;
 | 
			
		||||
			std::cerr << " va: " << std::setw(3) << std::setprecision(2) << va_burst_start;
 | 
			
		||||
			std::cerr << " sg: " << std::setw(3) << std::setprecision(2) << ebp.toa;
 | 
			
		||||
			std::cerr << " d: " << std::setw(3) << std::setprecision(2) << (ebp.toa * 4) - va_burst_start;
 | 
			
		||||
			std::cerr << " ! " << float(va_burst_start + 13) / 4 << std::endl;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	{
 | 
			
		||||
		auto rs = gen_burst(CorrType::TSC, 0, tsc);
 | 
			
		||||
		typeof(*rs.convolved) delay_buffer(rs.convolved->size() * 2); // plenty of space..
 | 
			
		||||
 | 
			
		||||
		for (int delay = -10; delay < 10; delay++) {
 | 
			
		||||
			std::fill(delay_buffer.begin(), delay_buffer.end(), 0);
 | 
			
		||||
			std::copy(rs.convolved->begin(), rs.convolved->end(),
 | 
			
		||||
				  delay_buffer.begin() + delaybuffer_realoffset + delay);
 | 
			
		||||
 | 
			
		||||
			auto conved_beg = &delay_buffer[delaybuffer_realoffset];
 | 
			
		||||
 | 
			
		||||
			std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
			float ncmax;
 | 
			
		||||
			auto va_burst_start = get_norm_chan_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, tsc);
 | 
			
		||||
 | 
			
		||||
			complex *rx_sigproc_cfloat = reinterpret_cast<complex *>(conved_beg);
 | 
			
		||||
			struct estim_burst_params ebp;
 | 
			
		||||
			signalVector sv(rx_sigproc_cfloat, 0, rs.convolved->size(), dummy_alloc, dummy_free);
 | 
			
		||||
			detectAnyBurst(sv, tsc, BURST_THRESH, 4, rs.ct, 60, &ebp);
 | 
			
		||||
			std::cerr << "delay:" << std::setw(3) << std::setprecision(2) << delay;
 | 
			
		||||
			std::cerr << " va: " << std::setw(3) << std::setprecision(2) << va_burst_start;
 | 
			
		||||
			std::cerr << " sg: " << std::setw(3) << std::setprecision(2) << ebp.toa;
 | 
			
		||||
			std::cerr << " d: " << std::setw(3) << std::setprecision(2) << (ebp.toa * 4) - va_burst_start;
 | 
			
		||||
			std::cerr << " ! " << float(va_burst_start + 19) / 4 << std::endl;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int main()
 | 
			
		||||
{
 | 
			
		||||
	convolve_init();
 | 
			
		||||
	convert_init();
 | 
			
		||||
	sigProcLibSetup();
 | 
			
		||||
	initvita();
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < 1; i++) {
 | 
			
		||||
		demod_real_burst(i);
 | 
			
		||||
		demod_generated_burst(CorrType::RACH);
 | 
			
		||||
		demod_generated_burst(CorrType::TSC);
 | 
			
		||||
		demod_test_offsets();
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										
											BIN
										
									
								
								utils/va-test/demodbits_tsc7.s8
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								utils/va-test/demodbits_tsc7.s8
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								utils/va-test/nb_chunk_tsc7.cfile
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								utils/va-test/nb_chunk_tsc7.cfile
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							
		Reference in New Issue
	
	Block a user