mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
				synced 2025-11-03 21:53:18 +00:00 
			
		
		
		
	Compare commits
	
		
			6 Commits
		
	
	
		
			mstx_oldtr
			...
			synctoy2
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					2f7b82f282 | ||
| 
						 | 
					53615c3ccd | ||
| 
						 | 
					d027037c09 | ||
| 
						 | 
					f2652f1640 | ||
| 
						 | 
					1b65c9278f | ||
| 
						 | 
					07cfdf79a8 | 
							
								
								
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -7,13 +7,10 @@ Transceiver52M/osmo-trx-usrp1
 | 
			
		||||
Transceiver52M/osmo-trx-lms
 | 
			
		||||
Transceiver52M/osmo-trx-ipc
 | 
			
		||||
Transceiver52M/osmo-trx-blade
 | 
			
		||||
Transceiver52M/osmo-trx-ipc2
 | 
			
		||||
Transceiver52M/osmo-trx-syncthing-blade
 | 
			
		||||
Transceiver52M/osmo-trx-syncthing-uhd
 | 
			
		||||
Transceiver52M/osmo-trx-syncthing-ipc
 | 
			
		||||
Transceiver52M/osmo-trx-ms-blade
 | 
			
		||||
Transceiver52M/osmo-trx-ms-uhd
 | 
			
		||||
Transceiver52M/osmo-trx-ms-ipc
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
.clang-format
 | 
			
		||||
 
 | 
			
		||||
@@ -74,9 +74,9 @@ COMMON_LDADD = \
 | 
			
		||||
	$(ARCH_LA) \
 | 
			
		||||
	$(GSM_LA) \
 | 
			
		||||
	$(COMMON_LA) \
 | 
			
		||||
	$(TRXCON_LA) \
 | 
			
		||||
	$(FFTWF_LIBS) \
 | 
			
		||||
	$(LIBOSMOCORE_LIBS) \
 | 
			
		||||
	$(LIBOSMOCODING_LIBS) \
 | 
			
		||||
	$(LIBOSMOCTRL_LIBS) \
 | 
			
		||||
	$(LIBOSMOVTY_LIBS)
 | 
			
		||||
 | 
			
		||||
@@ -101,11 +101,8 @@ osmo_trx_ms_uhd_LDADD = \
 | 
			
		||||
osmo_trx_ms_uhd_CPPFLAGS  = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DBUILDUHD
 | 
			
		||||
 | 
			
		||||
bin_PROGRAMS += osmo-trx-syncthing-uhd
 | 
			
		||||
osmo_trx_syncthing_uhd_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_burst.cpp
 | 
			
		||||
osmo_trx_syncthing_uhd_LDADD = \
 | 
			
		||||
	$(builddir)/device/bladerf/libdevice.la \
 | 
			
		||||
	$(COMMON_LDADD) \
 | 
			
		||||
	$(UHD_LIBS)
 | 
			
		||||
osmo_trx_syncthing_uhd_SOURCES = $(osmo_trx_ms_uhd_SOURCES) ms/ms_rx_burst.cpp
 | 
			
		||||
osmo_trx_syncthing_uhd_LDADD =  $(osmo_trx_ms_uhd_LDADD)
 | 
			
		||||
osmo_trx_syncthing_uhd_CPPFLAGS  = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DSYNCTHINGONLY -DBUILDUHD
 | 
			
		||||
#osmo_trx_syncthing_LDFLAGS  = -fsanitize=address,undefined -shared-libsan
 | 
			
		||||
endif
 | 
			
		||||
@@ -149,11 +146,8 @@ osmo_trx_ms_blade_LDADD = \
 | 
			
		||||
osmo_trx_ms_blade_CPPFLAGS  = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DBUILDBLADE
 | 
			
		||||
 | 
			
		||||
bin_PROGRAMS += osmo-trx-syncthing-blade
 | 
			
		||||
osmo_trx_syncthing_blade_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_burst.cpp
 | 
			
		||||
osmo_trx_syncthing_blade_LDADD =  \
 | 
			
		||||
	$(builddir)/device/bladerf/libdevice.la \
 | 
			
		||||
	$(COMMON_LDADD) \
 | 
			
		||||
	$(BLADE_LIBS)
 | 
			
		||||
osmo_trx_syncthing_blade_SOURCES = $(osmo_trx_ms_blade_SOURCES) ms/ms_rx_burst.cpp
 | 
			
		||||
osmo_trx_syncthing_blade_LDADD =  $(osmo_trx_ms_blade_LDADD)
 | 
			
		||||
osmo_trx_syncthing_blade_CPPFLAGS  = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DSYNCTHINGONLY -DBUILDBLADE -mcpu=cortex-a72 -mfloat-abi=hard -mfpu=neon-fp-armv8 -I../device/ipc
 | 
			
		||||
#osmo_trx_syncthing_LDFLAGS  = -fsanitize=address,undefined -shared-libsan
 | 
			
		||||
endif
 | 
			
		||||
@@ -181,7 +175,7 @@ osmo_trx_ms_ipc_LDADD = \
 | 
			
		||||
osmo_trx_ms_ipc_CPPFLAGS  = $(AM_CPPFLAGS) -DBUILDIPC  -I./device/ipc2 -I../device/ipc2
 | 
			
		||||
 | 
			
		||||
bin_PROGRAMS += osmo-trx-syncthing-ipc
 | 
			
		||||
osmo_trx_syncthing_ipc_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_burst.cpp
 | 
			
		||||
osmo_trx_syncthing_ipc_SOURCES = $(osmo_trx_ms_ipc_SOURCES) ms/ms_rx_burst.cpp
 | 
			
		||||
osmo_trx_syncthing_ipc_LDADD =  $(COMMON_LDADD)
 | 
			
		||||
osmo_trx_syncthing_ipc_CPPFLAGS  = $(AM_CPPFLAGS) -DSYNCTHINGONLY -DBUILDIPC  -I./device/ipc2 -I../device/ipc2
 | 
			
		||||
endif
 | 
			
		||||
@@ -191,6 +185,7 @@ noinst_HEADERS += \
 | 
			
		||||
	ms/bladerf_specific.h \
 | 
			
		||||
	ms/uhd_specific.h \
 | 
			
		||||
	ms/ms_rx_upper.h \
 | 
			
		||||
	ms/ms_state.h \
 | 
			
		||||
	itrq.h
 | 
			
		||||
# -fsanitize=address,undefined -shared-libsan -O0
 | 
			
		||||
#
 | 
			
		||||
 
 | 
			
		||||
@@ -427,6 +427,8 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
 | 
			
		||||
    ratectr_changed = false;
 | 
			
		||||
 | 
			
		||||
    zeros[i] = state->chanType[TN] == NONE || state->mMuted;
 | 
			
		||||
    if(zeros[i])
 | 
			
		||||
      LOGCHAN(i, DTRXDDL, FATAL) << "Z" << nowTime;
 | 
			
		||||
 | 
			
		||||
    Mutex *mtx = mTxPriorityQueues[i].getMutex();
 | 
			
		||||
    mtx->lock();
 | 
			
		||||
