mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-03 05:33:16 +00:00
Compare commits
3 Commits
synctoy2
...
mstx_oldtr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2ecd9f698f | ||
|
|
70bd9415a2 | ||
|
|
2c12b30ace |
3
.gitignore
vendored
3
.gitignore
vendored
@@ -7,10 +7,13 @@ 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,8 +101,11 @@ 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 = $(osmo_trx_ms_uhd_SOURCES) ms/ms_rx_burst.cpp
|
||||
osmo_trx_syncthing_uhd_LDADD = $(osmo_trx_ms_uhd_LDADD)
|
||||
osmo_trx_syncthing_uhd_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_burst.cpp
|
||||
osmo_trx_syncthing_uhd_LDADD = \
|
||||
$(builddir)/device/bladerf/libdevice.la \
|
||||
$(COMMON_LDADD) \
|
||||
$(UHD_LIBS)
|
||||
osmo_trx_syncthing_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DSYNCTHINGONLY -DBUILDUHD
|
||||
#osmo_trx_syncthing_LDFLAGS = -fsanitize=address,undefined -shared-libsan
|
||||
endif
|
||||
@@ -146,8 +149,11 @@ 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 = $(osmo_trx_ms_blade_SOURCES) ms/ms_rx_burst.cpp
|
||||
osmo_trx_syncthing_blade_LDADD = $(osmo_trx_ms_blade_LDADD)
|
||||
osmo_trx_syncthing_blade_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_burst.cpp
|
||||
osmo_trx_syncthing_blade_LDADD = \
|
||||
$(builddir)/device/bladerf/libdevice.la \
|
||||
$(COMMON_LDADD) \
|
||||
$(BLADE_LIBS)
|
||||
osmo_trx_syncthing_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DSYNCTHINGONLY -DBUILDBLADE -mcpu=cortex-a72 -mfloat-abi=hard -mfpu=neon-fp-armv8 -I../device/ipc
|
||||
#osmo_trx_syncthing_LDFLAGS = -fsanitize=address,undefined -shared-libsan
|
||||
endif
|
||||
@@ -175,7 +181,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 = $(osmo_trx_ms_ipc_SOURCES) ms/ms_rx_burst.cpp
|
||||
osmo_trx_syncthing_ipc_SOURCES = ms/syncthing.cpp ms/ms_rx_lower.cpp ms/ms_rx_burst.cpp
|
||||
osmo_trx_syncthing_ipc_LDADD = $(COMMON_LDADD)
|
||||
osmo_trx_syncthing_ipc_CPPFLAGS = $(AM_CPPFLAGS) -DSYNCTHINGONLY -DBUILDIPC -I./device/ipc2 -I../device/ipc2
|
||||
endif
|
||||
@@ -185,7 +191,6 @@ 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,8 +427,6 @@ 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();
|
||||
@@ -457,7 +455,6 @@ 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;
|
||||
@@ -874,7 +871,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()) {
|
||||
@@ -1140,7 +1137,7 @@ bool Transceiver::driveReceiveRadio()
|
||||
if (rc < 0)
|
||||
return false;
|
||||
|
||||
if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(3,0)) {
|
||||
if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) {
|
||||
if (mForceClockInterface)
|
||||
LOGC(DTRXCLK, NOTICE) << "Sending CLOCK indications";
|
||||
mForceClockInterface = false;
|
||||
|
||||
@@ -205,7 +205,6 @@ 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);
|
||||
@@ -214,9 +213,6 @@ 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
|
||||
@@ -285,7 +281,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 len;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool IPCDevice2::updateAlignment(TIMESTAMP timestamp)
|
||||
|
||||
@@ -18,16 +18,12 @@
|
||||
* 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;
|
||||
@@ -36,239 +32,44 @@ using sample_t = std::complex<int16_t>;
|
||||
struct shm_if {
|
||||
std::atomic<bool> ms_connected;
|
||||
struct {
|
||||
shm::sema r;
|
||||
shm::sema w;
|
||||
shm::shmmutex m;
|
||||
shm::shmcond c;
|
||||
std::atomic<uint64_t> ts;
|
||||
std::atomic<uint64_t> ts_req;
|
||||
std::atomic<size_t> len_written_sps; // ->
|
||||
std::atomic<size_t> len_req; // <-
|
||||
std::atomic<size_t> len_written; // ->
|
||||
sample_t buffer[max_ul_rdlen];
|
||||
} ul;
|
||||
struct {
|
||||
shm::sema r;
|
||||
shm::sema w;
|
||||
shm::shmmutex writemutex;
|
||||
shm::shmcond rdy2write;
|
||||
shm::shmmutex readmutex;
|
||||
shm::shmcond rdy2read;
|
||||
std::atomic<uint64_t> ts;
|
||||
std::atomic<uint64_t> ts_req;
|
||||
std::atomic<size_t> len_written_sps;
|
||||
std::atomic<size_t> len_req;
|
||||
std::atomic<size_t> len_written;
|
||||
sample_t buffer[max_dl_rdlen];
|
||||
} dl;
|
||||
};
|
||||
|
||||
// unique up to signed_type/2 diff
|
||||
// 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);
|
||||
};
|
||||
|
||||
constexpr inline int samp2byte(int v)
|
||||
{
|
||||
return v * sizeof(sample_t);
|
||||
}
|
||||
constexpr inline int byte2samp(int v)
|
||||
{
|
||||
return v / sizeof(sample_t);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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:
|
||||
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;
|
||||
int dl_readoffset;
|
||||
|
||||
ulburstprovider<10> p;
|
||||
|
||||
template <typename T> void read(T &direction, size_t howmany_sps, uint64_t *read_ts, sample_t *outbuf)
|
||||
int samp2byte(int v)
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
return v * sizeof(sample_t);
|
||||
}
|
||||
|
||||
public:
|
||||
trxmsif() : m("trx-ms-if")
|
||||
trxmsif() : m("trx-ms-if"), dl_readoffset(0)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -283,105 +84,78 @@ class trxmsif {
|
||||
m.open();
|
||||
ptr = m.p();
|
||||
ptr->ms_connected = true;
|
||||
ptr->dl.w.set(1);
|
||||
return m.isgood();
|
||||
}
|
||||
bool good()
|
||||
{
|
||||
return m.isgood();
|
||||
}
|
||||
bool is_connected()
|
||||
{
|
||||
return ptr->ms_connected == true;
|
||||
}
|
||||
|
||||
/* is being read from ms side */
|
||||
void read_dl(size_t howmany_sps, uint64_t *read_ts, sample_t *outbuf)
|
||||
{
|
||||
return read(ptr->dl, howmany_sps, read_ts, 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_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)
|
||||
void write_dl(size_t howmany, uint64_t write_ts, sample_t *inbuf)
|
||||
{
|
||||
auto &dl = ptr->dl;
|
||||
auto buf = &dl.buffer[0];
|
||||
if (ptr->ms_connected != true)
|
||||
return;
|
||||
// if (ptr->ms_connected != true)
|
||||
// return;
|
||||
|
||||
assert(sizeof(dl.buffer) >= samp2byte(howmany_sps));
|
||||
// print_guard() << "####w " << std::endl;
|
||||
assert(sizeof(dl.buffer) >= samp2byte(howmany));
|
||||
|
||||
{
|
||||
shm::sema_wait_guard g(dl.w, dl.r);
|
||||
shm::signal_guard g(dl.writemutex, dl.rdy2write, dl.rdy2read);
|
||||
|
||||
memcpy(buf, inbuf, samp2byte(howmany_sps));
|
||||
dl.ts.store(write_ts);
|
||||
dl.len_written_sps.store(howmany_sps);
|
||||
memcpy(buf, inbuf, samp2byte(howmany));
|
||||
dl.ts = write_ts;
|
||||
dl.len_written = howmany;
|
||||
}
|
||||
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)
|
||||
void read_dl(size_t howmany, uint64_t* read_ts, sample_t *outbuf)
|
||||
{
|
||||
auto &ul = ptr->ul;
|
||||
assert(sizeof(ul.buffer) >= samp2byte(howmany_sps_sps));
|
||||
// print_guard() << "####w " << std::endl;
|
||||
auto &dl = ptr->dl;
|
||||
auto buf = &dl.buffer[0];
|
||||
size_t len_avail = dl.len_written;
|
||||
uint64_t ts = dl.ts;
|
||||
|
||||
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);
|
||||
auto left_to_read = len_avail - dl_readoffset;
|
||||
|
||||
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))
|
||||
// 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;
|
||||
}
|
||||
|
||||
// FIXME: store written, notify after get!
|
||||
*read_ts = dl.ts + dl_readoffset;
|
||||
left_to_read = len_avail - dl_readoffset;
|
||||
|
||||
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);
|
||||
// data left from prev read
|
||||
if (left_to_read >= howmany) {
|
||||
memcpy(outbuf, buf, samp2byte(howmany));
|
||||
dl_readoffset += howmany;
|
||||
return;
|
||||
} else {
|
||||
memcpy(outbuf, buf, samp2byte(left_to_read));
|
||||
dl_readoffset = 0;
|
||||
auto still_left_to_read = howmany - left_to_read;
|
||||
{
|
||||
shm::signal_guard g(dl.readmutex, dl.rdy2read, dl.rdy2write);
|
||||
len_avail = dl.len_written;
|
||||
dl_readoffset += still_left_to_read;
|
||||
assert(len_avail >= still_left_to_read);
|
||||
memcpy(outbuf + left_to_read, buf, samp2byte(still_left_to_read));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void signal_read_start()
|
||||
{ /* nop */
|
||||
void read_ul(size_t howmany, uint64_t* read_ts, sample_t *outbuf)
|
||||
{
|
||||
// if (ptr->ms_connected != true) {
|
||||
memset(outbuf, 0, samp2byte(howmany));
|
||||
return;
|
||||
// }
|
||||
}
|
||||
};
|
||||
|
||||
@@ -18,15 +18,10 @@
|
||||
* 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>
|
||||
@@ -37,36 +32,6 @@
|
||||
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;
|
||||
|
||||
@@ -105,8 +70,6 @@ class shmmutex {
|
||||
{
|
||||
return &mutex;
|
||||
}
|
||||
shmmutex(const shmmutex &) = delete;
|
||||
shmmutex &operator=(const shmmutex &) = delete;
|
||||
};
|
||||
|
||||
class shmcond {
|
||||
@@ -141,142 +104,6 @@ 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 {
|
||||
@@ -372,4 +199,21 @@ 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,6 +27,7 @@
|
||||
#endif
|
||||
#include <complex>
|
||||
|
||||
|
||||
#include <algorithm>
|
||||
#include <string.h>
|
||||
#include <iostream>
|
||||
@@ -42,8 +43,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
|
||||
*
|
||||
@@ -51,7 +52,8 @@ 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);
|
||||
|
||||
@@ -61,15 +63,21 @@ 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 };
|
||||
@@ -78,31 +86,40 @@ void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_
|
||||
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 {
|
||||
@@ -112,20 +129,22 @@ 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;
|
||||
@@ -142,13 +161,16 @@ void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output
|
||||
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);
|
||||
|
||||
@@ -159,7 +181,9 @@ gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_c
|
||||
}
|
||||
|
||||
/* 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);
|
||||
@@ -168,7 +192,9 @@ inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
@@ -200,12 +226,10 @@ 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;
|
||||
@@ -302,12 +326,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);
|
||||
const auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
|
||||
const auto tseqlen = N_SYNC_BITS - (2 * TRAIN_BEGINNING);
|
||||
const int search_stop_pos = len - (N_SYNC_BITS*8);
|
||||
auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
|
||||
|
||||
return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen,
|
||||
corr_max);
|
||||
|
||||
@@ -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;
|
||||
return -1;
|
||||
exit(0);
|
||||
}
|
||||
if (bladerf_device_speed(dev) != bladerf_dev_speed::BLADERF_DEVICE_SPEED_SUPER) {
|
||||
std::cerr << "open failed, only superspeed (usb3) supported!" << std::endl;
|
||||
@@ -479,7 +479,4 @@ template <typename T> struct blade_hw {
|
||||
return exit(0);
|
||||
}
|
||||
}
|
||||
void signal_start()
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
*
|
||||
*/
|
||||
|
||||
#include <cassert>
|
||||
#include <complex>
|
||||
#include <cstring>
|
||||
#include <functional>
|
||||
@@ -31,7 +30,6 @@
|
||||
#include <Timeval.h>
|
||||
#include <vector>
|
||||
|
||||
// #define MTX_LOG_ENABLED
|
||||
#include <ipcif.h>
|
||||
|
||||
// typedef unsigned long long TIMESTAMP;
|
||||
@@ -74,6 +72,8 @@ 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(0)
|
||||
ipc_hw() : rxFullScale(32767), txFullScale(32767), rxtxdelay(-67)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -137,14 +137,13 @@ 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];
|
||||
|
||||
@@ -152,18 +151,42 @@ 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;
|
||||
}
|
||||
|
||||
@@ -171,6 +194,11 @@ 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);
|
||||
@@ -228,8 +256,4 @@ template <typename T> struct ipc_hw {
|
||||
// return exit(0);
|
||||
// }
|
||||
}
|
||||
void signal_start()
|
||||
{
|
||||
m.signal_read_start();
|
||||
}
|
||||
};
|
||||
@@ -20,10 +20,8 @@
|
||||
*/
|
||||
|
||||
#include <radioInterface.h>
|
||||
#include "l1if.h"
|
||||
#include "ms_rx_upper.h"
|
||||
#include "syncthing.h"
|
||||
#include "ms_state.h"
|
||||
|
||||
void upper_trx::driveControl()
|
||||
{
|
||||
@@ -91,6 +89,8 @@ 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,7 +101,8 @@ void upper_trx::commandhandler(char *buffer, char *response)
|
||||
if (!mOn) {
|
||||
// Prepare for thread start
|
||||
mPower = -20;
|
||||
start_ms();
|
||||
// start_ms();
|
||||
set_upper_ready(true);
|
||||
|
||||
writeClockInterface();
|
||||
mOn = true;
|
||||
@@ -196,8 +197,6 @@ 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;
|
||||
@@ -211,7 +210,6 @@ 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,7 +173,6 @@ __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);
|
||||
@@ -193,7 +192,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,61 +135,76 @@ void ms_trx::maybe_update_gain(one_burst &brst)
|
||||
gain_check = (gain_check + 1) % avgburst_num;
|
||||
}
|
||||
|
||||
bool ms_trx::handle_sch_or_nb(bool get_first_sch)
|
||||
static unsigned char sch_demod_bits[148];
|
||||
|
||||
bool ms_trx::handle_sch_or_nb()
|
||||
{
|
||||
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) {
|
||||
// sched_yield();
|
||||
return false;
|
||||
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));
|
||||
}
|
||||
auto rv = handle_sch(false);
|
||||
// sched_yield();
|
||||
return rv;
|
||||
// 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;
|
||||
}
|
||||
|
||||
float sch_acq_buffer[SCH_LEN_SPS * 2];
|
||||
static 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_buffer = is_first_sch_acq ? first_sch_buf : burst_copy_buffer;
|
||||
|
||||
const auto which_in_buffer = is_first_sch_acq ? first_sch_buf : burst_copy_buffer;
|
||||
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];
|
||||
unsigned char outbin[148];
|
||||
float max_corr = 0;
|
||||
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);
|
||||
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);
|
||||
}
|
||||
|
||||
SoftVector bitss(148);
|
||||
for (int i = 0; i < 148; i++) {
|
||||
bitss[i] = (!outbin[i]) < 1 ? -1 : 1;
|
||||
bitss[i] = (!sch_demod_bits[i]) < 1 ? -1 : 1;
|
||||
}
|
||||
|
||||
auto sch_decode_success = decode_sch(bitss.begin(), is_first_sch_acq);
|
||||
@@ -198,7 +213,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);
|
||||
first_sch_ts_start = first_sch_buf_rcv_ts + start - (ts_offset_symb * 4) - 1;
|
||||
} else if (abs(start) > 1) {
|
||||
// continuous sch tracking, only update if off too much
|
||||
temp_ts_corr_offset += -start;
|
||||
|
||||
@@ -21,21 +21,31 @@
|
||||
|
||||
#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()
|
||||
@@ -43,15 +53,6 @@ extern "C" {
|
||||
|
||||
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) {
|
||||
@@ -68,6 +69,26 @@ 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()
|
||||
@@ -75,106 +96,65 @@ 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 signalVector sv(625, 40);
|
||||
signalVector *burst = &sv;
|
||||
static complex workbuf[40 + 625 + 40];
|
||||
static signalVector sv(workbuf, 40, 625);
|
||||
|
||||
GSM::Time burst_time;
|
||||
auto ss = reinterpret_cast<std::complex<float> *>(&workbuf[40]);
|
||||
|
||||
memset((void *)&workbuf[0], 0, sizeof(workbuf));
|
||||
// assert(sv.begin() == &workbuf[40]);
|
||||
|
||||
one_burst e;
|
||||
unsigned char outbin[148];
|
||||
std::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 = [&]() { 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())); };
|
||||
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()));
|
||||
|
||||
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_fcch) {
|
||||
// return trash
|
||||
// fprintf(stderr, "c %d\n",burst_time.FN());
|
||||
return &bits;
|
||||
}
|
||||
|
||||
pow = energyDetect(*burst, 20 * rx_sps);
|
||||
if (is_sch) {
|
||||
for (int i = 0; i < 148; i++)
|
||||
(bits)[i] = (!e.sch_bits[i]) < 1 ? -1 : 1;
|
||||
RSSI = 10;
|
||||
timingOffset = 0;
|
||||
// fprintf(stderr, "s %d\n", burst_time.FN());
|
||||
return &bits;
|
||||
}
|
||||
|
||||
auto ts = trxcon::trxcon_instance->ts_list[burst_time.TN()];
|
||||
if (ts == NULL || ts->mf_layout == NULL)
|
||||
return 0;
|
||||
|
||||
convert_and_scale<float, int16_t>(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
|
||||
|
||||
pow = energyDetect(sv, 20 * rx_sps);
|
||||
if (pow < -1) {
|
||||
LOG(ALERT) << "Received empty burst";
|
||||
goto release;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -183,22 +163,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;
|
||||
|
||||
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);
|
||||
normal_burst_start = normal_burst_start < 39 ? normal_burst_start : 39;
|
||||
normal_burst_start = normal_burst_start > -39 ? normal_burst_start : -39;
|
||||
|
||||
// fprintf(stderr, "%s %d\n", (is_nb ? "N":"D"), burst_time.FN());
|
||||
// if (is_nb)
|
||||
detect_burst(ss, &chan_imp_resp[0], normal_burst_start, outbin);
|
||||
// else
|
||||
// detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
|
||||
|
||||
for (int i = 0; i < 148; i++)
|
||||
(bits)[i] = (outbin[i]) < 1 ? -1 : 1;
|
||||
}
|
||||
|
||||
RSSI = (int)floor(20.0 * log10(rxFullScale / avg));
|
||||
timingOffset = (int)round(0);
|
||||
|
||||
return &bits;
|
||||
|
||||
release:
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void upper_trx::driveReceiveFIFO()
|
||||
@@ -206,73 +186,45 @@ 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;
|
||||
|
||||
// _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;
|
||||
SoftVector *rxBurst = pullRadioVector(burstTime, RSSI, TOA);
|
||||
|
||||
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 (gsm_sch_check_fn(burstTime.FN())) {
|
||||
if (burstTime.TN() == 0 && gsm_sch_check_fn(burstTime.FN())) {
|
||||
clamp_array(rxBurst->begin(), 148, 1.5f);
|
||||
for (unsigned int i = 0; i < gSlotLen; i++) {
|
||||
auto val = *burstItr++;
|
||||
auto vval = isnan(val) ? 0 : val;
|
||||
((int8_t *)response->symbols)[i] = round((vval - 0.5) * 64.0);
|
||||
((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()
|
||||
{
|
||||
#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 e;
|
||||
while (!trxcon::txq.spsc_pop(&e)) {
|
||||
trxcon::txq.spsc_prep_pop();
|
||||
}
|
||||
|
||||
trxd_to_trx *burst = &buffer;
|
||||
#endif
|
||||
trxd_to_trx *burst = &e;
|
||||
|
||||
auto proper_fn = ntohl(burst->fn);
|
||||
// std::cerr << "got burst!" << proper_fn << ":" << burst->ts
|
||||
// << " current: " << timekeeper.gsmtime().FN()
|
||||
@@ -307,30 +259,9 @@ void upper_trx::driveTx()
|
||||
// memory read --binary --outfile /tmp/mem.bin &burst_buf[0] --count 2500 --force
|
||||
|
||||
submit_burst(burst_buf, txburst->size(), currTime);
|
||||
|
||||
#ifdef IPCIF
|
||||
free(burst);
|
||||
#endif
|
||||
delete txburst;
|
||||
}
|
||||
|
||||
// __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");
|
||||
@@ -350,14 +281,21 @@ int trxc_main(int argc, char *argv[])
|
||||
return status;
|
||||
}
|
||||
|
||||
extern "C" volatile bool gshutdown = false;
|
||||
extern "C" void init_external_transceiver(int argc, char **argv)
|
||||
extern "C" {
|
||||
void init_external_transceiver(struct trx_instance *trx, int argc, char **argv)
|
||||
{
|
||||
trxcon::trxcon_instance = (trxcon::trx_instance *)trx;
|
||||
std::cout << "init?" << std::endl;
|
||||
trxc_main(argc, argv);
|
||||
}
|
||||
|
||||
extern "C" void stop_trx()
|
||||
void close_external_transceiver(int argc, char **argv)
|
||||
{
|
||||
std::cout << "Shutting down transceiver..." << std::endl;
|
||||
}
|
||||
|
||||
void tx_external_transceiver(uint8_t *burst)
|
||||
{
|
||||
trxcon::txq.spsc_push((trxd_to_trx *)burst);
|
||||
}
|
||||
}
|
||||
@@ -28,13 +28,12 @@
|
||||
#include "GSMCommon.h"
|
||||
#include "radioClock.h"
|
||||
#include "syncthing.h"
|
||||
#include "ms_state.h"
|
||||
#include "l1if.h"
|
||||
|
||||
using tx_queue_t = spsc_cond<8 * 1, trxd_to_trx, true, false>;
|
||||
class upper_trx : public ms_trx {
|
||||
int rx_sps, tx_sps;
|
||||
|
||||
ms_TransceiverState mStates;
|
||||
|
||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||
double mTxFreq; ///< the transmit frequency
|
||||
double mRxFreq; ///< the receive frequency
|
||||
@@ -42,9 +41,6 @@ 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;
|
||||
@@ -98,8 +94,6 @@ 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:
|
||||
@@ -110,12 +104,8 @@ 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);
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
#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,9 +40,7 @@ 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;
|
||||
@@ -220,9 +218,6 @@ 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);
|
||||
@@ -256,7 +251,6 @@ 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;
|
||||
};
|
||||
}
|
||||
@@ -282,6 +276,11 @@ 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 = 12 * 1; // rx thread <-> upper rx queue
|
||||
const unsigned int NUM_RXQ_FRAMES = 1; // rx thread <-> upper rx queue
|
||||
const unsigned int SCH_LEN_SPS = (ONE_TS_BURST_LEN * 8 /*ts*/ * 12 /*frames*/);
|
||||
|
||||
template <typename T> void clamp_array(T *start2, unsigned int len, T max)
|
||||
@@ -69,11 +69,17 @@ template <typename DST_T, typename SRC_T> void convert_and_scale_default(void *d
|
||||
}
|
||||
|
||||
struct one_burst {
|
||||
one_burst()
|
||||
{
|
||||
}
|
||||
GSM::Time gsmts;
|
||||
blade_sample_type burst[ONE_TS_BURST_LEN];
|
||||
union {
|
||||
blade_sample_type burst[ONE_TS_BURST_LEN];
|
||||
unsigned char sch_bits[148];
|
||||
};
|
||||
};
|
||||
|
||||
using rx_queue_t = spsc_cond<8 * NUM_RXQ_FRAMES, one_burst, true, false>;
|
||||
using rx_queue_t = spsc_cond<8 * NUM_RXQ_FRAMES, one_burst, true, true>;
|
||||
|
||||
enum class SCH_STATE { SEARCHING, FOUND };
|
||||
|
||||
@@ -189,8 +195,10 @@ 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 first = false);
|
||||
bool handle_sch_or_nb();
|
||||
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,7 +271,4 @@ template <typename T> struct uhd_hw {
|
||||
return exit(0);
|
||||
}
|
||||
}
|
||||
void signal_start()
|
||||
{
|
||||
}
|
||||
};
|
||||
@@ -602,32 +602,9 @@ 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);
|
||||
@@ -641,7 +618,18 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
read_len);
|
||||
return -EINVAL;
|
||||
}
|
||||
#endif
|
||||
|
||||
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;
|
||||
|
||||
tn = buf[0];
|
||||
fn = osmo_load32be(buf + 1);
|
||||
rssi = -(int8_t)buf[5];
|
||||
@@ -681,6 +669,8 @@ static int trx_data_rx_cb(struct osmo_fd *ofd, unsigned int what)
|
||||
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)
|
||||
{
|
||||
@@ -719,7 +709,10 @@ int trx_if_tx_burst(struct trx_instance *trx, uint8_t tn, uint32_t fn,
|
||||
memcpy(buf + 6, bits, 148);
|
||||
|
||||
/* Send data to transceiver */
|
||||
send(trx->trx_ofd_data.fd, buf, 154, 0);
|
||||
if (tx_external_transceiver)
|
||||
tx_external_transceiver(buf);
|
||||
else
|
||||
send(trx->trx_ofd_data.fd, buf, 154, 0);
|
||||
|
||||
#endif
|
||||
return 0;
|
||||
|
||||
@@ -84,3 +84,4 @@ 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,9 +271,8 @@ static void signal_handler(int signum)
|
||||
}
|
||||
}
|
||||
|
||||
extern void init_external_transceiver(int argc, char **argv);
|
||||
extern void stop_trx();
|
||||
extern volatile bool gshutdown;
|
||||
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));
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
@@ -372,14 +371,14 @@ int main(int argc, char **argv)
|
||||
/* Initialize pseudo-random generator */
|
||||
srand(time(NULL));
|
||||
|
||||
init_external_transceiver(argc, argv);
|
||||
|
||||
while (!app_data.quit)
|
||||
osmo_select_main(0);
|
||||
|
||||
gshutdown = true;
|
||||
stop_trx();
|
||||
if (init_external_transceiver)
|
||||
init_external_transceiver(app_data.trx, argc, argv);
|
||||
else
|
||||
while (!app_data.quit)
|
||||
osmo_select_main(0);
|
||||
|
||||
if (close_external_transceiver)
|
||||
close_external_transceiver(argc, argv);
|
||||
|
||||
exit:
|
||||
/* Close active connections */
|
||||
|
||||
Reference in New Issue
Block a user