Compare commits

..

6 Commits

Author SHA1 Message Date
Eric
2f7b82f282 wip ul bursts
Change-Id: I1c92751a91b34da2539e12939ee5609045725272
2022-10-21 22:51:25 +02:00
Eric
53615c3ccd dl works
Change-Id: I3937d827e20728eaa6280f5750f059a0abf003e0
2022-10-21 22:51:25 +02:00
Eric
d027037c09 wip
Change-Id: Ie8fdf358df07f3944cb223584581514d6c229de7
2022-10-21 22:51:25 +02:00
Eric
f2652f1640 scale
Change-Id: Ifedb737b464beffe3dd9cc82f5051f47454cdf0c
2022-10-21 22:51:25 +02:00
Eric
1b65c9278f ipc2
Change-Id: Ieb15295199a9fcf49050f6d38cfb47a9c1719f02
2022-10-21 22:51:25 +02:00
Eric
07cfdf79a8 new ms
Change-Id: I7c5abe57182e7ef508cac4068c0b41f905d39fd6
2022-10-21 22:51:10 +02:00
21 changed files with 981 additions and 407 deletions

3
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

@@ -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 */
}
};

View File

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

View File

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

View File

@@ -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()
{
}
};

View File

@@ -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();
}
};

View File

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

View File

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

View File

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

View File

@@ -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);
}
}

View File

@@ -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);
};
};

View 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;
};

View File

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

View File

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

View File

@@ -271,4 +271,7 @@ template <typename T> struct uhd_hw {
return exit(0);
}
}
void signal_start()
{
}
};

View File

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

View File

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

View File

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