@@ -455,6 +457,7 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
 | 
			
		||||
    } else {
 | 
			
		||||
      modFN = nowTime.FN() % state->fillerModulus[TN];
 | 
			
		||||
      bursts[i] = state->fillerTable[modFN][TN];
 | 
			
		||||
      LOGCHAN(i, DTRXDDL, FATAL) << "F" << nowTime << ", retrans=" << state->mRetrans;
 | 
			
		||||
      if (i == 0 && state->mFiller == FILLER_ZERO) {
 | 
			
		||||
        LOGCHAN(i, DTRXDDL, INFO) << "No Tx burst available for " << nowTime
 | 
			
		||||
                                    << ", retrans=" << state->mRetrans;
 | 
			
		||||
@@ -871,7 +874,7 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
 | 
			
		||||
  LOGCHAN(chan, DTRXCTRL, INFO) << "command is '" << command << "'";
 | 
			
		||||
 | 
			
		||||
  if (match_cmd(command, "POWEROFF", NULL)) {
 | 
			
		||||
    stop();
 | 
			
		||||
    // stop();
 | 
			
		||||
    sprintf(response,"RSP POWEROFF 0");
 | 
			
		||||
  } else if (match_cmd(command, "POWERON", NULL)) {
 | 
			
		||||
    if (!start()) {
 | 
			
		||||
@@ -1137,7 +1140,7 @@ bool Transceiver::driveReceiveRadio()
 | 
			
		||||
  if (rc < 0)
 | 
			
		||||
    return false;
 | 
			
		||||
 | 
			
		||||
  if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
 | 
			
		||||
  if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(3,0)) {
 | 
			
		||||
    if (mForceClockInterface)
 | 
			
		||||
      LOGC(DTRXCLK, NOTICE) << "Sending CLOCK indications";
 | 
			
		||||
    mForceClockInterface = false;
 | 
			
		||||
 
 | 
			
		||||
@@ -205,6 +205,7 @@ TIMESTAMP IPCDevice2::initialReadTimestamp(void)
 | 
			
		||||
 | 
			
		||||
static timespec readtime, writetime;
 | 
			
		||||
static void wait_for_sample_time(timespec* last, unsigned int len) {
 | 
			
		||||
	#if 1
 | 
			
		||||
	timespec ts, diff;
 | 
			
		||||
	clock_gettime(CLOCK_MONOTONIC, &ts);
 | 
			
		||||
	timespecsub(&ts, last, &diff);
 | 
			
		||||
@@ -213,6 +214,9 @@ static void wait_for_sample_time(timespec* last, unsigned int len) {
 | 
			
		||||
	if(elapsed_us < max_wait_us)
 | 
			
		||||
		usleep(max_wait_us-elapsed_us);
 | 
			
		||||
	*last = ts;
 | 
			
		||||
	#else
 | 
			
		||||
	usleep(ONE_SAMPLE_DURATION_US * 625);
 | 
			
		||||
	#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NOTE: Assumes sequential reads
 | 
			
		||||
@@ -281,7 +285,7 @@ int IPCDevice2::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
 | 
			
		||||
	m.write_dl(len, timestamp, reinterpret_cast<sample_t *>(bufs[0]));
 | 
			
		||||
	wait_for_sample_time(&writetime, len);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
	return len;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool IPCDevice2::updateAlignment(TIMESTAMP timestamp)
 | 
			
		||||
 
 | 
			
		||||
@@ -18,12 +18,16 @@
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <complex>
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <deque>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
#include "shmif.h"
 | 
			
		||||
 | 
			
		||||
const int max_ul_rdlen = 1024 * 10;
 | 
			
		||||
@@ -32,44 +36,239 @@ using sample_t = std::complex<int16_t>;
 | 
			
		||||
struct shm_if {
 | 
			
		||||
	std::atomic<bool> ms_connected;
 | 
			
		||||
	struct {
 | 
			
		||||
		shm::shmmutex m;
 | 
			
		||||
		shm::shmcond c;
 | 
			
		||||
		shm::sema r;
 | 
			
		||||
		shm::sema w;
 | 
			
		||||
		std::atomic<uint64_t> ts;
 | 
			
		||||
		std::atomic<size_t> len_req; // <-
 | 
			
		||||
		std::atomic<size_t> len_written; // ->
 | 
			
		||||
		std::atomic<uint64_t> ts_req;
 | 
			
		||||
		std::atomic<size_t> len_written_sps; // ->
 | 
			
		||||
		sample_t buffer[max_ul_rdlen];
 | 
			
		||||
	} ul;
 | 
			
		||||
	struct {
 | 
			
		||||
		shm::shmmutex writemutex;
 | 
			
		||||
		shm::shmcond rdy2write;
 | 
			
		||||
		shm::shmmutex readmutex;
 | 
			
		||||
		shm::shmcond rdy2read;
 | 
			
		||||
		shm::sema r;
 | 
			
		||||
		shm::sema w;
 | 
			
		||||
		std::atomic<uint64_t> ts;
 | 
			
		||||
		std::atomic<size_t> len_req;
 | 
			
		||||
		std::atomic<size_t> len_written;
 | 
			
		||||
		std::atomic<uint64_t> ts_req;
 | 
			
		||||
		std::atomic<size_t> len_written_sps;
 | 
			
		||||
		sample_t buffer[max_dl_rdlen];
 | 
			
		||||
	} dl;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
// unique up to signed_type/2 diff
 | 
			
		||||
// ex: uint8/int8 (250, 0) = -6
 | 
			
		||||
template <typename A> auto unsigned_diff(A a, A b) -> typename std::make_signed<A>::type
 | 
			
		||||
{
 | 
			
		||||
	using stype = typename std::make_signed<A>::type;
 | 
			
		||||
	return (a > b) ? static_cast<stype>(a - b) : -static_cast<stype>(b - a);
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class trxmsif {
 | 
			
		||||
	shm::shm<shm_if> m;
 | 
			
		||||
	shm_if *ptr;
 | 
			
		||||
	int dl_readoffset;
 | 
			
		||||
constexpr inline int samp2byte(int v)
 | 
			
		||||
{
 | 
			
		||||
	return v * sizeof(sample_t);
 | 
			
		||||
}
 | 
			
		||||
constexpr inline int byte2samp(int v)
 | 
			
		||||
{
 | 
			
		||||
	return v / sizeof(sample_t);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	int samp2byte(int v)
 | 
			
		||||
struct ulentry {
 | 
			
		||||
	bool done;
 | 
			
		||||
	uint64_t ts;
 | 
			
		||||
	unsigned int len_in_sps;
 | 
			
		||||
	unsigned int read_pos_in_sps;
 | 
			
		||||
	sample_t buf[1000];
 | 
			
		||||
};
 | 
			
		||||
/*
 | 
			
		||||
		write: find read index +.. until marked free = "end" of current list
 | 
			
		||||
 | 
			
		||||
		check:
 | 
			
		||||
		within begin, end AND not free?
 | 
			
		||||
			y:
 | 
			
		||||
			copy (chunk)
 | 
			
		||||
				if chunk advance burst buf ptr
 | 
			
		||||
			n: next, advance, remove old.
 | 
			
		||||
		*/
 | 
			
		||||
template <unsigned int num_bursts> class ulburstprovider {
 | 
			
		||||
	std::mutex ul_q_m;
 | 
			
		||||
	// std::deque<ulentry> ul_q;
 | 
			
		||||
 | 
			
		||||
	// classic circular buffer
 | 
			
		||||
	ulentry foo[num_bursts];
 | 
			
		||||
	int current_index; // % num_bursts
 | 
			
		||||
 | 
			
		||||
	void cur_buf_done()
 | 
			
		||||
	{
 | 
			
		||||
		return v * sizeof(sample_t);
 | 
			
		||||
		foo[current_index].done = true;
 | 
			
		||||
		current_index = current_index + 1 % num_bursts;
 | 
			
		||||
	}
 | 
			
		||||
	bool is_empty()
 | 
			
		||||
	{
 | 
			
		||||
		return foo[current_index].done = true;
 | 
			
		||||
	}
 | 
			
		||||
	void reset()
 | 
			
		||||
	{
 | 
			
		||||
		for (auto &i : foo)
 | 
			
		||||
			i = {};
 | 
			
		||||
		current_index = 0;
 | 
			
		||||
	}
 | 
			
		||||
	ulentry &find_free_at_end()
 | 
			
		||||
	{
 | 
			
		||||
		for (int i = current_index, max_to_search = 0; max_to_search < num_bursts;
 | 
			
		||||
		     i = (i + 1 % num_bursts), max_to_search++) {
 | 
			
		||||
			if (foo[i].done)
 | 
			
		||||
				return foo[i];
 | 
			
		||||
		}
 | 
			
		||||
		return foo[0]; // FIXME actually broken, q full, wat do?
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void push_back(ulentry &e)
 | 
			
		||||
	{
 | 
			
		||||
		auto free_buf = find_free_at_end();
 | 
			
		||||
		free_buf = e;
 | 
			
		||||
		e.done = false;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
	trxmsif() : m("trx-ms-if"), dl_readoffset(0)
 | 
			
		||||
	void add(ulentry &e)
 | 
			
		||||
	{
 | 
			
		||||
		std::lock_guard<std::mutex> foo(ul_q_m);
 | 
			
		||||
		push_back(e);
 | 
			
		||||
	}
 | 
			
		||||
	void get(uint64_t requested_ts, unsigned int req_len_in_sps, sample_t *buf, unsigned int max_buf_write_len)
 | 
			
		||||
	{
 | 
			
		||||
		std::lock_guard<std::mutex> g(ul_q_m);
 | 
			
		||||
 | 
			
		||||
		/*
 | 
			
		||||
		1) if empty return
 | 
			
		||||
		2) if not empty prune stale bursts
 | 
			
		||||
		3) if only future bursts also return and zero buf
 | 
			
		||||
		*/
 | 
			
		||||
		for (int i = current_index, max_to_search = 0; max_to_search < num_bursts;
 | 
			
		||||
		     i = (i + 1 % num_bursts), max_to_search++) {
 | 
			
		||||
			auto cur_entry = foo[i];
 | 
			
		||||
			if (is_empty()) { // might be empty due to advance below!
 | 
			
		||||
				memset(buf, 0, samp2byte(req_len_in_sps));
 | 
			
		||||
				return;
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			if (cur_entry.ts + cur_entry.len_in_sps < requested_ts) { // remove late bursts
 | 
			
		||||
				if (i == current_index) // only advance if we are at the front
 | 
			
		||||
					cur_buf_done();
 | 
			
		||||
				else
 | 
			
		||||
					assert(true);
 | 
			
		||||
			} else if (cur_entry.ts >= requested_ts + byte2samp(max_buf_write_len)) { // not in range
 | 
			
		||||
				memset(buf, 0, samp2byte(req_len_in_sps));
 | 
			
		||||
				return;
 | 
			
		||||
 | 
			
		||||
				// FIXME: what about requested_ts <= entry.ts <= ts + reqlen?
 | 
			
		||||
			} else {
 | 
			
		||||
				// requested_ts <= cur_entry.ts <= requested_ts + byte2samp(max_write_len)
 | 
			
		||||
 | 
			
		||||
				auto before_sps = unsigned_diff(cur_entry.ts, requested_ts);
 | 
			
		||||
 | 
			
		||||
				// at least one whole buffer before our most recent "head" burst?
 | 
			
		||||
				// set 0, return.
 | 
			
		||||
				if (-before_sps >= byte2samp(max_buf_write_len)) {
 | 
			
		||||
					memset(buf, 0, samp2byte(req_len_in_sps));
 | 
			
		||||
					return;
 | 
			
		||||
				}
 | 
			
		||||
				// less than one full buffer before: pad 0
 | 
			
		||||
				auto to_pad_sps = -before_sps;
 | 
			
		||||
				memset(buf, 0, samp2byte(to_pad_sps));
 | 
			
		||||
				requested_ts += to_pad_sps;
 | 
			
		||||
				req_len_in_sps -= to_pad_sps;
 | 
			
		||||
 | 
			
		||||
				if (!req_len_in_sps)
 | 
			
		||||
					return;
 | 
			
		||||
 | 
			
		||||
				// actual burst data after possible 0 pad
 | 
			
		||||
				auto max_sps_to_write = std::min(cur_entry.len_in_sps, req_len_in_sps);
 | 
			
		||||
				memcpy(&buf[samp2byte(to_pad_sps)], cur_entry.buf, samp2byte(max_sps_to_write));
 | 
			
		||||
				requested_ts += max_sps_to_write;
 | 
			
		||||
				req_len_in_sps -= max_sps_to_write;
 | 
			
		||||
				cur_entry.read_pos_in_sps += max_sps_to_write;
 | 
			
		||||
 | 
			
		||||
				//this buf is done...
 | 
			
		||||
				if (cur_entry.read_pos_in_sps == cur_entry.len_in_sps) {
 | 
			
		||||
					cur_buf_done();
 | 
			
		||||
				}
 | 
			
		||||
 | 
			
		||||
				if (!req_len_in_sps)
 | 
			
		||||
					return;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class trxmsif {
 | 
			
		||||
	shm::shm<shm_if> m;
 | 
			
		||||
	shm_if *ptr;
 | 
			
		||||
 | 
			
		||||
	ulburstprovider<10> p;
 | 
			
		||||
 | 
			
		||||
	template <typename T> void read(T &direction, size_t howmany_sps, uint64_t *read_ts, sample_t *outbuf)
 | 
			
		||||
	{
 | 
			
		||||
		static int readoffset_sps;
 | 
			
		||||
		// auto &direction = ptr->dl;
 | 
			
		||||
		auto buf = &direction.buffer[0];
 | 
			
		||||
		size_t len_avail_sps = direction.len_written_sps.load();
 | 
			
		||||
 | 
			
		||||
		auto left_to_read = len_avail_sps - readoffset_sps;
 | 
			
		||||
 | 
			
		||||
		shm::mtx_log::print_guard() << "\tr @" << direction.ts.load() << " " << readoffset_sps << std::endl;
 | 
			
		||||
 | 
			
		||||
		// no data, wait for new buffer, maybe some data left afterwards
 | 
			
		||||
		if (!left_to_read) {
 | 
			
		||||
			assert(readoffset_sps == len_avail_sps);
 | 
			
		||||
			readoffset_sps = 0;
 | 
			
		||||
			direction.r.reset_unsafe();
 | 
			
		||||
			direction.ts_req = (*read_ts);
 | 
			
		||||
			direction.w.set(1);
 | 
			
		||||
			direction.r.wait_and_reset(1);
 | 
			
		||||
			assert(*read_ts != direction.ts.load());
 | 
			
		||||
			// shm::sema_guard g(dl.r, dl.w);
 | 
			
		||||
			*read_ts = direction.ts.load();
 | 
			
		||||
			len_avail_sps = direction.len_written_sps.load();
 | 
			
		||||
			readoffset_sps += howmany_sps;
 | 
			
		||||
			assert(len_avail_sps >= howmany_sps);
 | 
			
		||||
			memcpy(outbuf, buf, samp2byte(howmany_sps));
 | 
			
		||||
 | 
			
		||||
			shm::mtx_log::print_guard() << "\tr+ " << *read_ts << " " << howmany_sps << std::endl;
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		*read_ts = direction.ts.load() + readoffset_sps;
 | 
			
		||||
		left_to_read = len_avail_sps - readoffset_sps;
 | 
			
		||||
 | 
			
		||||
		// data left from prev read
 | 
			
		||||
		if (left_to_read >= howmany_sps) {
 | 
			
		||||
			memcpy(outbuf, &buf[readoffset_sps], samp2byte(howmany_sps));
 | 
			
		||||
			readoffset_sps += howmany_sps;
 | 
			
		||||
 | 
			
		||||
			shm::mtx_log::print_guard() << "\tr++ " << *read_ts << " " << howmany_sps << std::endl;
 | 
			
		||||
			return;
 | 
			
		||||
		} else {
 | 
			
		||||
			memcpy(outbuf, &buf[readoffset_sps], samp2byte(left_to_read));
 | 
			
		||||
			readoffset_sps = 0;
 | 
			
		||||
			auto still_left_to_read = howmany_sps - left_to_read;
 | 
			
		||||
			{
 | 
			
		||||
				direction.r.reset_unsafe();
 | 
			
		||||
				direction.ts_req = (*read_ts);
 | 
			
		||||
				direction.w.set(1);
 | 
			
		||||
				direction.r.wait_and_reset(1);
 | 
			
		||||
				assert(*read_ts != direction.ts.load());
 | 
			
		||||
				len_avail_sps = direction.len_written_sps.load();
 | 
			
		||||
				assert(len_avail_sps >= still_left_to_read);
 | 
			
		||||
				memcpy(&outbuf[left_to_read], buf, samp2byte(still_left_to_read));
 | 
			
		||||
				readoffset_sps += still_left_to_read;
 | 
			
		||||
				shm::mtx_log::print_guard()
 | 
			
		||||
					<< "\tr+++2 " << *read_ts << " " << howmany_sps << " " << still_left_to_read
 | 
			
		||||
					<< " new @" << direction.ts.load() << std::endl;
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
	trxmsif() : m("trx-ms-if")
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -84,78 +283,105 @@ class trxmsif {
 | 
			
		||||
		m.open();
 | 
			
		||||
		ptr = m.p();
 | 
			
		||||
		ptr->ms_connected = true;
 | 
			
		||||
		ptr->dl.w.set(1);
 | 
			
		||||
		return m.isgood();
 | 
			
		||||
	}
 | 
			
		||||
	bool good()
 | 
			
		||||
	{
 | 
			
		||||
		return m.isgood();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void write_dl(size_t howmany, uint64_t write_ts, sample_t *inbuf)
 | 
			
		||||
	bool is_connected()
 | 
			
		||||
	{
 | 
			
		||||
		auto &dl = ptr->dl;
 | 
			
		||||
		auto buf = &dl.buffer[0];
 | 
			
		||||
		// if (ptr->ms_connected != true)
 | 
			
		||||
		// 	return;
 | 
			
		||||
 | 
			
		||||
		assert(sizeof(dl.buffer) >= samp2byte(howmany));
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			shm::signal_guard g(dl.writemutex, dl.rdy2write, dl.rdy2read);
 | 
			
		||||
 | 
			
		||||
			memcpy(buf, inbuf, samp2byte(howmany));
 | 
			
		||||
			dl.ts = write_ts;
 | 
			
		||||
			dl.len_written = howmany;
 | 
			
		||||
		}
 | 
			
		||||
		return ptr->ms_connected == true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void read_dl(size_t howmany, uint64_t* read_ts, sample_t *outbuf)
 | 
			
		||||
	/* is being read from ms side */
 | 
			
		||||
	void read_dl(size_t howmany_sps, uint64_t *read_ts, sample_t *outbuf)
 | 
			
		||||
	{
 | 
			
		||||
		auto &dl = ptr->dl;
 | 
			
		||||
		auto buf = &dl.buffer[0];
 | 
			
		||||
		size_t len_avail = dl.len_written;
 | 
			
		||||
		uint64_t ts = dl.ts;
 | 
			
		||||
 | 
			
		||||
		auto left_to_read = len_avail - dl_readoffset;
 | 
			
		||||
 | 
			
		||||
		// no data, wait for new buffer, maybe some data left afterwards
 | 
			
		||||
		if (!left_to_read) {
 | 
			
		||||
			shm::signal_guard g(dl.readmutex, dl.rdy2read, dl.rdy2write);
 | 
			
		||||
			*read_ts = dl.ts;
 | 
			
		||||
			len_avail = dl.len_written;
 | 
			
		||||
			dl_readoffset += howmany;
 | 
			
		||||
			assert(len_avail >= howmany);
 | 
			
		||||
			memcpy(outbuf, buf, samp2byte(howmany));
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		*read_ts = dl.ts + dl_readoffset;
 | 
			
		||||
		left_to_read = len_avail - dl_readoffset;
 | 
			
		||||
 | 
			
		||||
		// data left from prev read
 | 
			
		||||
		if (left_to_read >= howmany) {
 | 
			
		||||
			memcpy(outbuf, buf, samp2byte(howmany));
 | 
			
		||||
			dl_readoffset += howmany;
 | 
			
		||||
			return;
 | 
			
		||||
		} else {
 | 
			
		||||
			memcpy(outbuf, buf, samp2byte(left_to_read));
 | 
			
		||||
			dl_readoffset = 0;
 | 
			
		||||
			auto still_left_to_read = howmany - left_to_read;
 | 
			
		||||
			{
 | 
			
		||||
				shm::signal_guard g(dl.readmutex, dl.rdy2read, dl.rdy2write);
 | 
			
		||||
				len_avail = dl.len_written;
 | 
			
		||||
				dl_readoffset += still_left_to_read;
 | 
			
		||||
				assert(len_avail >= still_left_to_read);
 | 
			
		||||
				memcpy(outbuf + left_to_read, buf, samp2byte(still_left_to_read));
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		return read(ptr->dl, howmany_sps, read_ts, outbuf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void read_ul(size_t howmany, uint64_t* read_ts, sample_t *outbuf)
 | 
			
		||||
	/* is being read from trx/network side */
 | 
			
		||||
	void read_ul(size_t howmany_sps, uint64_t *read_ts, sample_t *outbuf)
 | 
			
		||||
	{
 | 
			
		||||
		// if (ptr->ms_connected != true) {
 | 
			
		||||
		memset(outbuf, 0, samp2byte(howmany));
 | 
			
		||||
		return;
 | 
			
		||||
			memset(outbuf, 0, samp2byte(howmany_sps));
 | 
			
		||||
		// 	return;
 | 
			
		||||
		// }
 | 
			
		||||
		// return read(ptr->ul, howmany_sps, read_ts, outbuf);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void write_dl(size_t howmany_sps, uint64_t write_ts, sample_t *inbuf)
 | 
			
		||||
	{
 | 
			
		||||
		auto &dl = ptr->dl;
 | 
			
		||||
		auto buf = &dl.buffer[0];
 | 
			
		||||
		if (ptr->ms_connected != true)
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		assert(sizeof(dl.buffer) >= samp2byte(howmany_sps));
 | 
			
		||||
		// print_guard() << "####w " << std::endl;
 | 
			
		||||
 | 
			
		||||
		{
 | 
			
		||||
			shm::sema_wait_guard g(dl.w, dl.r);
 | 
			
		||||
 | 
			
		||||
			memcpy(buf, inbuf, samp2byte(howmany_sps));
 | 
			
		||||
			dl.ts.store(write_ts);
 | 
			
		||||
			dl.len_written_sps.store(howmany_sps);
 | 
			
		||||
		}
 | 
			
		||||
		shm::mtx_log::print_guard() << std::endl
 | 
			
		||||
					    << "####w+ " << write_ts << " " << howmany_sps << std::endl
 | 
			
		||||
					    << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void write_ul(size_t howmany_sps_sps, uint64_t write_ts, sample_t *inbuf)
 | 
			
		||||
	{
 | 
			
		||||
		auto &ul = ptr->ul;
 | 
			
		||||
		assert(sizeof(ul.buffer) >= samp2byte(howmany_sps_sps));
 | 
			
		||||
		// print_guard() << "####w " << std::endl;
 | 
			
		||||
 | 
			
		||||
		ulentry e;
 | 
			
		||||
		e.ts = write_ts;
 | 
			
		||||
		e.len_in_sps = howmany_sps_sps;
 | 
			
		||||
		e.done = false;
 | 
			
		||||
		e.read_pos_in_sps = 0;
 | 
			
		||||
		assert(sizeof(e.buf) >= samp2byte(howmany_sps_sps));
 | 
			
		||||
		memcpy(e.buf, inbuf, samp2byte(howmany_sps_sps));
 | 
			
		||||
		p.add(e);
 | 
			
		||||
 | 
			
		||||
		shm::mtx_log::print_guard() << std::endl
 | 
			
		||||
					    << "####q+ " << write_ts << " " << howmany_sps_sps << std::endl
 | 
			
		||||
					    << std::endl;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void drive_tx()
 | 
			
		||||
	{
 | 
			
		||||
		auto &ul = ptr->ul;
 | 
			
		||||
		auto buf = &ul.buffer[0];
 | 
			
		||||
		const auto max_write_len = sizeof(ul.buffer);
 | 
			
		||||
 | 
			
		||||
		// ul_q_m.lock();
 | 
			
		||||
		// ul_q.push_front(e);
 | 
			
		||||
		// ul_q_m.unlock();
 | 
			
		||||
		// ul.w.wait_and_reset();
 | 
			
		||||
 | 
			
		||||
		// no read waiting for a write
 | 
			
		||||
		if (!ul.w.check_unsafe(1))
 | 
			
		||||
			return;
 | 
			
		||||
 | 
			
		||||
		// FIXME: store written, notify after get!
 | 
			
		||||
 | 
			
		||||
		auto requested_ts = ul.ts_req.load();
 | 
			
		||||
 | 
			
		||||
		p.get(requested_ts, byte2samp(max_write_len), buf, max_write_len);
 | 
			
		||||
 | 
			
		||||
		// memset(buf, 0, max_write_len);
 | 
			
		||||
		ul.ts.store(requested_ts);
 | 
			
		||||
		ul.len_written_sps.store(byte2samp(max_write_len));
 | 
			
		||||
		ul.w.reset_unsafe();
 | 
			
		||||
		ul.r.set(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void signal_read_start()
 | 
			
		||||
	{ /* nop */
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -18,10 +18,15 @@
 | 
			
		||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 
 | 
			
		||||
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <atomic>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <mutex>
 | 
			
		||||
#include <sstream>
 | 
			
		||||
#include <unistd.h>
 | 
			
		||||
#include <sys/mman.h>
 | 
			
		||||
#include <sys/stat.h>
 | 
			
		||||
@@ -32,6 +37,36 @@
 | 
			
		||||
namespace shm
 | 
			
		||||
{
 | 
			
		||||
 | 
			
		||||
namespace mtx_log
 | 
			
		||||
{
 | 
			
		||||
#if defined(MTX_LOG_ENABLED)
 | 
			
		||||
	class print_guard : public std::ostringstream {
 | 
			
		||||
		static std::mutex thread_print_lock;
 | 
			
		||||
 | 
			
		||||
	    public:
 | 
			
		||||
		~print_guard()
 | 
			
		||||
		{
 | 
			
		||||
			std::lock_guard<std::mutex> guard(thread_print_lock);
 | 
			
		||||
			std::cerr << str();
 | 
			
		||||
		}
 | 
			
		||||
	};
 | 
			
		||||
 | 
			
		||||
#else
 | 
			
		||||
	struct print_guard {};
 | 
			
		||||
 | 
			
		||||
	template <typename T> constexpr print_guard operator<<(const print_guard dummy, T &&value)
 | 
			
		||||
	{
 | 
			
		||||
		return dummy;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	constexpr print_guard operator<<(const print_guard &dummy, std::ostream &(*f)(std::ostream &))
 | 
			
		||||
	{
 | 
			
		||||
		return dummy;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
} // namespace mtx_log
 | 
			
		||||
 | 
			
		||||
class shmmutex {
 | 
			
		||||
	pthread_mutex_t mutex;
 | 
			
		||||
 | 
			
		||||
@@ -70,6 +105,8 @@ class shmmutex {
 | 
			
		||||
	{
 | 
			
		||||
		return &mutex;
 | 
			
		||||
	}
 | 
			
		||||
	shmmutex(const shmmutex &) = delete;
 | 
			
		||||
	shmmutex &operator=(const shmmutex &) = delete;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class shmcond {
 | 
			
		||||
@@ -104,6 +141,142 @@ class shmcond {
 | 
			
		||||
	{
 | 
			
		||||
		pthread_cond_broadcast(&cond);
 | 
			
		||||
	}
 | 
			
		||||
	shmcond(const shmcond &) = delete;
 | 
			
		||||
	shmcond &operator=(const shmcond &) = delete;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class signal_guard {
 | 
			
		||||
	shmmutex &m;
 | 
			
		||||
	shmcond &s;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
	signal_guard() = delete;
 | 
			
		||||
	explicit signal_guard(shmmutex &m, shmcond &wait_for, shmcond &to_signal) : m(m), s(to_signal)
 | 
			
		||||
	{
 | 
			
		||||
		m.lock();
 | 
			
		||||
		wait_for.wait(&m);
 | 
			
		||||
	}
 | 
			
		||||
	~signal_guard()
 | 
			
		||||
	{
 | 
			
		||||
		s.signal();
 | 
			
		||||
		m.unlock();
 | 
			
		||||
	}
 | 
			
		||||
	signal_guard(const signal_guard &) = delete;
 | 
			
		||||
	signal_guard &operator=(const signal_guard &) = delete;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class mutex_guard {
 | 
			
		||||
	shmmutex &m;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
	mutex_guard() = delete;
 | 
			
		||||
	explicit mutex_guard(shmmutex &m) : m(m)
 | 
			
		||||
	{
 | 
			
		||||
		m.lock();
 | 
			
		||||
	}
 | 
			
		||||
	~mutex_guard()
 | 
			
		||||
	{
 | 
			
		||||
		m.unlock();
 | 
			
		||||
	}
 | 
			
		||||
	mutex_guard(const mutex_guard &) = delete;
 | 
			
		||||
	mutex_guard &operator=(const mutex_guard &) = delete;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class sema {
 | 
			
		||||
	std::atomic<int> value;
 | 
			
		||||
	shmmutex m;
 | 
			
		||||
	shmcond c;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
	sema() : value(0)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
	explicit sema(int v) : value(v)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void wait()
 | 
			
		||||
	{
 | 
			
		||||
		wait(1);
 | 
			
		||||
	}
 | 
			
		||||
	void wait(int v)
 | 
			
		||||
	{
 | 
			
		||||
		mtx_log::print_guard() << __FUNCTION__ << value << std::endl;
 | 
			
		||||
		mutex_guard g(m);
 | 
			
		||||
		assert(value <= v);
 | 
			
		||||
		while (value != v)
 | 
			
		||||
			c.wait(&m);
 | 
			
		||||
	}
 | 
			
		||||
	void wait_and_reset()
 | 
			
		||||
	{
 | 
			
		||||
		wait_and_reset(1);
 | 
			
		||||
	}
 | 
			
		||||
	void wait_and_reset(int v)
 | 
			
		||||
	{
 | 
			
		||||
		mtx_log::print_guard() << __FUNCTION__ << value << std::endl;
 | 
			
		||||
		mutex_guard g(m);
 | 
			
		||||
		assert(value <= v);
 | 
			
		||||
		while (value != v)
 | 
			
		||||
			c.wait(&m);
 | 
			
		||||
		value = 0;
 | 
			
		||||
	}
 | 
			
		||||
	void set()
 | 
			
		||||
	{
 | 
			
		||||
		set(1);
 | 
			
		||||
	}
 | 
			
		||||
	void set(int v)
 | 
			
		||||
	{
 | 
			
		||||
		mtx_log::print_guard() << __FUNCTION__ << value << std::endl;
 | 
			
		||||
		mutex_guard g(m);
 | 
			
		||||
		value = v;
 | 
			
		||||
		c.signal();
 | 
			
		||||
	}
 | 
			
		||||
	void reset_unsafe()
 | 
			
		||||
	{
 | 
			
		||||
		value = 0;
 | 
			
		||||
	}
 | 
			
		||||
	bool check_unsafe(int v)
 | 
			
		||||
	{
 | 
			
		||||
		return value == v;
 | 
			
		||||
	}
 | 
			
		||||
	sema(const sema &) = delete;
 | 
			
		||||
	sema &operator=(const sema &) = delete;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class sema_wait_guard {
 | 
			
		||||
	sema &a;
 | 
			
		||||
	sema &b;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
	sema_wait_guard() = delete;
 | 
			
		||||
	explicit sema_wait_guard(sema &wait, sema &signal) : a(wait), b(signal)
 | 
			
		||||
	{
 | 
			
		||||
		a.wait_and_reset(1);
 | 
			
		||||
	}
 | 
			
		||||
	~sema_wait_guard()
 | 
			
		||||
	{
 | 
			
		||||
		b.set(1);
 | 
			
		||||
	}
 | 
			
		||||
	sema_wait_guard(const sema_wait_guard &) = delete;
 | 
			
		||||
	sema_wait_guard &operator=(const sema_wait_guard &) = delete;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class sema_signal_guard {
 | 
			
		||||
	sema &a;
 | 
			
		||||
	sema &b;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
	sema_signal_guard() = delete;
 | 
			
		||||
	explicit sema_signal_guard(sema &wait, sema &signal) : a(wait), b(signal)
 | 
			
		||||
	{
 | 
			
		||||
		a.wait_and_reset(1);
 | 
			
		||||
	}
 | 
			
		||||
	~sema_signal_guard()
 | 
			
		||||
	{
 | 
			
		||||
		b.set(1);
 | 
			
		||||
	}
 | 
			
		||||
	sema_signal_guard(const sema_signal_guard &) = delete;
 | 
			
		||||
	sema_signal_guard &operator=(const sema_signal_guard &) = delete;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
template <typename IFT> class shm {
 | 
			
		||||
@@ -199,21 +372,4 @@ template <typename IFT> class shm {
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
class signal_guard {
 | 
			
		||||
	shmmutex &m;
 | 
			
		||||
	shmcond &s;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
	explicit signal_guard(shmmutex &m, shmcond &wait_for, shmcond &to_signal) : m(m), s(to_signal)
 | 
			
		||||
	{
 | 
			
		||||
		m.lock();
 | 
			
		||||
		wait_for.wait(&m);
 | 
			
		||||
	}
 | 
			
		||||
	~signal_guard()
 | 
			
		||||
	{
 | 
			
		||||
		s.signal();
 | 
			
		||||
		m.unlock();
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace shm
 | 
			
		||||
 
 | 
			
		||||
@@ -27,7 +27,6 @@
 | 
			
		||||
#endif
 | 
			
		||||
#include <complex>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#include <algorithm>
 | 
			
		||||
#include <string.h>
 | 
			
		||||
#include <iostream>
 | 
			
		||||
@@ -43,8 +42,8 @@ gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a S
 | 
			
		||||
gr_complex d_norm_training_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS]; ///<encoded training sequences of a normal and dummy burst
 | 
			
		||||
const int d_chan_imp_length = CHAN_IMP_RESP_LENGTH;
 | 
			
		||||
 | 
			
		||||
void initvita() {
 | 
			
		||||
 | 
			
		||||
void initvita()
 | 
			
		||||
{
 | 
			
		||||
	/**
 | 
			
		||||
	 * Prepare SCH sequence bits
 | 
			
		||||
	 *
 | 
			
		||||
@@ -52,8 +51,7 @@ 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);
 | 
			
		||||
 | 
			
		||||
@@ -63,21 +61,15 @@ void initvita() {
 | 
			
		||||
		 * If first bit of the sequence is 0
 | 
			
		||||
		 * => first symbol is 1, else -1
 | 
			
		||||
		 */
 | 
			
		||||
		gr_complex startpoint = train_seq[i][0] == 0 ?
 | 
			
		||||
			gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
 | 
			
		||||
		gmsk_mapper(train_seq[i], N_TRAIN_BITS,
 | 
			
		||||
			d_norm_training_seq[i], startpoint);
 | 
			
		||||
		gr_complex startpoint = train_seq[i][0] == 0 ? gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
 | 
			
		||||
		gmsk_mapper(train_seq[i], N_TRAIN_BITS, d_norm_training_seq[i], startpoint);
 | 
			
		||||
		for (auto &i : d_norm_training_seq[i])
 | 
			
		||||
			i = conj(i);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
MULTI_VER_TARGET_ATTR
 | 
			
		||||
void
 | 
			
		||||
detect_burst(const gr_complex* input,
 | 
			
		||||
	gr_complex* chan_imp_resp, int burst_start,
 | 
			
		||||
	unsigned char* output_binary)
 | 
			
		||||
void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, unsigned char *output_binary)
 | 
			
		||||
{
 | 
			
		||||
	std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
 | 
			
		||||
	unsigned int stop_states[2] = { 4, 12 };
 | 
			
		||||
@@ -86,40 +78,31 @@ detect_burst(const gr_complex* input,
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	autocorrelation(chan_imp_resp, &rhh_temp[0], d_chan_imp_length * d_OSR);
 | 
			
		||||
	for (int ii = 0; ii < d_chan_imp_length; ii++)
 | 
			
		||||
		rhh[ii] = conj(rhh_temp[ii * d_OSR]);
 | 
			
		||||
 | 
			
		||||
	mafi(&input[burst_start], BURST_SIZE, chan_imp_resp,
 | 
			
		||||
		d_chan_imp_length * d_OSR, filtered_burst);
 | 
			
		||||
	mafi(&input[burst_start], BURST_SIZE, chan_imp_resp, d_chan_imp_length * d_OSR, filtered_burst);
 | 
			
		||||
 | 
			
		||||
	viterbi_detector(filtered_burst, BURST_SIZE, rhh,
 | 
			
		||||
		start_state, stop_states, 2, output);
 | 
			
		||||
	viterbi_detector(filtered_burst, BURST_SIZE, rhh, start_state, stop_states, 2, output);
 | 
			
		||||
 | 
			
		||||
	for (int i = 0; i < BURST_SIZE; i++)
 | 
			
		||||
		output_binary[i] = output[i] > 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int process_vita_burst(gr_complex* input, int tsc, unsigned char* output_binary) {
 | 
			
		||||
int process_vita_burst(gr_complex *input, int tsc, unsigned char *output_binary)
 | 
			
		||||
{
 | 
			
		||||
	gr_complex channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
	int normal_burst_start, dummy_burst_start;
 | 
			
		||||
	float dummy_corr_max, normal_corr_max;
 | 
			
		||||
 | 
			
		||||
	dummy_burst_start = get_norm_chan_imp_resp(input,
 | 
			
		||||
		&channel_imp_resp[0], &dummy_corr_max, TS_DUMMY);
 | 
			
		||||
	normal_burst_start = get_norm_chan_imp_resp(input,
 | 
			
		||||
		&channel_imp_resp[0], &normal_corr_max, tsc);
 | 
			
		||||
	dummy_burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], &dummy_corr_max, TS_DUMMY);
 | 
			
		||||
	normal_burst_start = get_norm_chan_imp_resp(input, &channel_imp_resp[0], &normal_corr_max, tsc);
 | 
			
		||||
 | 
			
		||||
	if (normal_corr_max > dummy_corr_max) {
 | 
			
		||||
		/* Perform MLSE detection */
 | 
			
		||||
		detect_burst(input, &channel_imp_resp[0],
 | 
			
		||||
			normal_burst_start, output_binary);
 | 
			
		||||
		
 | 
			
		||||
		detect_burst(input, &channel_imp_resp[0], normal_burst_start, output_binary);
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	} else {
 | 
			
		||||
@@ -129,22 +112,20 @@ int process_vita_burst(gr_complex* input, int tsc, unsigned char* output_binary)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int process_vita_sc_burst(gr_complex* input, int tsc, unsigned char* output_binary, int* offset) {
 | 
			
		||||
int process_vita_sc_burst(gr_complex *input, int tsc, unsigned char *output_binary, int *offset)
 | 
			
		||||
{
 | 
			
		||||
	gr_complex channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
 | 
			
		||||
	/* Get channel impulse response */
 | 
			
		||||
	int d_c0_burst_start = get_sch_chan_imp_resp(input, &channel_imp_resp[0]);
 | 
			
		||||
	//	*offset = d_c0_burst_start;
 | 
			
		||||
	/* Perform MLSE detection */
 | 
			
		||||
	detect_burst(input, &channel_imp_resp[0],
 | 
			
		||||
	d_c0_burst_start, output_binary);
 | 
			
		||||
	detect_burst(input, &channel_imp_resp[0], d_c0_burst_start, output_binary);
 | 
			
		||||
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void
 | 
			
		||||
gmsk_mapper(const unsigned char* input,
 | 
			
		||||
	int nitems, gr_complex* gmsk_output, gr_complex start_point)
 | 
			
		||||
void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output, gr_complex start_point)
 | 
			
		||||
{
 | 
			
		||||
	gr_complex j = gr_complex(0.0, 1.0);
 | 
			
		||||
	gmsk_output[0] = start_point;
 | 
			
		||||
@@ -161,16 +142,13 @@ gmsk_mapper(const unsigned char* input,
 | 
			
		||||
		encoded_symbol = current_symbol * previous_symbol;
 | 
			
		||||
 | 
			
		||||
		/* And do GMSK mapping */
 | 
			
		||||
		gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0)
 | 
			
		||||
			* gmsk_output[i - 1];
 | 
			
		||||
		gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0) * gmsk_output[i - 1];
 | 
			
		||||
 | 
			
		||||
		previous_symbol = current_symbol;
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
gr_complex
 | 
			
		||||
correlate_sequence(const gr_complex* sequence,
 | 
			
		||||
	int length, const gr_complex* input)
 | 
			
		||||
gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_complex *input)
 | 
			
		||||
{
 | 
			
		||||
	gr_complex result(0.0, 0.0);
 | 
			
		||||
 | 
			
		||||
@@ -181,9 +159,7 @@ correlate_sequence(const gr_complex* sequence,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Computes autocorrelation for positive arguments */
 | 
			
		||||
inline void
 | 
			
		||||
autocorrelation(const gr_complex* input,
 | 
			
		||||
	gr_complex* out, int nitems)
 | 
			
		||||
inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems)
 | 
			
		||||
{
 | 
			
		||||
	for (int k = nitems - 1; k >= 0; k--) {
 | 
			
		||||
		out[k] = gr_complex(0, 0);
 | 
			
		||||
@@ -192,9 +168,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;
 | 
			
		||||
@@ -226,10 +200,12 @@ int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int se
 | 
			
		||||
 | 
			
		||||
	/* 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..
 | 
			
		||||
	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;
 | 
			
		||||
	iter_end = iter_end < power_buffer.end() ? iter_end : power_buffer.end(); //can end up out of range..
 | 
			
		||||
	while (iter != iter_end) {
 | 
			
		||||
		std::vector<float>::iterator iter_ii = iter;
 | 
			
		||||
		bool loop_end = false;
 | 
			
		||||
@@ -326,12 +302,12 @@ int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp)
 | 
			
		||||
 | 
			
		||||
int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max)
 | 
			
		||||
{
 | 
			
		||||
	const auto tseqlen = N_SYNC_BITS - (2 * TRAIN_BEGINNING);
 | 
			
		||||
	const int search_center = SYNC_POS + TRAIN_BEGINNING;
 | 
			
		||||
	const int search_start_pos = 0;
 | 
			
		||||
	// FIXME: proper end offset
 | 
			
		||||
	const int search_stop_pos = len - (N_SYNC_BITS*8);
 | 
			
		||||
	auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
 | 
			
		||||
	const int search_stop_pos = len - (N_SYNC_BITS * 8);
 | 
			
		||||
	const auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
 | 
			
		||||
	const auto tseqlen = N_SYNC_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);
 | 
			
		||||
 
 | 
			
		||||
@@ -249,7 +249,7 @@ template <typename T> struct blade_hw {
 | 
			
		||||
		blade_check(bladerf_open, &dev, "");
 | 
			
		||||
		if (!dev) {
 | 
			
		||||
			std::cerr << "open failed, device missing?" << std::endl;
 | 
			
		||||
			exit(0);
 | 
			
		||||
			return -1;
 | 
			
		||||
		}
 | 
			
		||||
		if (bladerf_device_speed(dev) != bladerf_dev_speed::BLADERF_DEVICE_SPEED_SUPER) {
 | 
			
		||||
			std::cerr << "open failed, only superspeed (usb3) supported!" << std::endl;
 | 
			
		||||
@@ -479,4 +479,7 @@ template <typename T> struct blade_hw {
 | 
			
		||||
			return exit(0);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	void signal_start()
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
@@ -21,6 +21,7 @@
 | 
			
		||||
 *
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <complex>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
#include <functional>
 | 
			
		||||
@@ -30,6 +31,7 @@
 | 
			
		||||
#include <Timeval.h>
 | 
			
		||||
#include <vector>
 | 
			
		||||
 | 
			
		||||
// #define MTX_LOG_ENABLED
 | 
			
		||||
#include <ipcif.h>
 | 
			
		||||
 | 
			
		||||
// typedef unsigned long long TIMESTAMP;
 | 
			
		||||
@@ -72,8 +74,6 @@ template <typename T> struct ipc_hw {
 | 
			
		||||
	// uhd::tx_streamer::sptr tx_stream;
 | 
			
		||||
	blade_sample_type *one_pkt_buf;
 | 
			
		||||
	std::vector<blade_sample_type *> pkt_ptrs;
 | 
			
		||||
	size_t rx_spp;
 | 
			
		||||
	double rxticks;
 | 
			
		||||
	const unsigned int rxFullScale, txFullScale;
 | 
			
		||||
	const int rxtxdelay;
 | 
			
		||||
	float rxgain, txgain;
 | 
			
		||||
@@ -83,7 +83,7 @@ template <typename T> struct ipc_hw {
 | 
			
		||||
	{
 | 
			
		||||
		delete[] one_pkt_buf;
 | 
			
		||||
	}
 | 
			
		||||
	ipc_hw() : rxFullScale(32767), txFullScale(32767), rxtxdelay(-67)
 | 
			
		||||
	ipc_hw() : rxFullScale(32767), txFullScale(32767), rxtxdelay(0)
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -137,13 +137,14 @@ template <typename T> struct ipc_hw {
 | 
			
		||||
		// 		osmo_select_main(0);
 | 
			
		||||
		// }).detach();
 | 
			
		||||
 | 
			
		||||
		return m.connect() ? 0: -1;
 | 
			
		||||
		return m.connect() ? 0 : -1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void *rx_cb(bh_fn_t burst_handler)
 | 
			
		||||
	{
 | 
			
		||||
		void *ret;
 | 
			
		||||
		static int to_skip = 0;
 | 
			
		||||
		static uint64_t last_ts;
 | 
			
		||||
 | 
			
		||||
		blade_sample_type pbuf[508 * 2];
 | 
			
		||||
 | 
			
		||||
@@ -151,42 +152,18 @@ template <typename T> struct ipc_hw {
 | 
			
		||||
 | 
			
		||||
		int len = 508 * 2;
 | 
			
		||||
		m.read_dl(508 * 2, &t, pbuf);
 | 
			
		||||
		// auto len = ipc_shm_read(ios_tx_to_device[0], (uint16_t *)&pbuf, 508 * 2, &t, 1);
 | 
			
		||||
		// if(len < 0) {
 | 
			
		||||
		// 	std::cerr << "fuck, rx fail!" << std::endl;
 | 
			
		||||
		// 	exit(0);
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		// uhd::rx_metadata_t md;
 | 
			
		||||
		// auto num_rx_samps = rx_stream->recv(pkt_ptrs.front(), rx_spp, md, 3.0, true);
 | 
			
		||||
 | 
			
		||||
		// if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) {
 | 
			
		||||
		// 	std::cerr << boost::format("Timeout while streaming") << std::endl;
 | 
			
		||||
		// 	exit(0);
 | 
			
		||||
		// }
 | 
			
		||||
		// if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW) {
 | 
			
		||||
		// 	std::cerr << boost::format("Got an overflow indication. Please consider the following:\n"
 | 
			
		||||
		// 				   "  Your write medium must sustain a rate of %fMB/s.\n"
 | 
			
		||||
		// 				   "  Dropped samples will not be written to the file.\n"
 | 
			
		||||
		// 				   "  Please modify this example for your purposes.\n"
 | 
			
		||||
		// 				   "  This message will not appear again.\n") %
 | 
			
		||||
		// 			     1.f;
 | 
			
		||||
		// 	exit(0);
 | 
			
		||||
		// 	;
 | 
			
		||||
		// }
 | 
			
		||||
		// if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) {
 | 
			
		||||
		// 	std::cerr << str(boost::format("Receiver error: %s") % md.strerror());
 | 
			
		||||
		// 	exit(0);
 | 
			
		||||
		// }
 | 
			
		||||
 | 
			
		||||
		dev_buf_t rcd = { t, static_cast<uint32_t>(len), pbuf };
 | 
			
		||||
 | 
			
		||||
		if (to_skip < 120) // prevents weird overflows on startup
 | 
			
		||||
			to_skip++;
 | 
			
		||||
		else {
 | 
			
		||||
			assert(last_ts != rcd.get_first_ts());
 | 
			
		||||
			burst_handler(&rcd);
 | 
			
		||||
			last_ts = rcd.get_first_ts();
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		m.drive_tx();
 | 
			
		||||
 | 
			
		||||
		return ret;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -194,11 +171,6 @@ template <typename T> struct ipc_hw {
 | 
			
		||||
	{
 | 
			
		||||
		auto fn = [this, burst_handler] {
 | 
			
		||||
			pthread_setname_np(pthread_self(), "rxrun");
 | 
			
		||||
			// wait_for_shm_open();
 | 
			
		||||
			// uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
 | 
			
		||||
			// stream_cmd.stream_now = true;
 | 
			
		||||
			// stream_cmd.time_spec = uhd::time_spec_t();
 | 
			
		||||
			// rx_stream->issue_stream_cmd(stream_cmd);
 | 
			
		||||
 | 
			
		||||
			while (1) {
 | 
			
		||||
				rx_cb(burst_handler);
 | 
			
		||||
@@ -256,4 +228,8 @@ template <typename T> struct ipc_hw {
 | 
			
		||||
		// 	return exit(0);
 | 
			
		||||
		// }
 | 
			
		||||
	}
 | 
			
		||||
	void signal_start()
 | 
			
		||||
	{
 | 
			
		||||
		m.signal_read_start();
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
@@ -20,8 +20,10 @@
 | 
			
		||||
 */
 | 
			
		||||
 | 
			
		||||
#include <radioInterface.h>
 | 
			
		||||
#include "l1if.h"
 | 
			
		||||
#include "ms_rx_upper.h"
 | 
			
		||||
#include "syncthing.h"
 | 
			
		||||
#include "ms_state.h"
 | 
			
		||||
 | 
			
		||||
void upper_trx::driveControl()
 | 
			
		||||
{
 | 
			
		||||
@@ -89,8 +91,6 @@ void upper_trx::commandhandler(char *buffer, char *response)
 | 
			
		||||
	} else if (strcmp(command, "POWEROFF") == 0) {
 | 
			
		||||
		set_ta(0);
 | 
			
		||||
		// turn off transmitter/demod
 | 
			
		||||
		// set_upper_ready(false);
 | 
			
		||||
 | 
			
		||||
		sprintf(response, "RSP POWEROFF 0");
 | 
			
		||||
	} else if (strcmp(command, "POWERON") == 0) {
 | 
			
		||||
		// turn on transmitter/demod
 | 
			
		||||
@@ -101,8 +101,7 @@ void upper_trx::commandhandler(char *buffer, char *response)
 | 
			
		||||
			if (!mOn) {
 | 
			
		||||
				// Prepare for thread start
 | 
			
		||||
				mPower = -20;
 | 
			
		||||
				// start_ms();
 | 
			
		||||
				set_upper_ready(true);
 | 
			
		||||
				start_ms();
 | 
			
		||||
 | 
			
		||||
				writeClockInterface();
 | 
			
		||||
				mOn = true;
 | 
			
		||||
@@ -197,6 +196,8 @@ void upper_trx::commandhandler(char *buffer, char *response)
 | 
			
		||||
			sprintf(response, "RSP SETSLOT 1 %d %d", timeslot, corrCode);
 | 
			
		||||
			return;
 | 
			
		||||
		}
 | 
			
		||||
		mStates.chanType[timeslot] = (ChannelCombination)corrCode;
 | 
			
		||||
		mStates.setModulus(timeslot);
 | 
			
		||||
		sprintf(response, "RSP SETSLOT 0 %d %d", timeslot, corrCode);
 | 
			
		||||
	} else if (!strcmp(command, "SETRXMASK")) {
 | 
			
		||||
		int slot;
 | 
			
		||||
@@ -210,6 +211,7 @@ void upper_trx::commandhandler(char *buffer, char *response)
 | 
			
		||||
		}
 | 
			
		||||
	} else if (!strcmp(command, "SYNC")) {
 | 
			
		||||
		// msleep(10);
 | 
			
		||||
		mStates.mode = trx_mode::TRX_MODE_MS_TRACK;
 | 
			
		||||
		sprintf(response, "RSP SYNC 0");
 | 
			
		||||
		mMaxExpectedDelay = 48;
 | 
			
		||||
		// setRxGain(30);
 | 
			
		||||
 
 | 
			
		||||
@@ -173,6 +173,7 @@ __attribute__((xray_always_instrument)) __attribute__((noinline)) void rcv_burst
 | 
			
		||||
	unsigned int fnbm = 0;
 | 
			
		||||
	signalVector burst(ONE_TS_BURST_LEN, 100, 100);
 | 
			
		||||
 | 
			
		||||
#if 0
 | 
			
		||||
	cpu_set_t cpuset;
 | 
			
		||||
 | 
			
		||||
	CPU_ZERO(&cpuset);
 | 
			
		||||
@@ -192,7 +193,7 @@ __attribute__((xray_always_instrument)) __attribute__((noinline)) void rcv_burst
 | 
			
		||||
		std::cerr << "scheduler: errreur! " << std::strerror(errno);
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
	while (1) {
 | 
			
		||||
		one_burst e;
 | 
			
		||||
		while (!q->spsc_pop(&e)) {
 | 
			
		||||
 
 | 
			
		||||
@@ -44,13 +44,13 @@ extern "C" {
 | 
			
		||||
#define DBGLG(...) std::cerr
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#if !defined(SYNCTHINGONLY) || !defined(NODAMNLOG)
 | 
			
		||||
#if !defined(SYNCTHINGONLY) //|| !defined(NODAMNLOG)
 | 
			
		||||
#define DBGLG2(...) ms_trx::dummy_log()
 | 
			
		||||
#else
 | 
			
		||||
#define DBGLG2(...) std::cerr
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
#define PRINT_Q_OVERFLOW
 | 
			
		||||
// #define PRINT_Q_OVERFLOW
 | 
			
		||||
__attribute__((xray_always_instrument)) __attribute__((noinline)) bool ms_trx::decode_sch(float *bits,
 | 
			
		||||
											  bool update_global_clock)
 | 
			
		||||
{
 | 
			
		||||
@@ -135,76 +135,61 @@ void ms_trx::maybe_update_gain(one_burst &brst)
 | 
			
		||||
	gain_check = (gain_check + 1) % avgburst_num;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static unsigned char sch_demod_bits[148];
 | 
			
		||||
 | 
			
		||||
bool ms_trx::handle_sch_or_nb()
 | 
			
		||||
bool ms_trx::handle_sch_or_nb(bool get_first_sch)
 | 
			
		||||
{
 | 
			
		||||
	one_burst brst;
 | 
			
		||||
	auto current_gsm_time = timekeeper.gsmtime();
 | 
			
		||||
	brst.gsmts = current_gsm_time;
 | 
			
		||||
	memcpy(brst.burst, burst_copy_buffer, sizeof(blade_sample_type) * ONE_TS_BURST_LEN);
 | 
			
		||||
	auto pushok = rxqueue.spsc_push(&brst);
 | 
			
		||||
#ifdef PRINT_Q_OVERFLOW
 | 
			
		||||
	if (!pushok)
 | 
			
		||||
		std::cout << "F" << std::endl;
 | 
			
		||||
	else
 | 
			
		||||
	 std::cout << "+" << std::endl;;
 | 
			
		||||
#endif
 | 
			
		||||
	if (do_auto_gain)
 | 
			
		||||
		maybe_update_gain(brst);
 | 
			
		||||
 | 
			
		||||
	// only continue for SCH, don't touch FCCH
 | 
			
		||||
	auto is_sch = gsm_sch_check_fn(current_gsm_time.FN()) && current_gsm_time.TN() == 0;
 | 
			
		||||
	auto is_fcch = gsm_fcch_check_fn(current_gsm_time.FN()) && current_gsm_time.TN() == 0;
 | 
			
		||||
#pragma unused(is_fcch)
 | 
			
		||||
 | 
			
		||||
	//either pass burst to upper layer for demod, OR pass demodded SCH to upper layer so we don't waste time processing it twice
 | 
			
		||||
	brst.gsmts = current_gsm_time;
 | 
			
		||||
 | 
			
		||||
	if (!is_sch) {
 | 
			
		||||
		memcpy(brst.burst, burst_copy_buffer, sizeof(blade_sample_type) * ONE_TS_BURST_LEN);
 | 
			
		||||
	} else {
 | 
			
		||||
		handle_sch(false);
 | 
			
		||||
		memcpy(brst.sch_bits, sch_demod_bits, sizeof(sch_demod_bits));
 | 
			
		||||
		// sched_yield();
 | 
			
		||||
		return false;
 | 
			
		||||
	}
 | 
			
		||||
	// auto pushok = rxqueue.spsc_push(&brst);
 | 
			
		||||
#ifndef SYNCTHINGONLY
 | 
			
		||||
	if (upper_is_ready) { // this is blocking, so only submit if there is a reader - only if upper exists!
 | 
			
		||||
#endif
 | 
			
		||||
		while (!rxqueue.spsc_push(&brst))
 | 
			
		||||
			;
 | 
			
		||||
#ifndef SYNCTHINGONLY
 | 
			
		||||
	}
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
	// #ifdef PRINT_Q_OVERFLOW
 | 
			
		||||
	// 	if (!pushok)
 | 
			
		||||
	// 		std::cout << "F" << std::endl;
 | 
			
		||||
	// #endif
 | 
			
		||||
	if (do_auto_gain)
 | 
			
		||||
		maybe_update_gain(brst);
 | 
			
		||||
 | 
			
		||||
	return false;
 | 
			
		||||
	auto rv = handle_sch(false);
 | 
			
		||||
	// sched_yield();
 | 
			
		||||
	return rv;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static float sch_acq_buffer[SCH_LEN_SPS * 2];
 | 
			
		||||
float sch_acq_buffer[SCH_LEN_SPS * 2];
 | 
			
		||||
 | 
			
		||||
bool ms_trx::handle_sch(bool is_first_sch_acq)
 | 
			
		||||
{
 | 
			
		||||
	struct estim_burst_params ebp;
 | 
			
		||||
	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;
 | 
			
		||||
	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];
 | 
			
		||||
	const auto which_buffer = is_first_sch_acq ? first_sch_buf : burst_copy_buffer;
 | 
			
		||||
 | 
			
		||||
	std::complex<float> channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
	unsigned char outbin[148];
 | 
			
		||||
	float max_corr = 0;
 | 
			
		||||
	int start;
 | 
			
		||||
	memset((void *)&sch_acq_buffer[0], 0, sizeof(sch_acq_buffer));
 | 
			
		||||
	if (is_first_sch_acq) {
 | 
			
		||||
		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);
 | 
			
		||||
	}
 | 
			
		||||
	const auto ss = reinterpret_cast<std::complex<float> *>(&sch_acq_buffer[0]);
 | 
			
		||||
	convert_and_scale<float, int16_t>(sch_acq_buffer, which_buffer, buf_len * 2, 1.f / float(rxFullScale));
 | 
			
		||||
 | 
			
		||||
	auto start = is_first_sch_acq ? get_sch_buffer_chan_imp_resp(ss, &channel_imp_resp[0], buf_len, &max_corr) :
 | 
			
		||||
					get_sch_chan_imp_resp(ss, channel_imp_resp);
 | 
			
		||||
	if(start <0)
 | 
			
		||||
		std::cerr << "wtf start" << start << (is_first_sch_acq ? "FIRST" : "") << std::endl;
 | 
			
		||||
	auto clamped_start = start < 0 ? 0 : start;
 | 
			
		||||
	detect_burst(&ss[0], &channel_imp_resp[0], clamped_start, outbin);
 | 
			
		||||
 | 
			
		||||
	SoftVector bitss(148);
 | 
			
		||||
	for (int i = 0; i < 148; i++) {
 | 
			
		||||
		bitss[i] = (!sch_demod_bits[i]) < 1 ? -1 : 1;
 | 
			
		||||
		bitss[i] = (!outbin[i]) < 1 ? -1 : 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto sch_decode_success = decode_sch(bitss.begin(), is_first_sch_acq);
 | 
			
		||||
@@ -213,7 +198,7 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
 | 
			
		||||
		const auto ts_offset_symb = 0;
 | 
			
		||||
		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;
 | 
			
		||||
			first_sch_ts_start = first_sch_buf_rcv_ts + start - (ts_offset_symb * 4);
 | 
			
		||||
		} else if (abs(start) > 1) {
 | 
			
		||||
			// continuous sch tracking, only update if off too much
 | 
			
		||||
			temp_ts_corr_offset += -start;
 | 
			
		||||
 
 | 
			
		||||
@@ -21,31 +21,21 @@
 | 
			
		||||
 | 
			
		||||
#include "sigProcLib.h"
 | 
			
		||||
#include "syncthing.h"
 | 
			
		||||
#include "l1if.h"
 | 
			
		||||
#include <signalVector.h>
 | 
			
		||||
#include <radioVector.h>
 | 
			
		||||
#include <radioInterface.h>
 | 
			
		||||
#include "grgsm_vitac/grgsm_vitac.h"
 | 
			
		||||
#include "ms_state.h"
 | 
			
		||||
#include "ms_rx_upper.h"
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <osmocom/core/select.h>
 | 
			
		||||
#include "sch.h"
 | 
			
		||||
#include "convolve.h"
 | 
			
		||||
#include "convert.h"
 | 
			
		||||
#include "proto_trxd.h"
 | 
			
		||||
 | 
			
		||||
void __lsan_do_recoverable_leak_check();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
namespace trxcon
 | 
			
		||||
{
 | 
			
		||||
extern "C" {
 | 
			
		||||
#include <trxcon/trx_if.h>
 | 
			
		||||
}
 | 
			
		||||
trx_instance *trxcon_instance; // local handle
 | 
			
		||||
static tx_queue_t txq;
 | 
			
		||||
} // namespace trxcon
 | 
			
		||||
 | 
			
		||||
#ifdef LOG
 | 
			
		||||
#undef LOG
 | 
			
		||||
#define LOG(...) upper_trx::dummy_log()
 | 
			
		||||
@@ -53,6 +43,15 @@ static tx_queue_t txq;
 | 
			
		||||
 | 
			
		||||
void upper_trx::start_threads()
 | 
			
		||||
{
 | 
			
		||||
	thr_rx = std::thread([this] {
 | 
			
		||||
		set_name_aff_sched("upper_rx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5);
 | 
			
		||||
		while (1) {
 | 
			
		||||
			driveReceiveFIFO();
 | 
			
		||||
			pthread_testcancel();
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
	msleep(1);
 | 
			
		||||
 | 
			
		||||
	thr_control = std::thread([this] {
 | 
			
		||||
		set_name_aff_sched("upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR));
 | 
			
		||||
		while (1) {
 | 
			
		||||
@@ -69,26 +68,6 @@ void upper_trx::start_threads()
 | 
			
		||||
			pthread_testcancel();
 | 
			
		||||
		}
 | 
			
		||||
	});
 | 
			
		||||
 | 
			
		||||
	// atomic ensures data is not written to q until loop reads
 | 
			
		||||
	start_ms();
 | 
			
		||||
 | 
			
		||||
	set_name_aff_sched("upper_rx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_RR) - 5);
 | 
			
		||||
	while (1) {
 | 
			
		||||
		// set_upper_ready(true);
 | 
			
		||||
		driveReceiveFIFO();
 | 
			
		||||
		pthread_testcancel();
 | 
			
		||||
		osmo_select_main(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// std::thread([this] {
 | 
			
		||||
	// 	set_name_aff_sched("leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10);
 | 
			
		||||
 | 
			
		||||
	// 	while (1) {
 | 
			
		||||
	// 		std::this_thread::sleep_for(std::chrono::seconds{ 5 });
 | 
			
		||||
	// 		__lsan_do_recoverable_leak_check();
 | 
			
		||||
	// 	}
 | 
			
		||||
	// }).detach();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void upper_trx::start_ms()
 | 
			
		||||
@@ -96,65 +75,106 @@ void upper_trx::start_ms()
 | 
			
		||||
	ms_trx::start();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* Detect SCH synchronization sequence within a burst */
 | 
			
		||||
bool upper_trx::detectSCH(ms_TransceiverState *state, signalVector &burst, struct estim_burst_params *ebp)
 | 
			
		||||
{
 | 
			
		||||
	int shift;
 | 
			
		||||
	sch_detect_type full;
 | 
			
		||||
	float mag, threshold = 4.0;
 | 
			
		||||
 | 
			
		||||
	full = (state->mode == trx_mode::TRX_MODE_MS_TRACK) ? sch_detect_type::SCH_DETECT_NARROW :
 | 
			
		||||
							      sch_detect_type::SCH_DETECT_FULL;
 | 
			
		||||
 | 
			
		||||
	if (!detectSCHBurst(burst, threshold, rx_sps, full, ebp))
 | 
			
		||||
		return false;
 | 
			
		||||
 | 
			
		||||
	std::clog << "SCH : Timing offset     " << ebp->toa << " symbols" << std::endl;
 | 
			
		||||
 | 
			
		||||
	mag = fabsf(ebp->toa);
 | 
			
		||||
	if (mag < 1.0f)
 | 
			
		||||
		return true;
 | 
			
		||||
 | 
			
		||||
	shift = (int)(mag / 2.0f);
 | 
			
		||||
	if (!shift)
 | 
			
		||||
		shift++;
 | 
			
		||||
 | 
			
		||||
	shift = ebp->toa > 0 ? shift : -shift;
 | 
			
		||||
	std::clog << "SCH : shift ->     " << shift << " symbols" << std::endl;
 | 
			
		||||
	// mRadioInterface->applyOffset(shift);
 | 
			
		||||
	return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SoftVector *upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset) __attribute__((optnone))
 | 
			
		||||
{
 | 
			
		||||
	float pow, avg = 1.0;
 | 
			
		||||
	static SoftVector bits(148);
 | 
			
		||||
	static complex workbuf[40 + 625 + 40];
 | 
			
		||||
	static signalVector sv(workbuf, 40, 625);
 | 
			
		||||
 | 
			
		||||
	static signalVector sv(625, 40);
 | 
			
		||||
	signalVector *burst = &sv;
 | 
			
		||||
	GSM::Time burst_time;
 | 
			
		||||
	auto ss = reinterpret_cast<std::complex<float> *>(&workbuf[40]);
 | 
			
		||||
 | 
			
		||||
	memset((void *)&workbuf[0], 0, sizeof(workbuf));
 | 
			
		||||
	// assert(sv.begin() == &workbuf[40]);
 | 
			
		||||
 | 
			
		||||
	one_burst e;
 | 
			
		||||
	unsigned char outbin[148];
 | 
			
		||||
 | 
			
		||||
	std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
	std::stringstream dbgout;
 | 
			
		||||
 | 
			
		||||
	while (!rxqueue.spsc_pop(&e)) {
 | 
			
		||||
		rxqueue.spsc_prep_pop();
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto ss = reinterpret_cast<std::complex<float> *>(burst->begin());
 | 
			
		||||
 | 
			
		||||
	convert_and_scale<float, int16_t>(burst->begin(), e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
 | 
			
		||||
 | 
			
		||||
	/* Set time and determine correlation type */
 | 
			
		||||
	burst_time = e.gsmts;
 | 
			
		||||
 | 
			
		||||
	wTime = burst_time;
 | 
			
		||||
 | 
			
		||||
	auto is_sch = (burst_time.TN() == 0 && gsm_sch_check_fn(burst_time.FN()));
 | 
			
		||||
	auto is_fcch = (burst_time.TN() == 0 && gsm_fcch_check_fn(burst_time.FN()));
 | 
			
		||||
	auto is_sch = [&]() { return (burst_time.TN() == 0 && gsm_sch_check_fn(burst_time.FN())); };
 | 
			
		||||
	auto is_fcch = [&]() { return (burst_time.TN() == 0 && gsm_fcch_check_fn(burst_time.FN())); };
 | 
			
		||||
 | 
			
		||||
	if (is_fcch) {
 | 
			
		||||
		// return trash
 | 
			
		||||
		// fprintf(stderr, "c %d\n",burst_time.FN());
 | 
			
		||||
		return &bits;
 | 
			
		||||
	CorrType type = TSC;
 | 
			
		||||
 | 
			
		||||
	// tickle UL by returning null bursts if demod is skipped due to unused TS
 | 
			
		||||
	switch (mStates.mode) {
 | 
			
		||||
	case trx_mode::TRX_MODE_MS_TRACK:
 | 
			
		||||
		if (mStates.chanType[burst_time.TN()] == ChannelCombination::NONE_INACTIVE) {
 | 
			
		||||
			type = OFF;
 | 
			
		||||
			goto release;
 | 
			
		||||
		} else if (is_sch())
 | 
			
		||||
			type = SCH;
 | 
			
		||||
		else if (!is_fcch()) // all ts0, but not fcch or sch..
 | 
			
		||||
			type = TSC;
 | 
			
		||||
		break;
 | 
			
		||||
 | 
			
		||||
	case trx_mode::TRX_MODE_OFF:
 | 
			
		||||
	default:
 | 
			
		||||
		goto release;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if (is_sch) {
 | 
			
		||||
		for (int i = 0; i < 148; i++)
 | 
			
		||||
			(bits)[i] = (!e.sch_bits[i]) < 1 ? -1 : 1;
 | 
			
		||||
		RSSI = 10;
 | 
			
		||||
		timingOffset = 0;
 | 
			
		||||
		// fprintf(stderr, "s %d\n", burst_time.FN());
 | 
			
		||||
		return &bits;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	auto ts = trxcon::trxcon_instance->ts_list[burst_time.TN()];
 | 
			
		||||
	if (ts == NULL || ts->mf_layout == NULL)
 | 
			
		||||
		return 0;
 | 
			
		||||
 | 
			
		||||
	convert_and_scale<float, int16_t>(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
 | 
			
		||||
 | 
			
		||||
	pow = energyDetect(sv, 20 * rx_sps);
 | 
			
		||||
	pow = energyDetect(*burst, 20 * rx_sps);
 | 
			
		||||
	if (pow < -1) {
 | 
			
		||||
		LOG(ALERT) << "Received empty burst";
 | 
			
		||||
		return NULL;
 | 
			
		||||
		goto release;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	avg = sqrt(pow);
 | 
			
		||||
	{
 | 
			
		||||
 | 
			
		||||
	if (type == SCH) {
 | 
			
		||||
		int d_c0_burst_start = get_sch_chan_imp_resp(ss, &chan_imp_resp[0]);
 | 
			
		||||
		detect_burst(ss, &chan_imp_resp[0], d_c0_burst_start, outbin);
 | 
			
		||||
 | 
			
		||||
		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;
 | 
			
		||||
 | 
			
		||||
		// std::cerr << dbgout.str();
 | 
			
		||||
	} else {
 | 
			
		||||
		float ncmax, dcmax;
 | 
			
		||||
		std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
		std::complex<float> chan_imp_resp2[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
			
		||||
		auto normal_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp[0], &ncmax, mTSC);
 | 
			
		||||
		auto dummy_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp2[0], &dcmax, TS_DUMMY);
 | 
			
		||||
@@ -163,22 +183,22 @@ SoftVector *upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingO
 | 
			
		||||
		// std::cerr << " U " << (is_nb ? "NB" : "DB") << "@ o nb: " << normal_burst_start
 | 
			
		||||
		// 	  << " o db: " << dummy_burst_start << std::endl;
 | 
			
		||||
 | 
			
		||||
		normal_burst_start = normal_burst_start < 39 ? normal_burst_start : 39;
 | 
			
		||||
		normal_burst_start = normal_burst_start > -39 ? normal_burst_start : -39;
 | 
			
		||||
 | 
			
		||||
		// fprintf(stderr, "%s %d\n", (is_nb ? "N":"D"), burst_time.FN());
 | 
			
		||||
		// if (is_nb)
 | 
			
		||||
		detect_burst(ss, &chan_imp_resp[0], normal_burst_start, outbin);
 | 
			
		||||
		// else
 | 
			
		||||
		// 	detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
 | 
			
		||||
		if (is_nb)
 | 
			
		||||
			detect_burst(ss, &chan_imp_resp[0], normal_burst_start, outbin);
 | 
			
		||||
		else
 | 
			
		||||
			detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
 | 
			
		||||
 | 
			
		||||
		for (int i = 0; i < 148; i++)
 | 
			
		||||
			(bits)[i] = (outbin[i]) < 1 ? -1 : 1;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	RSSI = (int)floor(20.0 * log10(rxFullScale / avg));
 | 
			
		||||
	timingOffset = (int)round(0);
 | 
			
		||||
 | 
			
		||||
	return &bits;
 | 
			
		||||
 | 
			
		||||
release:
 | 
			
		||||
	return NULL;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void upper_trx::driveReceiveFIFO()
 | 
			
		||||
@@ -186,45 +206,73 @@ void upper_trx::driveReceiveFIFO()
 | 
			
		||||
	int RSSI;
 | 
			
		||||
	int TOA; // in 1/256 of a symbol
 | 
			
		||||
	GSM::Time burstTime;
 | 
			
		||||
	SoftVector *rxBurst = pullRadioVector(burstTime, RSSI, TOA);
 | 
			
		||||
 | 
			
		||||
	if (!mOn)
 | 
			
		||||
		return;
 | 
			
		||||
 | 
			
		||||
	SoftVector *rxBurst = pullRadioVector(burstTime, RSSI, TOA);
 | 
			
		||||
	// _only_ return useless fcch trash to tickle trxcons tx path
 | 
			
		||||
	// auto is_fcch = [&burstTime]{ return burstTime.TN() == 0 && gsm_fcch_check_fn(burstTime.FN());};
 | 
			
		||||
	// if(!rxBurst && !is_fcch())
 | 
			
		||||
	// 	return;
 | 
			
		||||
 | 
			
		||||
	auto response = (trxd_from_trx *)calloc(1, sizeof(trxd_from_trx));
 | 
			
		||||
 | 
			
		||||
	response->ts = burstTime.TN();
 | 
			
		||||
	response->fn = htonl(burstTime.FN());
 | 
			
		||||
	response->rssi = RSSI;
 | 
			
		||||
	response->toa = htons(TOA);
 | 
			
		||||
	if (rxBurst) {
 | 
			
		||||
		trxd_from_trx response;
 | 
			
		||||
		response.ts = burstTime.TN();
 | 
			
		||||
		response.fn = htonl(burstTime.FN());
 | 
			
		||||
		response.rssi = RSSI;
 | 
			
		||||
		response.toa = htons(TOA);
 | 
			
		||||
 | 
			
		||||
		SoftVector::const_iterator burstItr = rxBurst->begin();
 | 
			
		||||
		if (burstTime.TN() == 0 && gsm_sch_check_fn(burstTime.FN())) {
 | 
			
		||||
		if (gsm_sch_check_fn(burstTime.FN())) {
 | 
			
		||||
			clamp_array(rxBurst->begin(), 148, 1.5f);
 | 
			
		||||
			for (unsigned int i = 0; i < gSlotLen; i++) {
 | 
			
		||||
				auto val = *burstItr++;
 | 
			
		||||
				auto vval = isnan(val) ? 0 : val;
 | 
			
		||||
				((int8_t *)response.symbols)[i] = round((vval - 0.5) * 64.0);
 | 
			
		||||
				((int8_t *)response->symbols)[i] = round((vval - 0.5) * 64.0);
 | 
			
		||||
			}
 | 
			
		||||
		} else {
 | 
			
		||||
			// invert and fix to +-127 sbits
 | 
			
		||||
			for (int i = 0; i < 148; i++)
 | 
			
		||||
				((int8_t *)response.symbols)[i] = *burstItr++ > 0.0f ? -127 : 127;
 | 
			
		||||
				((int8_t *)response->symbols)[i] = *burstItr++ > 0.0f ? -127 : 127;
 | 
			
		||||
		}
 | 
			
		||||
		trxcon::trx_data_rx_handler(trxcon::trxcon_instance, (uint8_t *)&response);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
#ifdef IPCIF
 | 
			
		||||
	push_d(response);
 | 
			
		||||
#else
 | 
			
		||||
	int rv = sendto(mDataSockets, response, sizeof(trxd_from_trx), 0, (struct sockaddr *)&datadest,
 | 
			
		||||
			sizeof(struct sockaddr_in));
 | 
			
		||||
	if (rv < 0) {
 | 
			
		||||
		std::cerr << "fuck, send?" << std::endl;
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
	free(response);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void upper_trx::driveTx()
 | 
			
		||||
{
 | 
			
		||||
	trxd_to_trx e;
 | 
			
		||||
	while (!trxcon::txq.spsc_pop(&e)) {
 | 
			
		||||
		trxcon::txq.spsc_prep_pop();
 | 
			
		||||
#ifdef IPCIF
 | 
			
		||||
	auto burst = pop_d();
 | 
			
		||||
	if (!burst) {
 | 
			
		||||
		// std::cerr << "wtf no tx burst?" << std::endl;
 | 
			
		||||
		// exit(0);
 | 
			
		||||
		continue;
 | 
			
		||||
	}
 | 
			
		||||
#else
 | 
			
		||||
	trxd_to_trx buffer;
 | 
			
		||||
 | 
			
		||||
	socklen_t addr_len = sizeof(datasrc);
 | 
			
		||||
	int rdln = recvfrom(mDataSockets, (void *)&buffer, sizeof(trxd_to_trx), 0, &datasrc, &addr_len);
 | 
			
		||||
	if (rdln < 0 && errno == EAGAIN) {
 | 
			
		||||
		std::cerr << "fuck, rcv?" << std::endl;
 | 
			
		||||
		exit(0);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	trxd_to_trx *burst = &e;
 | 
			
		||||
 | 
			
		||||
	trxd_to_trx *burst = &buffer;
 | 
			
		||||
#endif
 | 
			
		||||
	auto proper_fn = ntohl(burst->fn);
 | 
			
		||||
	// std::cerr << "got burst!" << proper_fn << ":" << burst->ts
 | 
			
		||||
	// 	  << " current: " << timekeeper.gsmtime().FN()
 | 
			
		||||
@@ -259,9 +307,30 @@ void upper_trx::driveTx()
 | 
			
		||||
	// memory read --binary --outfile /tmp/mem.bin &burst_buf[0] --count 2500 --force
 | 
			
		||||
 | 
			
		||||
	submit_burst(burst_buf, txburst->size(), currTime);
 | 
			
		||||
	delete txburst;
 | 
			
		||||
 | 
			
		||||
#ifdef IPCIF
 | 
			
		||||
	free(burst);
 | 
			
		||||
#endif
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// __attribute__((xray_always_instrument)) static void *rx_stream_callback(struct bladerf *dev,
 | 
			
		||||
// 									struct bladerf_stream *stream,
 | 
			
		||||
// 									struct bladerf_metadata *meta, void *samples,
 | 
			
		||||
// 									size_t num_samples, void *user_data)
 | 
			
		||||
// {
 | 
			
		||||
// 	struct ms_trx *trx = (struct ms_trx *)user_data;
 | 
			
		||||
// 	return trx->rx_cb(dev, stream, meta, samples, num_samples, user_data);
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
// __attribute__((xray_always_instrument)) static void *tx_stream_callback(struct bladerf *dev,
 | 
			
		||||
// 									struct bladerf_stream *stream,
 | 
			
		||||
// 									struct bladerf_metadata *meta, void *samples,
 | 
			
		||||
// 									size_t num_samples, void *user_data)
 | 
			
		||||
// {
 | 
			
		||||
// 	struct ms_trx *trx = (struct ms_trx *)user_data;
 | 
			
		||||
// 	return BLADERF_STREAM_NO_DATA;
 | 
			
		||||
// }
 | 
			
		||||
 | 
			
		||||
int trxc_main(int argc, char *argv[])
 | 
			
		||||
{
 | 
			
		||||
	pthread_setname_np(pthread_self(), "main_trxc");
 | 
			
		||||
@@ -281,21 +350,14 @@ int trxc_main(int argc, char *argv[])
 | 
			
		||||
	return status;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern "C" {
 | 
			
		||||
void init_external_transceiver(struct trx_instance *trx, int argc, char **argv)
 | 
			
		||||
extern "C" volatile bool gshutdown = false;
 | 
			
		||||
extern "C" void init_external_transceiver(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
	trxcon::trxcon_instance = (trxcon::trx_instance *)trx;
 | 
			
		||||
	std::cout << "init?" << std::endl;
 | 
			
		||||
	trxc_main(argc, argv);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void close_external_transceiver(int argc, char **argv)
 | 
			
		||||
extern "C" void stop_trx()
 | 
			
		||||
{
 | 
			
		||||
	std::cout << "Shutting down transceiver..." << std::endl;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void tx_external_transceiver(uint8_t *burst)
 | 
			
		||||
{
 | 
			
		||||
	trxcon::txq.spsc_push((trxd_to_trx *)burst);
 | 
			
		||||
}
 | 
			
		||||
}
 | 
			
		||||
@@ -28,12 +28,13 @@
 | 
			
		||||
#include "GSMCommon.h"
 | 
			
		||||
#include "radioClock.h"
 | 
			
		||||
#include "syncthing.h"
 | 
			
		||||
#include "l1if.h"
 | 
			
		||||
#include "ms_state.h"
 | 
			
		||||
 | 
			
		||||
using tx_queue_t = spsc_cond<8 * 1, trxd_to_trx, true, false>;
 | 
			
		||||
class upper_trx : public ms_trx {
 | 
			
		||||
	int rx_sps, tx_sps;
 | 
			
		||||
 | 
			
		||||
	ms_TransceiverState mStates;
 | 
			
		||||
 | 
			
		||||
	bool mOn; ///< flag to indicate that transceiver is powered on
 | 
			
		||||
	double mTxFreq; ///< the transmit frequency
 | 
			
		||||
	double mRxFreq; ///< the receive frequency
 | 
			
		||||
@@ -41,6 +42,9 @@ class upper_trx : public ms_trx {
 | 
			
		||||
	unsigned mMaxExpectedDelay; ///< maximum TOA offset in GSM symbols
 | 
			
		||||
	unsigned long long mRxSlotMask[8]; ///< MS - enabled multiframe slot mask
 | 
			
		||||
 | 
			
		||||
	int mDataSockets;
 | 
			
		||||
	sockaddr_in datadest;
 | 
			
		||||
	sockaddr datasrc;
 | 
			
		||||
	int mCtrlSockets;
 | 
			
		||||
	sockaddr_in ctrldest;
 | 
			
		||||
	sockaddr ctrlsrc;
 | 
			
		||||
@@ -94,6 +98,8 @@ class upper_trx : public ms_trx {
 | 
			
		||||
 | 
			
		||||
	SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
 | 
			
		||||
 | 
			
		||||
	bool detectSCH(ms_TransceiverState *state, signalVector &burst, struct estim_burst_params *ebp);
 | 
			
		||||
 | 
			
		||||
	std::thread thr_control, thr_rx, thr_tx;
 | 
			
		||||
 | 
			
		||||
    public:
 | 
			
		||||
@@ -104,8 +110,12 @@ class upper_trx : public ms_trx {
 | 
			
		||||
	{
 | 
			
		||||
		auto c_srcport = 6700 + 2 * 0 + 1;
 | 
			
		||||
		auto c_dstport = 6700 + 2 * 0 + 101;
 | 
			
		||||
		auto d_srcport = 6700 + 2 * 0 + 2;
 | 
			
		||||
		auto d_dstport = 6700 + 2 * 0 + 102;
 | 
			
		||||
 | 
			
		||||
		openudp(&mCtrlSockets, c_srcport, "127.0.0.1");
 | 
			
		||||
		openudp(&mDataSockets, d_srcport, "127.0.0.1");
 | 
			
		||||
		resolveAddress(&ctrldest, "127.0.0.1", c_dstport);
 | 
			
		||||
		resolveAddress(&datadest, "127.0.0.1", d_dstport);
 | 
			
		||||
	};
 | 
			
		||||
};
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										175
									
								
								Transceiver52M/ms/ms_state.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								Transceiver52M/ms/ms_state.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,175 @@
 | 
			
		||||
#pragma once
 | 
			
		||||
 | 
			
		||||
#include <radioVector.h>
 | 
			
		||||
#include <signalVector.h>
 | 
			
		||||
 | 
			
		||||
enum class trx_mode {
 | 
			
		||||
	TRX_MODE_OFF,
 | 
			
		||||
	TRX_MODE_BTS,
 | 
			
		||||
	TRX_MODE_MS_ACQUIRE,
 | 
			
		||||
	TRX_MODE_MS_TRACK,
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
enum class ChannelCombination {
 | 
			
		||||
	FILL, ///< Channel is transmitted, but unused
 | 
			
		||||
	I, ///< TCH/FS
 | 
			
		||||
	II, ///< TCH/HS, idle every other slot
 | 
			
		||||
	III, ///< TCH/HS
 | 
			
		||||
	IV, ///< FCCH+SCH+CCCH+BCCH, uplink RACH
 | 
			
		||||
	V, ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
 | 
			
		||||
	VI, ///< CCCH+BCCH, uplink RACH
 | 
			
		||||
	VII, ///< SDCCH/8 + SACCH/8
 | 
			
		||||
	VIII, ///< TCH/F + FACCH/F + SACCH/M
 | 
			
		||||
	IX, ///< TCH/F + SACCH/M
 | 
			
		||||
	X, ///< TCH/FD + SACCH/MD
 | 
			
		||||
	XI, ///< PBCCH+PCCCH+PDTCH+PACCH+PTCCH
 | 
			
		||||
	XII, ///< PCCCH+PDTCH+PACCH+PTCCH
 | 
			
		||||
	XIII, ///< PDTCH+PACCH+PTCCH
 | 
			
		||||
	NONE_INACTIVE, ///< Channel is inactive, default
 | 
			
		||||
	LOOPBACK ///< similar go VII, used in loopback testing
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
struct ms_TransceiverState {
 | 
			
		||||
	ms_TransceiverState() : mFreqOffsets(10), mode(trx_mode::TRX_MODE_OFF)
 | 
			
		||||
	{
 | 
			
		||||
		for (int i = 0; i < 8; i++) {
 | 
			
		||||
			chanType[i] = ChannelCombination::NONE_INACTIVE;
 | 
			
		||||
			fillerModulus[i] = 26;
 | 
			
		||||
 | 
			
		||||
			for (int n = 0; n < 102; n++)
 | 
			
		||||
				fillerTable[n][i] = nullptr;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	~ms_TransceiverState()
 | 
			
		||||
	{
 | 
			
		||||
		for (int i = 0; i < 8; i++) {
 | 
			
		||||
			for (int n = 0; n < 102; n++)
 | 
			
		||||
				delete fillerTable[n][i];
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	void setModulus(size_t timeslot)
 | 
			
		||||
	{
 | 
			
		||||
		switch (chanType[timeslot]) {
 | 
			
		||||
		case ChannelCombination::NONE_INACTIVE:
 | 
			
		||||
		case ChannelCombination::I:
 | 
			
		||||
		case ChannelCombination::II:
 | 
			
		||||
		case ChannelCombination::III:
 | 
			
		||||
		case ChannelCombination::FILL:
 | 
			
		||||
			fillerModulus[timeslot] = 26;
 | 
			
		||||
			break;
 | 
			
		||||
		case ChannelCombination::IV:
 | 
			
		||||
		case ChannelCombination::VI:
 | 
			
		||||
		case ChannelCombination::V:
 | 
			
		||||
			fillerModulus[timeslot] = 51;
 | 
			
		||||
			break;
 | 
			
		||||
			//case V:
 | 
			
		||||
		case ChannelCombination::VII:
 | 
			
		||||
			fillerModulus[timeslot] = 102;
 | 
			
		||||
			break;
 | 
			
		||||
		case ChannelCombination::XIII:
 | 
			
		||||
			fillerModulus[timeslot] = 52;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	CorrType expectedCorrType(GSM::Time currTime, unsigned long long *mRxSlotMask)
 | 
			
		||||
	{
 | 
			
		||||
		unsigned burstTN = currTime.TN();
 | 
			
		||||
		unsigned burstFN = currTime.FN();
 | 
			
		||||
 | 
			
		||||
		if (mode == trx_mode::TRX_MODE_MS_TRACK) {
 | 
			
		||||
			/* 102 modulus case currently unhandled */
 | 
			
		||||
			if (fillerModulus[burstTN] > 52)
 | 
			
		||||
				return OFF;
 | 
			
		||||
 | 
			
		||||
			int modFN = burstFN % fillerModulus[burstTN];
 | 
			
		||||
			unsigned long long reg = (unsigned long long)1 << modFN;
 | 
			
		||||
			if (reg & mRxSlotMask[burstTN])
 | 
			
		||||
				return TSC;
 | 
			
		||||
			else
 | 
			
		||||
				return OFF;
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		switch (chanType[burstTN]) {
 | 
			
		||||
		case ChannelCombination::NONE_INACTIVE:
 | 
			
		||||
			return OFF;
 | 
			
		||||
			break;
 | 
			
		||||
		case ChannelCombination::FILL:
 | 
			
		||||
			return IDLE;
 | 
			
		||||
			break;
 | 
			
		||||
		case ChannelCombination::I:
 | 
			
		||||
			return TSC;
 | 
			
		||||
			/*if (burstFN % 26 == 25) 
 | 
			
		||||
      return IDLE;
 | 
			
		||||
    else
 | 
			
		||||
      return TSC;*/
 | 
			
		||||
			break;
 | 
			
		||||
		case ChannelCombination::II:
 | 
			
		||||
			return TSC;
 | 
			
		||||
			break;
 | 
			
		||||
		case ChannelCombination::III:
 | 
			
		||||
			return TSC;
 | 
			
		||||
			break;
 | 
			
		||||
		case ChannelCombination::IV:
 | 
			
		||||
		case ChannelCombination::VI:
 | 
			
		||||
			return RACH;
 | 
			
		||||
			break;
 | 
			
		||||
		case ChannelCombination::V: {
 | 
			
		||||
			int mod51 = burstFN % 51;
 | 
			
		||||
			if ((mod51 <= 36) && (mod51 >= 14))
 | 
			
		||||
				return RACH;
 | 
			
		||||
			else if ((mod51 == 4) || (mod51 == 5))
 | 
			
		||||
				return RACH;
 | 
			
		||||
			else if ((mod51 == 45) || (mod51 == 46))
 | 
			
		||||
				return RACH;
 | 
			
		||||
			else
 | 
			
		||||
				return TSC;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case ChannelCombination::VII:
 | 
			
		||||
			if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
 | 
			
		||||
				return IDLE;
 | 
			
		||||
			else
 | 
			
		||||
				return TSC;
 | 
			
		||||
			break;
 | 
			
		||||
		case ChannelCombination::XIII: {
 | 
			
		||||
			int mod52 = burstFN % 52;
 | 
			
		||||
			if ((mod52 == 12) || (mod52 == 38))
 | 
			
		||||
				return RACH;
 | 
			
		||||
			else if ((mod52 == 25) || (mod52 == 51))
 | 
			
		||||
				return IDLE;
 | 
			
		||||
			else
 | 
			
		||||
				return TSC;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
		case ChannelCombination::LOOPBACK:
 | 
			
		||||
			if ((burstFN % 51 <= 50) && (burstFN % 51 >= 48))
 | 
			
		||||
				return IDLE;
 | 
			
		||||
			else
 | 
			
		||||
				return TSC;
 | 
			
		||||
			break;
 | 
			
		||||
		default:
 | 
			
		||||
			return OFF;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	/* Initialize a multiframe slot in the filler table */
 | 
			
		||||
	void init(size_t slot, signalVector *burst, bool fill);
 | 
			
		||||
 | 
			
		||||
	ChannelCombination chanType[8];
 | 
			
		||||
 | 
			
		||||
	/* The filler table */
 | 
			
		||||
	signalVector *fillerTable[102][8];
 | 
			
		||||
	int fillerModulus[8];
 | 
			
		||||
 | 
			
		||||
	/* Received noise energy levels */
 | 
			
		||||
	avgVector mFreqOffsets;
 | 
			
		||||
 | 
			
		||||
	/* Transceiver mode */
 | 
			
		||||
	trx_mode mode;
 | 
			
		||||
};
 | 
			
		||||
@@ -40,7 +40,9 @@ extern "C" {
 | 
			
		||||
#include "convolve.h"
 | 
			
		||||
#include "convert.h"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
#if defined (MTX_LOG_ENABLED)
 | 
			
		||||
std::mutex shm::mtx_log::print_guard::thread_print_lock;
 | 
			
		||||
#endif
 | 
			
		||||
dummylog ms_trx::dummy_log;
 | 
			
		||||
 | 
			
		||||
const int offset_start = -15;
 | 
			
		||||
@@ -218,6 +220,9 @@ int main(int argc, char *argv[])
 | 
			
		||||
		if (tx_flag)
 | 
			
		||||
			std::thread(tx_test, trx, &trx->ts_hitter_q, &trx->mTSC).detach();
 | 
			
		||||
		trx->start();
 | 
			
		||||
 | 
			
		||||
		usleep(1000*100);
 | 
			
		||||
		trx->signal_start();
 | 
			
		||||
		do {
 | 
			
		||||
			sleep(1);
 | 
			
		||||
		} while (1);
 | 
			
		||||
@@ -251,6 +256,7 @@ bh_fn_t ms_trx::rx_bh()
 | 
			
		||||
	return [this](dev_buf_t *rcd) -> int {
 | 
			
		||||
		if (this->search_for_sch(rcd) == SCH_STATE::FOUND)
 | 
			
		||||
			this->grab_bursts(rcd);
 | 
			
		||||
 | 
			
		||||
		return 0;
 | 
			
		||||
	};
 | 
			
		||||
}
 | 
			
		||||
@@ -276,11 +282,6 @@ void ms_trx::start()
 | 
			
		||||
	tx_task = std::thread(fn2);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ms_trx::set_upper_ready(bool is_ready)
 | 
			
		||||
{
 | 
			
		||||
	upper_is_ready = is_ready;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void ms_trx::stop_threads()
 | 
			
		||||
{
 | 
			
		||||
	std::cerr << "killing threads...\r\n" << std::endl;
 | 
			
		||||
 
 | 
			
		||||
@@ -46,7 +46,7 @@
 | 
			
		||||
#include "itrq.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 NUM_RXQ_FRAMES = 12 * 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)
 | 
			
		||||
@@ -69,17 +69,11 @@ template <typename DST_T, typename SRC_T> void convert_and_scale_default(void *d
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
struct one_burst {
 | 
			
		||||
	one_burst()
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
	GSM::Time gsmts;
 | 
			
		||||
	union {
 | 
			
		||||
		blade_sample_type burst[ONE_TS_BURST_LEN];
 | 
			
		||||
		unsigned char sch_bits[148];
 | 
			
		||||
	};
 | 
			
		||||
	blade_sample_type burst[ONE_TS_BURST_LEN];
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
using rx_queue_t = spsc_cond<8 * NUM_RXQ_FRAMES, one_burst, true, true>;
 | 
			
		||||
using rx_queue_t = spsc_cond<8 * NUM_RXQ_FRAMES, one_burst, true, false>;
 | 
			
		||||
 | 
			
		||||
enum class SCH_STATE { SEARCHING, FOUND };
 | 
			
		||||
 | 
			
		||||
@@ -195,10 +189,8 @@ struct ms_trx : public BASET {
 | 
			
		||||
	time_keeper timekeeper;
 | 
			
		||||
 | 
			
		||||
	void start();
 | 
			
		||||
	std::atomic<bool> upper_is_ready;
 | 
			
		||||
	void set_upper_ready(bool is_ready);
 | 
			
		||||
 | 
			
		||||
	bool handle_sch_or_nb();
 | 
			
		||||
	bool handle_sch_or_nb(bool first = false);
 | 
			
		||||
	bool handle_sch(bool first = false);
 | 
			
		||||
	bool decode_sch(float *bits, bool update_global_clock);
 | 
			
		||||
	SCH_STATE search_for_sch(dev_buf_t *rcd);
 | 
			
		||||
 
 | 
			
		||||
@@ -271,4 +271,7 @@ template <typename T> struct uhd_hw {
 | 
			
		||||
			return exit(0);
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	void signal_start()
 | 
			
		||||
	{
 | 
			
		||||
	}
 | 
			
		||||
};
 | 
			
		||||
@@ -602,9 +602,32 @@ rsp_error:
 | 
			
		||||
static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
{
 | 
			
		||||
	struct trx_instance *trx = ofd->data;
 | 
			
		||||
	struct trx_meas_set meas;
 | 
			
		||||
	uint8_t buf[TRXD_BUF_SIZE];
 | 
			
		||||
	sbit_t bits[148];
 | 
			
		||||
	int8_t rssi, tn;
 | 
			
		||||
	int16_t toa256;
 | 
			
		||||
	uint32_t fn;
 | 
			
		||||
	ssize_t read_len;
 | 
			
		||||
 | 
			
		||||
#ifdef IPCIF
 | 
			
		||||
	struct trxd_from_trx* rcvd = trxif_from_trx_d();
 | 
			
		||||
	if (!rcvd) {
 | 
			
		||||
		LOGP(DTRX, LOGL_ERROR, "read() failed with rc=%zd\n", rcvd);
 | 
			
		||||
		return rcvd;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	tn = rcvd->ts;
 | 
			
		||||
	fn = rcvd->fn;
 | 
			
		||||
	rssi = -(int8_t) rcvd->rssi;
 | 
			
		||||
	toa256 = (int16_t) rcvd->toa;
 | 
			
		||||
 | 
			
		||||
	/* Copy and convert bits {254..0} to sbits {-127..127} */
 | 
			
		||||
	//osmo_ubit2sbit(bits, rcvd->symbols, 148);
 | 
			
		||||
	memcpy(bits, rcvd->symbols, 148);
 | 
			
		||||
 | 
			
		||||
	free(rcvd);
 | 
			
		||||
#else
 | 
			
		||||
	read_len = read(ofd->fd, buf, sizeof(buf));
 | 
			
		||||
	if (read_len <= 0) {
 | 
			
		||||
		LOGP(DTRXD, LOGL_ERROR, "read() failed with rc=%zd\n", read_len);
 | 
			
		||||
@@ -618,18 +641,7 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
 | 
			
		||||
		     read_len);
 | 
			
		||||
		return -EINVAL;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return trx_data_rx_handler(trx, buf);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
int trx_data_rx_handler(struct trx_instance *trx, uint8_t *buf)
 | 
			
		||||
{
 | 
			
		||||
	struct trx_meas_set meas;
 | 
			
		||||
	sbit_t bits[148];
 | 
			
		||||
	int8_t rssi, tn;
 | 
			
		||||
	int16_t toa256;
 | 
			
		||||
	uint32_t fn;
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
	tn = buf[0];
 | 
			
		||||
	fn = osmo_load32be(buf + 1);
 | 
			
		||||
	rssi = -(int8_t)buf[5];
 | 
			
		||||
@@ -669,8 +681,6 @@ int trx_data_rx_handler(struct trx_instance *trx, uint8_t *buf)
 | 
			
		||||
	return 0;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern void tx_external_transceiver(uint8_t *burst) __attribute__((weak));
 | 
			
		||||
 | 
			
		||||
int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
 | 
			
		||||
	uint8_t pwr, const ubit_t *bits)
 | 
			
		||||
{
 | 
			
		||||
@@ -709,10 +719,7 @@ int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
 | 
			
		||||
	memcpy(buf + 6, bits, 148);
 | 
			
		||||
 | 
			
		||||
	/* Send data to transceiver */
 | 
			
		||||
	if (tx_external_transceiver)
 | 
			
		||||
		tx_external_transceiver(buf);
 | 
			
		||||
	else
 | 
			
		||||
		send(trx->trx_ofd_data.fd, buf, 154, 0);
 | 
			
		||||
	send(trx->trx_ofd_data.fd, buf, 154, 0);
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
	return 0;
 | 
			
		||||
 
 | 
			
		||||
@@ -84,4 +84,3 @@ int trx_if_cmd_measure(struct trx_instance *trx,
 | 
			
		||||
 | 
			
		||||
int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
 | 
			
		||||
	uint8_t pwr, const ubit_t *bits);
 | 
			
		||||
int trx_data_rx_handler(struct trx_instance *trx, uint8_t *buf);
 | 
			
		||||
@@ -271,8 +271,9 @@ static void signal_handler(int signum)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
extern void init_external_transceiver(struct trx_instance *trx, int argc, char **argv) __attribute__((weak));
 | 
			
		||||
extern void close_external_transceiver(int argc, char **argv) __attribute__((weak));
 | 
			
		||||
extern void init_external_transceiver(int argc, char **argv);
 | 
			
		||||
extern void stop_trx();
 | 
			
		||||
extern volatile bool gshutdown;
 | 
			
		||||
 | 
			
		||||
int main(int argc, char **argv)
 | 
			
		||||
{
 | 
			
		||||
@@ -371,14 +372,14 @@ int main(int argc, char **argv)
 | 
			
		||||
	/* Initialize pseudo-random generator */
 | 
			
		||||
	srand(time(NULL));
 | 
			
		||||
 | 
			
		||||
	if (init_external_transceiver)
 | 
			
		||||
		init_external_transceiver(app_data.trx, argc, argv);
 | 
			
		||||
	else
 | 
			
		||||
		while (!app_data.quit)
 | 
			
		||||
			osmo_select_main(0);
 | 
			
		||||
	init_external_transceiver(argc, argv);
 | 
			
		||||
 | 
			
		||||
	while (!app_data.quit)
 | 
			
		||||
		osmo_select_main(0);
 | 
			
		||||
 | 
			
		||||
	gshutdown = true;
 | 
			
		||||
	stop_trx();
 | 
			
		||||
 | 
			
		||||
	if (close_external_transceiver)
 | 
			
		||||
		close_external_transceiver(argc, argv);
 | 
			
		||||
 | 
			
		||||
exit:
 | 
			
		||||
	/* Close active connections */
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user