mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-05 06:33:15 +00:00
Compare commits
4 Commits
1.6.1
...
mstx_newtr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a0e1ed3215 | ||
|
|
a5439daaf7 | ||
|
|
42cc715fac | ||
|
|
008418327b |
@@ -1,4 +0,0 @@
|
|||||||
--exclude osmocom-bb/.*
|
|
||||||
--exclude .*h
|
|
||||||
--exclude Transceiver52M/grgsm_vitac/.*
|
|
||||||
--ignore FUNCTION_WITHOUT_ARGS
|
|
||||||
@@ -25,7 +25,7 @@ AllowShortLoopsOnASingleLine: false
|
|||||||
AlwaysBreakAfterDefinitionReturnType: None
|
AlwaysBreakAfterDefinitionReturnType: None
|
||||||
AlwaysBreakAfterReturnType: None
|
AlwaysBreakAfterReturnType: None
|
||||||
AlwaysBreakBeforeMultilineStrings: false
|
AlwaysBreakBeforeMultilineStrings: false
|
||||||
AlwaysBreakTemplateDeclarations: true
|
AlwaysBreakTemplateDeclarations: false
|
||||||
BinPackArguments: true
|
BinPackArguments: true
|
||||||
BinPackParameters: true
|
BinPackParameters: true
|
||||||
BraceWrapping:
|
BraceWrapping:
|
||||||
@@ -515,7 +515,7 @@ SpacesInContainerLiterals: false
|
|||||||
SpacesInCStyleCastParentheses: false
|
SpacesInCStyleCastParentheses: false
|
||||||
SpacesInParentheses: false
|
SpacesInParentheses: false
|
||||||
SpacesInSquareBrackets: false
|
SpacesInSquareBrackets: false
|
||||||
Standard: Cpp11
|
Standard: Cpp03
|
||||||
TabWidth: 8
|
TabWidth: 8
|
||||||
UseTab: Always
|
UseTab: Always
|
||||||
...
|
...
|
||||||
|
|||||||
1
.gitmodules
vendored
1
.gitmodules
vendored
@@ -1,3 +1,4 @@
|
|||||||
[submodule "osmocom-bb"]
|
[submodule "osmocom-bb"]
|
||||||
path = osmocom-bb
|
path = osmocom-bb
|
||||||
url = https://gitea.osmocom.org/phone-side/osmocom-bb.git
|
url = https://gitea.osmocom.org/phone-side/osmocom-bb.git
|
||||||
|
branch = a4aac5c3554559c2c994609f90b92a9daf6e8a89
|
||||||
|
|||||||
@@ -517,7 +517,7 @@ public:
|
|||||||
@param timeout The blocking timeout in ms.
|
@param timeout The blocking timeout in ms.
|
||||||
@return Pointer at key or NULL on timeout.
|
@return Pointer at key or NULL on timeout.
|
||||||
*/
|
*/
|
||||||
D* read(const K &key, unsigned timeout)
|
D* read(const K &key, unsigned timeout) const
|
||||||
{
|
{
|
||||||
if (timeout==0) return readNoBlock(key);
|
if (timeout==0) return readNoBlock(key);
|
||||||
ScopedLock lock(mLock);
|
ScopedLock lock(mLock);
|
||||||
@@ -537,7 +537,7 @@ public:
|
|||||||
@param key The key to read from.
|
@param key The key to read from.
|
||||||
@return Pointer at key.
|
@return Pointer at key.
|
||||||
*/
|
*/
|
||||||
D* read(const K &key)
|
D* read(const K &key) const
|
||||||
{
|
{
|
||||||
ScopedLock lock(mLock);
|
ScopedLock lock(mLock);
|
||||||
typename Map::const_iterator iter = mMap.find(key);
|
typename Map::const_iterator iter = mMap.find(key);
|
||||||
|
|||||||
@@ -22,8 +22,8 @@
|
|||||||
include $(top_srcdir)/Makefile.common
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
|
||||||
AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libcommon.la
|
noinst_LTLIBRARIES = libcommon.la
|
||||||
|
|
||||||
@@ -37,12 +37,7 @@ libcommon_la_SOURCES = \
|
|||||||
trx_rate_ctr.cpp \
|
trx_rate_ctr.cpp \
|
||||||
trx_vty.c \
|
trx_vty.c \
|
||||||
debug.c
|
debug.c
|
||||||
libcommon_la_LIBADD = \
|
libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
|
||||||
$(LIBOSMOCORE_LIBS) \
|
|
||||||
$(LIBOSMOCTRL_LIBS) \
|
|
||||||
$(LIBOSMOVTY_LIBS) \
|
|
||||||
-lpthread \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
noinst_HEADERS = \
|
noinst_HEADERS = \
|
||||||
BitVector.h \
|
BitVector.h \
|
||||||
|
|||||||
@@ -43,6 +43,71 @@ using namespace std;
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
Mutex gStreamLock; ///< Global lock to control access to cout and cerr.
|
||||||
|
|
||||||
|
void lockCout()
|
||||||
|
{
|
||||||
|
gStreamLock.lock();
|
||||||
|
Timeval entryTime;
|
||||||
|
cout << entryTime << " " << osmo_gettid() << ": ";
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void unlockCout()
|
||||||
|
{
|
||||||
|
cout << dec << endl << flush;
|
||||||
|
gStreamLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void lockCerr()
|
||||||
|
{
|
||||||
|
gStreamLock.lock();
|
||||||
|
Timeval entryTime;
|
||||||
|
cerr << entryTime << " " << osmo_gettid() << ": ";
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlockCerr()
|
||||||
|
{
|
||||||
|
cerr << dec << endl << flush;
|
||||||
|
gStreamLock.unlock();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Mutex::Mutex()
|
||||||
|
{
|
||||||
|
bool res;
|
||||||
|
res = pthread_mutexattr_init(&mAttribs);
|
||||||
|
assert(!res);
|
||||||
|
res = pthread_mutexattr_settype(&mAttribs,PTHREAD_MUTEX_RECURSIVE);
|
||||||
|
assert(!res);
|
||||||
|
res = pthread_mutex_init(&mMutex,&mAttribs);
|
||||||
|
assert(!res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Mutex::~Mutex()
|
||||||
|
{
|
||||||
|
pthread_mutex_destroy(&mMutex);
|
||||||
|
bool res = pthread_mutexattr_destroy(&mAttribs);
|
||||||
|
assert(!res);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/** Block for the signal up to the cancellation timeout. */
|
||||||
|
void Signal::wait(Mutex& wMutex, unsigned timeout) const
|
||||||
|
{
|
||||||
|
Timeval then(timeout);
|
||||||
|
struct timespec waitTime = then.timespec();
|
||||||
|
pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
|
||||||
|
}
|
||||||
|
|
||||||
void set_selfthread_name(const char *name)
|
void set_selfthread_name(const char *name)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -28,96 +28,143 @@
|
|||||||
#ifndef THREADS_H
|
#ifndef THREADS_H
|
||||||
#define THREADS_H
|
#define THREADS_H
|
||||||
|
|
||||||
#include <chrono>
|
#include "config.h"
|
||||||
#include <mutex>
|
|
||||||
#include <condition_variable>
|
|
||||||
#include <pthread.h>
|
#include <pthread.h>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <cassert>
|
#include <assert.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
#include "config.h"
|
|
||||||
#include "Timeval.h"
|
|
||||||
|
|
||||||
class Mutex;
|
class Mutex;
|
||||||
|
|
||||||
|
|
||||||
|
/**@name Multithreaded access for standard streams. */
|
||||||
|
//@{
|
||||||
|
|
||||||
|
/**@name Functions for gStreamLock. */
|
||||||
|
//@{
|
||||||
|
extern Mutex gStreamLock; ///< global lock for cout and cerr
|
||||||
|
void lockCerr(); ///< call prior to writing cerr
|
||||||
|
void unlockCerr(); ///< call after writing cerr
|
||||||
|
void lockCout(); ///< call prior to writing cout
|
||||||
|
void unlockCout(); ///< call after writing cout
|
||||||
|
//@}
|
||||||
|
|
||||||
|
/**@name Macros for standard messages. */
|
||||||
|
//@{
|
||||||
|
#define COUT(text) { lockCout(); std::cout << text; unlockCout(); }
|
||||||
|
#define CERR(text) { lockCerr(); std::cerr << __FILE__ << ":" << __LINE__ << ": " << text; unlockCerr(); }
|
||||||
|
#ifdef NDEBUG
|
||||||
|
#define DCOUT(text) {}
|
||||||
|
#define OBJDCOUT(text) {}
|
||||||
|
#else
|
||||||
|
#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); }
|
||||||
|
#define OBJDCOUT(text) { DCOUT(this << " " << text); }
|
||||||
|
#endif
|
||||||
|
//@}
|
||||||
|
//@}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**@defgroup C++ wrappers for pthread mechanisms. */
|
/**@defgroup C++ wrappers for pthread mechanisms. */
|
||||||
//@{
|
//@{
|
||||||
|
|
||||||
/** A class for recursive mutexes. */
|
/** A class for recursive mutexes based on pthread_mutex. */
|
||||||
class Mutex {
|
class Mutex {
|
||||||
std::recursive_mutex m;
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
pthread_mutex_t mMutex;
|
||||||
|
pthread_mutexattr_t mAttribs;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void lock() {
|
Mutex();
|
||||||
m.lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool trylock() {
|
~Mutex();
|
||||||
return m.try_lock();
|
|
||||||
}
|
|
||||||
|
|
||||||
void unlock() {
|
void lock() { pthread_mutex_lock(&mMutex); }
|
||||||
m.unlock();
|
|
||||||
}
|
bool trylock() { return pthread_mutex_trylock(&mMutex)==0; }
|
||||||
|
|
||||||
|
void unlock() { pthread_mutex_unlock(&mMutex); }
|
||||||
|
|
||||||
friend class Signal;
|
friend class Signal;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
class ScopedLock {
|
class ScopedLock {
|
||||||
Mutex &mMutex;
|
|
||||||
|
private:
|
||||||
|
Mutex& mMutex;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ScopedLock(Mutex &wMutex) : mMutex(wMutex) {
|
ScopedLock(Mutex& wMutex) :mMutex(wMutex) { mMutex.lock(); }
|
||||||
mMutex.lock();
|
~ScopedLock() { mMutex.unlock(); }
|
||||||
}
|
|
||||||
~ScopedLock() {
|
|
||||||
mMutex.unlock();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/** A C++ interthread signal. */
|
|
||||||
|
|
||||||
|
|
||||||
|
/** A C++ interthread signal based on pthread condition variables. */
|
||||||
class Signal {
|
class Signal {
|
||||||
/* any, because for some reason our mutex is recursive... */
|
|
||||||
std::condition_variable_any mSignal;
|
private:
|
||||||
|
|
||||||
|
mutable pthread_cond_t mSignal;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
void wait(Mutex &wMutex, unsigned timeout) {
|
Signal() { int s = pthread_cond_init(&mSignal,NULL); assert(!s); }
|
||||||
mSignal.wait_for(wMutex.m, std::chrono::milliseconds(timeout));
|
|
||||||
}
|
|
||||||
|
|
||||||
void wait(Mutex &wMutex) {
|
~Signal() { pthread_cond_destroy(&mSignal); }
|
||||||
mSignal.wait(wMutex.m);
|
|
||||||
}
|
|
||||||
|
|
||||||
void signal() {
|
/**
|
||||||
mSignal.notify_one();
|
Block for the signal up to the cancellation timeout.
|
||||||
}
|
Under Linux, spurious returns are possible.
|
||||||
|
*/
|
||||||
|
void wait(Mutex& wMutex, unsigned timeout) const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
Block for the signal.
|
||||||
|
Under Linux, spurious returns are possible.
|
||||||
|
*/
|
||||||
|
void wait(Mutex& wMutex) const
|
||||||
|
{ pthread_cond_wait(&mSignal,&wMutex.mMutex); }
|
||||||
|
|
||||||
|
void signal() { pthread_cond_signal(&mSignal); }
|
||||||
|
|
||||||
|
void broadcast() { pthread_cond_broadcast(&mSignal); }
|
||||||
|
|
||||||
void broadcast() {
|
|
||||||
mSignal.notify_all();
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#define START_THREAD(thread,function,argument) \
|
||||||
|
thread.start((void *(*)(void*))function, (void*)argument);
|
||||||
|
|
||||||
void set_selfthread_name(const char *name);
|
void set_selfthread_name(const char *name);
|
||||||
void thread_enable_cancel(bool cancel);
|
void thread_enable_cancel(bool cancel);
|
||||||
|
|
||||||
/** A C++ wrapper for pthread threads. */
|
/** A C++ wrapper for pthread threads. */
|
||||||
class Thread {
|
class Thread {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
pthread_t mThread;
|
pthread_t mThread;
|
||||||
pthread_attr_t mAttrib;
|
pthread_attr_t mAttrib;
|
||||||
// FIXME -- Can this be reduced now?
|
// FIXME -- Can this be reduced now?
|
||||||
size_t mStackSize;
|
size_t mStackSize;
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** Create a thread in a non-running state. */
|
/** Create a thread in a non-running state. */
|
||||||
Thread(size_t wStackSize = 0) : mThread((pthread_t)0)
|
Thread(size_t wStackSize = 0):mThread((pthread_t)0) {
|
||||||
{
|
|
||||||
pthread_attr_init(&mAttrib); // (pat) moved this here.
|
pthread_attr_init(&mAttrib); // (pat) moved this here.
|
||||||
mStackSize = wStackSize;
|
mStackSize=wStackSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -125,17 +172,14 @@ class Thread {
|
|||||||
It should be stopped and joined.
|
It should be stopped and joined.
|
||||||
*/
|
*/
|
||||||
// (pat) If the Thread is destroyed without being started, then mAttrib is undefined. Oops.
|
// (pat) If the Thread is destroyed without being started, then mAttrib is undefined. Oops.
|
||||||
~Thread()
|
~Thread() { pthread_attr_destroy(&mAttrib); }
|
||||||
{
|
|
||||||
pthread_attr_destroy(&mAttrib);
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Start the thread on a task. */
|
/** Start the thread on a task. */
|
||||||
void start(void *(*task)(void *), void *arg);
|
void start(void *(*task)(void*), void *arg);
|
||||||
|
|
||||||
/** Join a thread that will stop on its own. */
|
/** Join a thread that will stop on its own. */
|
||||||
void join()
|
void join() {
|
||||||
{
|
|
||||||
if (mThread) {
|
if (mThread) {
|
||||||
int s = pthread_join(mThread, NULL);
|
int s = pthread_join(mThread, NULL);
|
||||||
assert(!s);
|
assert(!s);
|
||||||
@@ -143,10 +187,7 @@ class Thread {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Send cancellation to thread */
|
/** Send cancellation to thread */
|
||||||
void cancel()
|
void cancel() { pthread_cancel(mThread); }
|
||||||
{
|
|
||||||
pthread_cancel(mThread);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#ifdef HAVE_ATOMIC_OPS
|
#ifdef HAVE_ATOMIC_OPS
|
||||||
|
|||||||
@@ -57,15 +57,4 @@ struct trx_cfg {
|
|||||||
unsigned int stack_size;
|
unsigned int stack_size;
|
||||||
unsigned int num_chans;
|
unsigned int num_chans;
|
||||||
struct trx_chan chans[TRX_CHAN_MAX];
|
struct trx_chan chans[TRX_CHAN_MAX];
|
||||||
struct {
|
|
||||||
bool ul_freq_override;
|
|
||||||
bool dl_freq_override;
|
|
||||||
bool ul_gain_override;
|
|
||||||
bool dl_gain_override;
|
|
||||||
double ul_freq;
|
|
||||||
double dl_freq;
|
|
||||||
double ul_gain;
|
|
||||||
double dl_gain;
|
|
||||||
} overrides;
|
|
||||||
bool use_va;
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -285,79 +285,6 @@ DEFUN_ATTR(cfg_ul_fn_offset, cfg_ul_fn_offset_cmd,
|
|||||||
return CMD_SUCCESS;
|
return CMD_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
DEFUN_ATTR(cfg_ul_freq_override, cfg_ul_freq_override_cmd,
|
|
||||||
"ul-freq-override FLOAT",
|
|
||||||
"Overrides Rx carrier frequency\n"
|
|
||||||
"Frequency in Hz (e.g. 145300000)\n",
|
|
||||||
CMD_ATTR_HIDDEN)
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
trx->cfg.overrides.ul_freq_override = true;
|
|
||||||
trx->cfg.overrides.ul_freq = atof(argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
DEFUN_ATTR(cfg_dl_freq_override, cfg_dl_freq_override_cmd,
|
|
||||||
"dl-freq-override FLOAT",
|
|
||||||
"Overrides Tx carrier frequency\n"
|
|
||||||
"Frequency in Hz (e.g. 145300000)\n",
|
|
||||||
CMD_ATTR_HIDDEN)
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
trx->cfg.overrides.dl_freq_override = true;
|
|
||||||
trx->cfg.overrides.dl_freq = atof(argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN_ATTR(cfg_ul_gain_override, cfg_ul_gain_override_cmd,
|
|
||||||
"ul-gain-override FLOAT",
|
|
||||||
"Overrides Rx gain\n"
|
|
||||||
"gain in dB\n",
|
|
||||||
CMD_ATTR_HIDDEN)
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
trx->cfg.overrides.ul_gain_override = true;
|
|
||||||
trx->cfg.overrides.ul_gain = atof(argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
DEFUN_ATTR(cfg_dl_gain_override, cfg_dl_gain_override_cmd,
|
|
||||||
"dl-gain-override FLOAT",
|
|
||||||
"Overrides Tx gain\n"
|
|
||||||
"gain in dB\n",
|
|
||||||
CMD_ATTR_HIDDEN)
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
trx->cfg.overrides.dl_gain_override = true;
|
|
||||||
trx->cfg.overrides.dl_gain = atof(argv[0]);
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN_ATTR(cfg_use_viterbi, cfg_use_viterbi_cmd,
|
|
||||||
"viterbi-eq (disable|enable)",
|
|
||||||
"Use viterbi equalizer for gmsk (default=disable)\n"
|
|
||||||
"Disable VA\n"
|
|
||||||
"Enable VA\n",
|
|
||||||
CMD_ATTR_HIDDEN)
|
|
||||||
{
|
|
||||||
struct trx_ctx *trx = trx_from_vty(vty);
|
|
||||||
|
|
||||||
if (strcmp("disable", argv[0]) == 0)
|
|
||||||
trx->cfg.use_va = false;
|
|
||||||
else if (strcmp("enable", argv[0]) == 0)
|
|
||||||
trx->cfg.use_va = true;
|
|
||||||
else
|
|
||||||
return CMD_WARNING;
|
|
||||||
|
|
||||||
return CMD_SUCCESS;
|
|
||||||
}
|
|
||||||
|
|
||||||
DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
|
DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
|
||||||
"swap-channels (disable|enable)",
|
"swap-channels (disable|enable)",
|
||||||
"Swap primary and secondary channels of the PHY (if any)\n"
|
"Swap primary and secondary channels of the PHY (if any)\n"
|
||||||
@@ -539,6 +466,7 @@ DEFUN_ATTR(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,
|
|||||||
int rc;
|
int rc;
|
||||||
struct ctr_threshold ctr;
|
struct ctr_threshold ctr;
|
||||||
|
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
rc = vty_ctr_name_2_id(argv[0]);
|
rc = vty_ctr_name_2_id(argv[0]);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
|
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
|
||||||
@@ -570,6 +498,7 @@ DEFUN_ATTR(cfg_no_ctr_error_threshold, cfg_no_ctr_error_threshold_cmd,
|
|||||||
int rc;
|
int rc;
|
||||||
struct ctr_threshold ctr;
|
struct ctr_threshold ctr;
|
||||||
|
|
||||||
|
struct trx_ctx *trx = trx_from_vty(vty);
|
||||||
rc = vty_ctr_name_2_id(argv[0]);
|
rc = vty_ctr_name_2_id(argv[0]);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
|
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
|
||||||
@@ -680,7 +609,7 @@ static int config_write_trx(struct vty *vty)
|
|||||||
vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
|
vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
|
||||||
if (trx->cfg.base_port != DEFAULT_TRX_PORT)
|
if (trx->cfg.base_port != DEFAULT_TRX_PORT)
|
||||||
vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE);
|
vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE);
|
||||||
if (strlen(trx->cfg.dev_args))
|
if (trx->cfg.dev_args)
|
||||||
vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE);
|
vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE);
|
||||||
if (trx->cfg.tx_sps != DEFAULT_TX_SPS)
|
if (trx->cfg.tx_sps != DEFAULT_TX_SPS)
|
||||||
vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
|
vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
|
||||||
@@ -711,16 +640,6 @@ static int config_write_trx(struct vty *vty)
|
|||||||
vty_out(vty, " stack-size %u%s", trx->cfg.stack_size, VTY_NEWLINE);
|
vty_out(vty, " stack-size %u%s", trx->cfg.stack_size, VTY_NEWLINE);
|
||||||
if (trx->cfg.ul_fn_offset != 0)
|
if (trx->cfg.ul_fn_offset != 0)
|
||||||
vty_out(vty, " ul-fn-offset %d%s", trx->cfg.ul_fn_offset, VTY_NEWLINE);
|
vty_out(vty, " ul-fn-offset %d%s", trx->cfg.ul_fn_offset, VTY_NEWLINE);
|
||||||
if (trx->cfg.overrides.dl_freq_override)
|
|
||||||
vty_out(vty, " dl-freq-override %f%s", trx->cfg.overrides.dl_freq, VTY_NEWLINE);
|
|
||||||
if (trx->cfg.overrides.ul_freq_override)
|
|
||||||
vty_out(vty, " ul-freq-override %f%s", trx->cfg.overrides.ul_freq, VTY_NEWLINE);
|
|
||||||
if (trx->cfg.overrides.dl_gain_override)
|
|
||||||
vty_out(vty, " dl-gain-override %f%s", trx->cfg.overrides.dl_gain, VTY_NEWLINE);
|
|
||||||
if (trx->cfg.overrides.ul_gain_override)
|
|
||||||
vty_out(vty, " ul-gain-override %f%s", trx->cfg.overrides.ul_gain, VTY_NEWLINE);
|
|
||||||
if (trx->cfg.use_va)
|
|
||||||
vty_out(vty, " viterbi-eq %s%s", trx->cfg.use_va ? "enable" : "disable", VTY_NEWLINE);
|
|
||||||
trx_rate_ctr_threshold_write_config(vty, " ");
|
trx_rate_ctr_threshold_write_config(vty, " ");
|
||||||
|
|
||||||
for (i = 0; i < trx->cfg.num_chans; i++) {
|
for (i = 0; i < trx->cfg.num_chans; i++) {
|
||||||
@@ -843,7 +762,6 @@ struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx)
|
|||||||
trx->cfg.rx_sps = DEFAULT_RX_SPS;
|
trx->cfg.rx_sps = DEFAULT_RX_SPS;
|
||||||
trx->cfg.filler = FILLER_ZERO;
|
trx->cfg.filler = FILLER_ZERO;
|
||||||
trx->cfg.rssi_offset = 0.0f;
|
trx->cfg.rssi_offset = 0.0f;
|
||||||
trx->cfg.dev_args = talloc_strdup(trx, "");
|
|
||||||
|
|
||||||
return trx;
|
return trx;
|
||||||
}
|
}
|
||||||
@@ -886,11 +804,6 @@ int trx_vty_init(struct trx_ctx* trx)
|
|||||||
|
|
||||||
install_element(TRX_NODE, &cfg_chan_cmd);
|
install_element(TRX_NODE, &cfg_chan_cmd);
|
||||||
install_element(TRX_NODE, &cfg_ul_fn_offset_cmd);
|
install_element(TRX_NODE, &cfg_ul_fn_offset_cmd);
|
||||||
install_element(TRX_NODE, &cfg_ul_freq_override_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_dl_freq_override_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_ul_gain_override_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_dl_gain_override_cmd);
|
|
||||||
install_element(TRX_NODE, &cfg_use_viterbi_cmd);
|
|
||||||
install_node(&chan_node, dummy_config_write);
|
install_node(&chan_node, dummy_config_write);
|
||||||
install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
|
install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
|
||||||
install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
|
install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
|
||||||
|
|||||||
@@ -29,11 +29,12 @@ AM_CXXFLAGS = -Wall -pthread
|
|||||||
SUBDIRS =
|
SUBDIRS =
|
||||||
|
|
||||||
if ENABLE_MS_TRX
|
if ENABLE_MS_TRX
|
||||||
SUBDIRS += $(LIBTRXCON_DIR)
|
SUBDIRS += osmocom-bb/src/host/trxcon
|
||||||
endif
|
endif
|
||||||
|
|
||||||
# Order must be preserved
|
# Order must be preserved
|
||||||
SUBDIRS += \
|
SUBDIRS += \
|
||||||
|
osmocom-bb/src/host/trxcon \
|
||||||
CommonLibs \
|
CommonLibs \
|
||||||
GSM \
|
GSM \
|
||||||
Transceiver52M \
|
Transceiver52M \
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ unlike the built-in complex<> templates, these inline most operations for speed
|
|||||||
template<class Real> class Complex {
|
template<class Real> class Complex {
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef Real value_type;
|
|
||||||
Real r, i;
|
Real r, i;
|
||||||
|
|
||||||
/**@name constructors */
|
/**@name constructors */
|
||||||
|
|||||||
@@ -23,9 +23,9 @@ include $(top_srcdir)/Makefile.common
|
|||||||
|
|
||||||
SUBDIRS = arch device
|
SUBDIRS = arch device
|
||||||
|
|
||||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device/common
|
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device/common -I$(top_srcdir)/osmocom-bb/src/host/trxcon/include/
|
||||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) -I$(top_srcdir)/osmocom-bb/src/host/trxcon/include/
|
||||||
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
|
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) -I$(top_srcdir)/osmocom-bb/src/host/trxcon/include/
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libtransceiver_common.la
|
noinst_LTLIBRARIES = libtransceiver_common.la
|
||||||
|
|
||||||
@@ -63,7 +63,9 @@ noinst_HEADERS = \
|
|||||||
ChannelizerBase.h \
|
ChannelizerBase.h \
|
||||||
Channelizer.h \
|
Channelizer.h \
|
||||||
Synthesis.h \
|
Synthesis.h \
|
||||||
proto_trxd.h
|
proto_trxd.h \
|
||||||
|
grgsm_vitac/viterbi_detector.h \
|
||||||
|
grgsm_vitac/constants.h
|
||||||
|
|
||||||
COMMON_LDADD = \
|
COMMON_LDADD = \
|
||||||
libtransceiver_common.la \
|
libtransceiver_common.la \
|
||||||
@@ -72,47 +74,25 @@ COMMON_LDADD = \
|
|||||||
$(COMMON_LA) \
|
$(COMMON_LA) \
|
||||||
$(FFTWF_LIBS) \
|
$(FFTWF_LIBS) \
|
||||||
$(LIBOSMOCORE_LIBS) \
|
$(LIBOSMOCORE_LIBS) \
|
||||||
|
$(LIBOSMOCODING_LIBS) \
|
||||||
$(LIBOSMOCTRL_LIBS) \
|
$(LIBOSMOCTRL_LIBS) \
|
||||||
$(LIBOSMOVTY_LIBS)
|
$(LIBOSMOVTY_LIBS)
|
||||||
|
|
||||||
if ENABLE_MS_TRX
|
|
||||||
AM_CPPFLAGS += -I$(top_srcdir)/osmocom-bb/src/host/trxcon/include/
|
|
||||||
AM_CPPFLAGS += -I${srcdir}
|
|
||||||
|
|
||||||
TRXCON_LDADD = \
|
TRXCON_LDADD = \
|
||||||
$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libtrxcon.a \
|
|
||||||
$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libl1sched.a \
|
$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libl1sched.a \
|
||||||
$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libl1gprs.a \
|
$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libtrxcon.a
|
||||||
$(LIBOSMOCODING_LIBS)
|
|
||||||
|
|
||||||
MS_LOWER_SRC = \
|
MS_SOURCES = \
|
||||||
ms/sch.c \
|
ms/sch.c \
|
||||||
ms/ms.cpp \
|
ms/ms.cpp \
|
||||||
ms/ms_rx_lower.cpp \
|
ms/ms_rx_lower.cpp
|
||||||
grgsm_vitac/grgsm_vitac.cpp \
|
|
||||||
grgsm_vitac/viterbi_detector.cc
|
|
||||||
|
|
||||||
MS_UPPER_SRC = \
|
|
||||||
ms/ms_upper.cpp \
|
|
||||||
ms/l1ctl_server.c \
|
|
||||||
ms/logging.c \
|
|
||||||
ms/l1ctl_server_cb.cpp \
|
|
||||||
ms/ms_trxcon_if.cpp
|
|
||||||
|
|
||||||
noinst_HEADERS += \
|
noinst_HEADERS += \
|
||||||
ms/ms.h \
|
ms/ms.h \
|
||||||
ms/bladerf_specific.h \
|
ms/bladerf_specific.h \
|
||||||
ms/uhd_specific.h \
|
ms/uhd_specific.h \
|
||||||
ms/ms_upper.h \
|
ms/ms_upper.h \
|
||||||
ms/ms_trxcon_if.h \
|
ms/itrq.h
|
||||||
ms/itrq.h \
|
|
||||||
ms/sch.h \
|
|
||||||
ms/threadpool.h \
|
|
||||||
grgsm_vitac/viterbi_detector.h \
|
|
||||||
grgsm_vitac/constants.h \
|
|
||||||
grgsm_vitac/grgsm_vitac.h
|
|
||||||
|
|
||||||
endif
|
|
||||||
|
|
||||||
bin_PROGRAMS =
|
bin_PROGRAMS =
|
||||||
|
|
||||||
@@ -127,13 +107,22 @@ osmo_trx_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
|
|||||||
|
|
||||||
if ENABLE_MS_TRX
|
if ENABLE_MS_TRX
|
||||||
bin_PROGRAMS += osmo-trx-ms-uhd
|
bin_PROGRAMS += osmo-trx-ms-uhd
|
||||||
osmo_trx_ms_uhd_SOURCES = $(MS_LOWER_SRC) $(MS_UPPER_SRC)
|
osmo_trx_ms_uhd_SOURCES = $(MS_SOURCES) ms/ms_upper.cpp ms/l1ctl_server.c ms/logging.cpp ms/l1ctl_server_cb.cpp
|
||||||
osmo_trx_ms_uhd_LDADD = \
|
osmo_trx_ms_uhd_LDADD = \
|
||||||
$(builddir)/device/uhd/libdevice.la \
|
$(builddir)/device/uhd/libdevice.la \
|
||||||
$(COMMON_LDADD) \
|
$(COMMON_LDADD) \
|
||||||
$(UHD_LIBS) \
|
$(UHD_LIBS) \
|
||||||
$(TRXCON_LDADD)
|
$(TRXCON_LDADD)
|
||||||
osmo_trx_ms_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DBUILDUHD
|
osmo_trx_ms_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DBUILDUHD
|
||||||
|
|
||||||
|
bin_PROGRAMS += osmo-trx-syncthing-uhd
|
||||||
|
osmo_trx_syncthing_uhd_SOURCES = $(MS_SOURCES) ms/ms_rx_burst_test.cpp
|
||||||
|
osmo_trx_syncthing_uhd_LDADD = \
|
||||||
|
$(builddir)/device/uhd/libdevice.la \
|
||||||
|
$(COMMON_LDADD) \
|
||||||
|
$(UHD_LIBS) \
|
||||||
|
$(TRXCON_LDADD)
|
||||||
|
osmo_trx_syncthing_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DSYNCTHINGONLY -DBUILDUHD
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -168,13 +157,22 @@ osmo_trx_blade_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
|
|||||||
|
|
||||||
if ENABLE_MS_TRX
|
if ENABLE_MS_TRX
|
||||||
bin_PROGRAMS += osmo-trx-ms-blade
|
bin_PROGRAMS += osmo-trx-ms-blade
|
||||||
osmo_trx_ms_blade_SOURCES = $(MS_LOWER_SRC) $(MS_UPPER_SRC)
|
osmo_trx_ms_blade_SOURCES = $(MS_SOURCES) ms/ms_upper.cpp ms/l1ctl_server.c ms/logging.cpp ms/l1ctl_server_cb.cpp
|
||||||
osmo_trx_ms_blade_LDADD = \
|
osmo_trx_ms_blade_LDADD = \
|
||||||
$(builddir)/device/bladerf/libdevice.la \
|
$(builddir)/device/bladerf/libdevice.la \
|
||||||
$(COMMON_LDADD) \
|
$(COMMON_LDADD) \
|
||||||
$(BLADE_LIBS) \
|
$(BLADE_LIBS) \
|
||||||
$(TRXCON_LDADD)
|
$(TRXCON_LDADD)
|
||||||
osmo_trx_ms_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DBUILDBLADE
|
osmo_trx_ms_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DBUILDBLADE
|
||||||
|
|
||||||
|
bin_PROGRAMS += osmo-trx-syncthing-blade
|
||||||
|
osmo_trx_syncthing_blade_SOURCES = $(MS_SOURCES) ms/ms_rx_burst_test.cpp
|
||||||
|
osmo_trx_syncthing_blade_LDADD = \
|
||||||
|
$(builddir)/device/bladerf/libdevice.la \
|
||||||
|
$(COMMON_LDADD) \
|
||||||
|
$(BLADE_LIBS) \
|
||||||
|
$(TRXCON_LDADD)
|
||||||
|
osmo_trx_syncthing_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DSYNCTHINGONLY -DBUILDBLADE -I../device/ipc
|
||||||
endif
|
endif
|
||||||
endif
|
endif
|
||||||
|
|
||||||
@@ -185,4 +183,24 @@ osmo_trx_ipc_LDADD = \
|
|||||||
$(builddir)/device/ipc/libdevice.la \
|
$(builddir)/device/ipc/libdevice.la \
|
||||||
$(COMMON_LDADD)
|
$(COMMON_LDADD)
|
||||||
osmo_trx_ipc_CPPFLAGS = $(AM_CPPFLAGS)
|
osmo_trx_ipc_CPPFLAGS = $(AM_CPPFLAGS)
|
||||||
|
|
||||||
|
# bin_PROGRAMS += osmo-trx-ipc2
|
||||||
|
# osmo_trx_ipc2_SOURCES = osmo-trx.cpp
|
||||||
|
# osmo_trx_ipc2_LDADD = \
|
||||||
|
# $(builddir)/device/ipc2/libdevice.la \
|
||||||
|
# $(COMMON_LDADD)
|
||||||
|
# osmo_trx_ipc2_CPPFLAGS = $(AM_CPPFLAGS)
|
||||||
|
# if ENABLE_MS_TRX
|
||||||
|
# bin_PROGRAMS += osmo-trx-ms-ipc
|
||||||
|
# osmo_trx_ms_ipc_SOURCES = $(MS_SOURCES) ms/ms_upper.cpp
|
||||||
|
# osmo_trx_ms_ipc_LDADD = \
|
||||||
|
# $(COMMON_LDADD) \
|
||||||
|
# $(TRXCON_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_SOURCES) ms/ms_rx_burst_test.cpp
|
||||||
|
# osmo_trx_syncthing_ipc_LDADD = $(COMMON_LDADD)
|
||||||
|
# osmo_trx_syncthing_ipc_CPPFLAGS = $(AM_CPPFLAGS) -DSYNCTHINGONLY -DBUILDIPC -I./device/ipc2 -I../device/ipc2
|
||||||
|
# endif
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -29,7 +29,6 @@
|
|||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include "Transceiver.h"
|
#include "Transceiver.h"
|
||||||
#include <Logger.h>
|
#include <Logger.h>
|
||||||
#include <grgsm_vitac/grgsm_vitac.h>
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "osmo_signal.h"
|
#include "osmo_signal.h"
|
||||||
@@ -136,13 +135,12 @@ bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t r
|
|||||||
Transceiver::Transceiver(const struct trx_cfg *cfg,
|
Transceiver::Transceiver(const struct trx_cfg *cfg,
|
||||||
GSM::Time wTransmitLatency,
|
GSM::Time wTransmitLatency,
|
||||||
RadioInterface *wRadioInterface)
|
RadioInterface *wRadioInterface)
|
||||||
: mChans(cfg->num_chans), cfg(cfg),
|
: cfg(cfg), mClockSocket(-1),
|
||||||
mCtrlSockets(mChans), mClockSocket(-1),
|
mRxLowerLoopThread(nullptr), mTxLowerLoopThread(nullptr),
|
||||||
mTxPriorityQueues(mChans), mReceiveFIFO(mChans),
|
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
||||||
mRxServiceLoopThreads(mChans), mRxLowerLoopThread(nullptr), mTxLowerLoopThread(nullptr),
|
mChans(cfg->num_chans), mOn(false), mForceClockInterface(false),
|
||||||
mTxPriorityQueueServiceLoopThreads(mChans), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0),
|
||||||
mOn(false),mForceClockInterface(false), mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0),
|
mMaxExpectedDelayNB(0), mWriteBurstToDiskMask(0)
|
||||||
mMaxExpectedDelayNB(0), mWriteBurstToDiskMask(0), mVersionTRXD(mChans), mStates(mChans)
|
|
||||||
{
|
{
|
||||||
txFullScale = mRadioInterface->fullScaleInputValue();
|
txFullScale = mRadioInterface->fullScaleInputValue();
|
||||||
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
||||||
@@ -209,10 +207,15 @@ bool Transceiver::init()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
initvita();
|
|
||||||
|
|
||||||
mDataSockets.resize(mChans, -1);
|
mDataSockets.resize(mChans, -1);
|
||||||
|
mCtrlSockets.resize(mChans);
|
||||||
|
mTxPriorityQueueServiceLoopThreads.resize(mChans);
|
||||||
|
mRxServiceLoopThreads.resize(mChans);
|
||||||
|
|
||||||
|
mTxPriorityQueues.resize(mChans);
|
||||||
|
mReceiveFIFO.resize(mChans);
|
||||||
|
mStates.resize(mChans);
|
||||||
|
mVersionTRXD.resize(mChans);
|
||||||
|
|
||||||
/* Filler table retransmissions - support only on channel 0 */
|
/* Filler table retransmissions - support only on channel 0 */
|
||||||
if (cfg->filler == FILLER_DUMMY)
|
if (cfg->filler == FILLER_DUMMY)
|
||||||
@@ -617,44 +620,6 @@ double Transceiver::rssiOffset(size_t chan)
|
|||||||
return mRadioInterface->rssiOffset(chan) + cfg->rssi_offset;
|
return mRadioInterface->rssiOffset(chan) + cfg->rssi_offset;
|
||||||
}
|
}
|
||||||
|
|
||||||
static SoftVector *demodAnyBurst_va(const signalVector &burst, CorrType type, int sps, int rach_max_toa, int tsc)
|
|
||||||
{
|
|
||||||
auto conved_beg = reinterpret_cast<const std::complex<float> *>(&burst.begin()[0]);
|
|
||||||
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
|
|
||||||
float ncmax;
|
|
||||||
const unsigned burst_len_bits = 148 + 8;
|
|
||||||
char demodded_softbits[burst_len_bits];
|
|
||||||
SoftVector *bits = new SoftVector(burst_len_bits);
|
|
||||||
|
|
||||||
if (type == CorrType::TSC) {
|
|
||||||
auto rach_burst_start = get_norm_chan_imp_resp(conved_beg, chan_imp_resp, &ncmax, tsc);
|
|
||||||
rach_burst_start = std::max(rach_burst_start, 0);
|
|
||||||
detect_burst_nb(conved_beg, chan_imp_resp, rach_burst_start, demodded_softbits);
|
|
||||||
} else {
|
|
||||||
auto normal_burst_start = get_access_imp_resp(conved_beg, chan_imp_resp, &ncmax, 0);
|
|
||||||
normal_burst_start = std::max(normal_burst_start, 0);
|
|
||||||
detect_burst_ab(conved_beg, chan_imp_resp, normal_burst_start, demodded_softbits, rach_max_toa);
|
|
||||||
}
|
|
||||||
|
|
||||||
float *s = &bits->begin()[0];
|
|
||||||
for (unsigned int i = 0; i < 148; i++)
|
|
||||||
s[i] = demodded_softbits[i] * -1;
|
|
||||||
for (unsigned int i = 148; i < burst_len_bits; i++)
|
|
||||||
s[i] = 0;
|
|
||||||
return bits;
|
|
||||||
}
|
|
||||||
|
|
||||||
#define USE_VA
|
|
||||||
|
|
||||||
#ifdef USE_VA
|
|
||||||
// signalvector is owning despite claiming not to, but we can pretend, too..
|
|
||||||
static void dummy_free(void *wData){};
|
|
||||||
static void *dummy_alloc(size_t newSize)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Pull bursts from the FIFO and handle according to the slot
|
* Pull bursts from the FIFO and handle according to the slot
|
||||||
* and burst correlation type. Equalzation is currently disabled.
|
* and burst correlation type. Equalzation is currently disabled.
|
||||||
@@ -675,9 +640,6 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
|
|||||||
TransceiverState *state = &mStates[chan];
|
TransceiverState *state = &mStates[chan];
|
||||||
bool ctr_changed = false;
|
bool ctr_changed = false;
|
||||||
double rssi_offset;
|
double rssi_offset;
|
||||||
static complex burst_shift_buffer[625];
|
|
||||||
static signalVector shift_vec(burst_shift_buffer, 0, 625, dummy_alloc, dummy_free);
|
|
||||||
signalVector *shvec_ptr = &shift_vec;
|
|
||||||
|
|
||||||
/* Blocking FIFO read */
|
/* Blocking FIFO read */
|
||||||
radioVector *radio_burst = mReceiveFIFO[chan]->read();
|
radioVector *radio_burst = mReceiveFIFO[chan]->read();
|
||||||
@@ -757,15 +719,8 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
|
|||||||
max_toa = (type == RACH || type == EXT_RACH) ?
|
max_toa = (type == RACH || type == EXT_RACH) ?
|
||||||
mMaxExpectedDelayAB : mMaxExpectedDelayNB;
|
mMaxExpectedDelayAB : mMaxExpectedDelayNB;
|
||||||
|
|
||||||
if (cfg->use_va) {
|
|
||||||
// shifted burst copy to make the old demod and detection happy
|
|
||||||
std::copy(burst->begin() + 20, burst->end() - 20, shift_vec.begin());
|
|
||||||
} else {
|
|
||||||
shvec_ptr = burst;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Detect normal or RACH bursts */
|
/* Detect normal or RACH bursts */
|
||||||
rc = detectAnyBurst(*shvec_ptr, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
|
rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
|
||||||
if (rc <= 0) {
|
if (rc <= 0) {
|
||||||
if (rc == -SIGERR_CLIP) {
|
if (rc == -SIGERR_CLIP) {
|
||||||
LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst";
|
LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst";
|
||||||
@@ -779,13 +734,7 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
|
|||||||
goto ret_idle;
|
goto ret_idle;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg->use_va) {
|
rxBurst = demodAnyBurst(*burst, (CorrType) rc, cfg->rx_sps, &ebp);
|
||||||
scaleVector(*burst, { (1. / (float)((1 << 14) - 1)), 0 });
|
|
||||||
rxBurst = demodAnyBurst_va(*burst, (CorrType)rc, cfg->rx_sps, max_toa, mTSC);
|
|
||||||
} else {
|
|
||||||
rxBurst = demodAnyBurst(*shvec_ptr, (CorrType)rc, cfg->rx_sps, &ebp);
|
|
||||||
}
|
|
||||||
|
|
||||||
bi->toa = ebp.toa;
|
bi->toa = ebp.toa;
|
||||||
bi->tsc = ebp.tsc;
|
bi->tsc = ebp.tsc;
|
||||||
bi->ci = ebp.ci;
|
bi->ci = ebp.ci;
|
||||||
|
|||||||
@@ -148,7 +148,6 @@ public:
|
|||||||
} ChannelCombination;
|
} ChannelCombination;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
size_t mChans;
|
|
||||||
struct ctrl_msg {
|
struct ctrl_msg {
|
||||||
char data[101];
|
char data[101];
|
||||||
ctrl_msg() {};
|
ctrl_msg() {};
|
||||||
@@ -162,9 +161,9 @@ struct ctrl_sock_state {
|
|||||||
}
|
}
|
||||||
~ctrl_sock_state() {
|
~ctrl_sock_state() {
|
||||||
if(conn_bfd.fd >= 0) {
|
if(conn_bfd.fd >= 0) {
|
||||||
osmo_fd_unregister(&conn_bfd);
|
|
||||||
close(conn_bfd.fd);
|
close(conn_bfd.fd);
|
||||||
conn_bfd.fd = -1;
|
conn_bfd.fd = -1;
|
||||||
|
osmo_fd_unregister(&conn_bfd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -219,7 +218,7 @@ struct ctrl_sock_state {
|
|||||||
/** drive handling of control messages from GSM core */
|
/** drive handling of control messages from GSM core */
|
||||||
int ctrl_sock_handle_rx(int chan);
|
int ctrl_sock_handle_rx(int chan);
|
||||||
|
|
||||||
|
size_t mChans;
|
||||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||||
bool mForceClockInterface; ///< flag to indicate whether IND CLOCK shall be sent unconditionally after transceiver is started
|
bool mForceClockInterface; ///< flag to indicate whether IND CLOCK shall be sent unconditionally after transceiver is started
|
||||||
bool mHandover[8][8]; ///< expect handover to the timeslot/subslot
|
bool mHandover[8][8]; ///< expect handover to the timeslot/subslot
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ include $(top_srcdir)/Makefile.common
|
|||||||
SUBDIRS = common
|
SUBDIRS = common
|
||||||
|
|
||||||
if DEVICE_IPC
|
if DEVICE_IPC
|
||||||
SUBDIRS += ipc
|
SUBDIRS += ipc ipc2
|
||||||
endif
|
endif
|
||||||
|
|
||||||
if DEVICE_USRP1
|
if DEVICE_USRP1
|
||||||
|
|||||||
@@ -39,19 +39,48 @@ extern "C" {
|
|||||||
|
|
||||||
#define SAMPLE_BUF_SZ (1 << 20)
|
#define SAMPLE_BUF_SZ (1 << 20)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* USRP version dependent device timings
|
||||||
|
*/
|
||||||
|
|
||||||
#define B2XX_TIMING_4_4SPS 6.18462e-5
|
#define B2XX_TIMING_4_4SPS 6.18462e-5
|
||||||
|
|
||||||
#define CHKRET() \
|
#define CHKRET() \
|
||||||
{ \
|
{ \
|
||||||
if (status != 0) \
|
if (status != 0) \
|
||||||
LOGC(DDEV, ERROR) << bladerf_strerror(status); \
|
fprintf(stderr, "%s:%s:%d %s\n", __FILE__, __FUNCTION__, __LINE__, bladerf_strerror(status)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
static const dev_map_t dev_param_map{
|
/*
|
||||||
|
* Tx / Rx sample offset values. In a perfect world, there is no group delay
|
||||||
|
* though analog components, and behaviour through digital filters exactly
|
||||||
|
* matches calculated values. In reality, there are unaccounted factors,
|
||||||
|
* which are captured in these empirically measured (using a loopback test)
|
||||||
|
* timing correction values.
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* USRP1 with timestamps is not supported by UHD.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/* Device Type, Tx-SPS, Rx-SPS */
|
||||||
|
typedef std::tuple<blade_dev_type, int, int> dev_key;
|
||||||
|
|
||||||
|
/* Device parameter descriptor */
|
||||||
|
struct dev_desc {
|
||||||
|
unsigned channels;
|
||||||
|
double mcr;
|
||||||
|
double rate;
|
||||||
|
double offset;
|
||||||
|
std::string str;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<dev_key, dev_desc> dev_param_map{
|
||||||
{ std::make_tuple(blade_dev_type::BLADE2, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
|
{ std::make_tuple(blade_dev_type::BLADE2, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const power_map_t dev_band_nom_power_param_map{
|
typedef std::tuple<blade_dev_type, enum gsm_band> dev_band_key;
|
||||||
|
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
|
||||||
|
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map{
|
||||||
{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
|
{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
|
||||||
{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
|
{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
|
||||||
{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
|
{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
|
||||||
@@ -70,10 +99,11 @@ static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
|
|||||||
return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
|
return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
|
||||||
}
|
}
|
||||||
|
|
||||||
blade_device::blade_device(InterfaceType iface, const struct trx_cfg *cfg)
|
blade_device::blade_device(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
|
||||||
: RadioDevice(iface, cfg), band_manager(dev_band_nom_power_param_map, dev_param_map), dev(nullptr),
|
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
|
||||||
rx_gain_min(0.0), rx_gain_max(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), drop_cnt(0),
|
: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths), dev(nullptr), rx_gain_min(0.0),
|
||||||
prev_ts(0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
|
rx_gain_max(0.0), band_ass_curr_sess(false), band((enum gsm_band)0), tx_spp(0), rx_spp(0), started(false),
|
||||||
|
aligned(false), drop_cnt(0), prev_ts(0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,6 +120,47 @@ blade_device::~blade_device()
|
|||||||
delete rx_buffers[i];
|
delete rx_buffers[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void blade_device::assign_band_desc(enum gsm_band req_band)
|
||||||
|
{
|
||||||
|
dev_band_map_it it;
|
||||||
|
|
||||||
|
it = dev_band_nom_power_param_map.find(dev_band_key(dev_type, req_band));
|
||||||
|
if (it == dev_band_nom_power_param_map.end()) {
|
||||||
|
dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
|
||||||
|
LOGC(DDEV, ERROR) << "No Power parameters exist for device " << desc.str << " on band "
|
||||||
|
<< gsm_band_name(req_band) << ", using B210 ones as fallback";
|
||||||
|
it = dev_band_nom_power_param_map.find(dev_band_key(blade_dev_type::BLADE2, req_band));
|
||||||
|
}
|
||||||
|
OSMO_ASSERT(it != dev_band_nom_power_param_map.end())
|
||||||
|
band_desc = it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool blade_device::set_band(enum gsm_band req_band)
|
||||||
|
{
|
||||||
|
if (band_ass_curr_sess && req_band != band) {
|
||||||
|
LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band) << " different from previous band "
|
||||||
|
<< gsm_band_name(band);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req_band != band) {
|
||||||
|
band = req_band;
|
||||||
|
assign_band_desc(band);
|
||||||
|
}
|
||||||
|
band_ass_curr_sess = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void blade_device::get_dev_band_desc(dev_band_desc &desc)
|
||||||
|
{
|
||||||
|
if (band == 0) {
|
||||||
|
LOGC(DDEV, ERROR)
|
||||||
|
<< "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
|
||||||
|
assign_band_desc(GSM_BAND_900);
|
||||||
|
}
|
||||||
|
desc = band_desc;
|
||||||
|
}
|
||||||
|
|
||||||
void blade_device::init_gains()
|
void blade_device::init_gains()
|
||||||
{
|
{
|
||||||
double tx_gain_min, tx_gain_max;
|
double tx_gain_min, tx_gain_max;
|
||||||
@@ -110,7 +181,7 @@ void blade_device::init_gains()
|
|||||||
bladerf_get_gain_mode(dev, BLADERF_CHANNEL_RX(i), &m);
|
bladerf_get_gain_mode(dev, BLADERF_CHANNEL_RX(i), &m);
|
||||||
LOGC(DDEV, INFO) << (m == BLADERF_GAIN_MANUAL ? "gain manual" : "gain AUTO");
|
LOGC(DDEV, INFO) << (m == BLADERF_GAIN_MANUAL ? "gain manual" : "gain AUTO");
|
||||||
|
|
||||||
status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0);
|
status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0); //gain);
|
||||||
CHKRET()
|
CHKRET()
|
||||||
int actual_gain;
|
int actual_gain;
|
||||||
status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain);
|
status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain);
|
||||||
@@ -119,7 +190,7 @@ void blade_device::init_gains()
|
|||||||
<< r->scale << " actual " << actual_gain;
|
<< r->scale << " actual " << actual_gain;
|
||||||
rx_gains[i] = actual_gain;
|
rx_gains[i] = actual_gain;
|
||||||
|
|
||||||
status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0);
|
status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0); //gain);
|
||||||
CHKRET()
|
CHKRET()
|
||||||
status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain);
|
status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain);
|
||||||
CHKRET()
|
CHKRET()
|
||||||
@@ -136,7 +207,7 @@ void blade_device::init_gains()
|
|||||||
|
|
||||||
for (size_t i = 0; i < tx_gains.size(); i++) {
|
for (size_t i = 0; i < tx_gains.size(); i++) {
|
||||||
double gain = (tx_gain_min + tx_gain_max) / 2;
|
double gain = (tx_gain_min + tx_gain_max) / 2;
|
||||||
status = bladerf_set_gain(dev, BLADERF_CHANNEL_TX(i), 30);
|
status = bladerf_set_gain(dev, BLADERF_CHANNEL_TX(i), 30); //gain);
|
||||||
CHKRET()
|
CHKRET()
|
||||||
int actual_gain;
|
int actual_gain;
|
||||||
status = bladerf_get_gain(dev, BLADERF_CHANNEL_TX(i), &actual_gain);
|
status = bladerf_get_gain(dev, BLADERF_CHANNEL_TX(i), &actual_gain);
|
||||||
@@ -151,6 +222,8 @@ void blade_device::init_gains()
|
|||||||
|
|
||||||
void blade_device::set_rates()
|
void blade_device::set_rates()
|
||||||
{
|
{
|
||||||
|
//dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
|
||||||
|
|
||||||
struct bladerf_rational_rate rate = { 0, static_cast<uint64_t>((1625e3 * 4)), 6 }, actual;
|
struct bladerf_rational_rate rate = { 0, static_cast<uint64_t>((1625e3 * 4)), 6 }, actual;
|
||||||
auto status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_RX(0), &rate, &actual);
|
auto status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_RX(0), &rate, &actual);
|
||||||
CHKRET()
|
CHKRET()
|
||||||
@@ -164,7 +237,8 @@ void blade_device::set_rates()
|
|||||||
bladerf_set_bandwidth(dev, BLADERF_CHANNEL_RX(0), (bladerf_bandwidth)2e6, (bladerf_bandwidth *)NULL);
|
bladerf_set_bandwidth(dev, BLADERF_CHANNEL_RX(0), (bladerf_bandwidth)2e6, (bladerf_bandwidth *)NULL);
|
||||||
bladerf_set_bandwidth(dev, BLADERF_CHANNEL_TX(0), (bladerf_bandwidth)2e6, (bladerf_bandwidth *)NULL);
|
bladerf_set_bandwidth(dev, BLADERF_CHANNEL_TX(0), (bladerf_bandwidth)2e6, (bladerf_bandwidth *)NULL);
|
||||||
|
|
||||||
ts_offset = 60; // FIXME: actual blade offset, should equal b2xx
|
ts_offset = 60; //static_cast<TIMESTAMP>(desc.offset * rx_rate);
|
||||||
|
//LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
|
||||||
}
|
}
|
||||||
|
|
||||||
double blade_device::setRxGain(double db, size_t chan)
|
double blade_device::setRxGain(double db, size_t chan)
|
||||||
@@ -224,7 +298,7 @@ double blade_device::setPowerAttenuation(int atten, size_t chan)
|
|||||||
tx_power = desc.nom_out_tx_power - atten;
|
tx_power = desc.nom_out_tx_power - atten;
|
||||||
db = TxPower2TxGain(desc, tx_power);
|
db = TxPower2TxGain(desc, tx_power);
|
||||||
|
|
||||||
bladerf_set_gain(dev, BLADERF_CHANNEL_TX(chan), 30);
|
bladerf_set_gain(dev, BLADERF_CHANNEL_TX(chan), 30); //db);
|
||||||
int actual_gain;
|
int actual_gain;
|
||||||
bladerf_get_gain(dev, BLADERF_CHANNEL_RX(chan), &actual_gain);
|
bladerf_get_gain(dev, BLADERF_CHANNEL_RX(chan), &actual_gain);
|
||||||
|
|
||||||
@@ -256,15 +330,15 @@ int blade_device::getNominalTxPower(size_t chan)
|
|||||||
return desc.nom_out_tx_power;
|
return desc.nom_out_tx_power;
|
||||||
}
|
}
|
||||||
|
|
||||||
int blade_device::open()
|
int blade_device::open(const std::string &args, int ref, bool swap_channels)
|
||||||
{
|
{
|
||||||
bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_VERBOSE);
|
bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_VERBOSE);
|
||||||
bladerf_set_usb_reset_on_open(true);
|
bladerf_set_usb_reset_on_open(true);
|
||||||
auto success = bladerf_open(&dev, cfg->dev_args);
|
auto success = bladerf_open(&dev, args.c_str());
|
||||||
if (success != 0) {
|
if (success != 0) {
|
||||||
struct bladerf_devinfo *info;
|
struct bladerf_devinfo *info;
|
||||||
auto num_devs = bladerf_get_device_list(&info);
|
auto num_devs = bladerf_get_device_list(&info);
|
||||||
LOGC(DDEV, ALERT) << "No bladerf devices found with identifier '" << cfg->dev_args << "'";
|
LOGC(DDEV, ALERT) << "No bladerf devices found with identifier '" << args << "'";
|
||||||
if (num_devs) {
|
if (num_devs) {
|
||||||
for (int i = 0; i < num_devs; i++)
|
for (int i = 0; i < num_devs; i++)
|
||||||
LOGC(DDEV, ALERT) << "Found device:" << info[i].product << " serial " << info[i].serial;
|
LOGC(DDEV, ALERT) << "Found device:" << info[i].product << " serial " << info[i].serial;
|
||||||
@@ -279,10 +353,10 @@ int blade_device::open()
|
|||||||
|
|
||||||
dev_type = blade_dev_type::BLADE2;
|
dev_type = blade_dev_type::BLADE2;
|
||||||
tx_window = TX_WINDOW_FIXED;
|
tx_window = TX_WINDOW_FIXED;
|
||||||
update_band_dev(dev_key(dev_type, tx_sps, rx_sps));
|
|
||||||
|
|
||||||
struct bladerf_devinfo info;
|
struct bladerf_devinfo info;
|
||||||
bladerf_get_devinfo(dev, &info);
|
bladerf_get_devinfo(dev, &info);
|
||||||
|
// Use the first found device
|
||||||
LOGC(DDEV, INFO) << "Using discovered bladerf device " << info.serial;
|
LOGC(DDEV, INFO) << "Using discovered bladerf device " << info.serial;
|
||||||
|
|
||||||
tx_freqs.resize(chans);
|
tx_freqs.resize(chans);
|
||||||
@@ -291,7 +365,7 @@ int blade_device::open()
|
|||||||
rx_gains.resize(chans);
|
rx_gains.resize(chans);
|
||||||
rx_buffers.resize(chans);
|
rx_buffers.resize(chans);
|
||||||
|
|
||||||
switch (cfg->clock_ref) {
|
switch (ref) {
|
||||||
case REF_INTERNAL:
|
case REF_INTERNAL:
|
||||||
case REF_EXTERNAL:
|
case REF_EXTERNAL:
|
||||||
break;
|
break;
|
||||||
@@ -300,7 +374,7 @@ int blade_device::open()
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg->clock_ref == REF_EXTERNAL) {
|
if (ref == REF_EXTERNAL) {
|
||||||
bool is_locked;
|
bool is_locked;
|
||||||
int status = bladerf_set_pll_enable(dev, true);
|
int status = bladerf_set_pll_enable(dev, true);
|
||||||
CHKRET()
|
CHKRET()
|
||||||
@@ -319,8 +393,7 @@ int blade_device::open()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
LOGC(DDEV, INFO)
|
LOGC(DDEV, INFO) << "Selected clock source is " << ((ref == REF_INTERNAL) ? "internal" : "external 10Mhz");
|
||||||
<< "Selected clock source is " << ((cfg->clock_ref == REF_INTERNAL) ? "internal" : "external 10Mhz");
|
|
||||||
|
|
||||||
set_rates();
|
set_rates();
|
||||||
|
|
||||||
@@ -331,7 +404,7 @@ int blade_device::open()
|
|||||||
|
|
||||||
rif convertbuffer = 625*4 = 2500 -> 4 ts
|
rif convertbuffer = 625*4 = 2500 -> 4 ts
|
||||||
rif rxtxbuf = 4 * segment(625*4) = 10000 -> 16 ts
|
rif rxtxbuf = 4 * segment(625*4) = 10000 -> 16 ts
|
||||||
*/
|
*/
|
||||||
const unsigned int num_buffers = 256;
|
const unsigned int num_buffers = 256;
|
||||||
const unsigned int buffer_size = 1024 * 4; /* Must be a multiple of 1024 */
|
const unsigned int buffer_size = 1024 * 4; /* Must be a multiple of 1024 */
|
||||||
const unsigned int num_transfers = 32;
|
const unsigned int num_transfers = 32;
|
||||||
@@ -346,14 +419,17 @@ int blade_device::open()
|
|||||||
/* Number of samples per over-the-wire packet */
|
/* Number of samples per over-the-wire packet */
|
||||||
tx_spp = rx_spp = buffer_size;
|
tx_spp = rx_spp = buffer_size;
|
||||||
|
|
||||||
|
// Create receive buffer
|
||||||
size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
|
size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
|
||||||
for (size_t i = 0; i < rx_buffers.size(); i++)
|
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||||
rx_buffers[i] = new smpl_buf(buf_len);
|
rx_buffers[i] = new smpl_buf(buf_len);
|
||||||
|
|
||||||
|
// Create vector buffer
|
||||||
pkt_bufs = std::vector<std::vector<short> >(chans, std::vector<short>(2 * rx_spp));
|
pkt_bufs = std::vector<std::vector<short> >(chans, std::vector<short>(2 * rx_spp));
|
||||||
for (size_t i = 0; i < pkt_bufs.size(); i++)
|
for (size_t i = 0; i < pkt_bufs.size(); i++)
|
||||||
pkt_ptrs.push_back(&pkt_bufs[i].front());
|
pkt_ptrs.push_back(&pkt_bufs[i].front());
|
||||||
|
|
||||||
|
// Initialize and shadow gain values
|
||||||
init_gains();
|
init_gains();
|
||||||
|
|
||||||
return NORMAL;
|
return NORMAL;
|
||||||
@@ -387,6 +463,7 @@ bool blade_device::start()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start streaming
|
||||||
if (!restart())
|
if (!restart())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -403,7 +480,7 @@ bool blade_device::stop()
|
|||||||
for (size_t i = 0; i < rx_buffers.size(); i++)
|
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||||
rx_buffers[i]->reset();
|
rx_buffers[i]->reset();
|
||||||
|
|
||||||
band_reset();
|
band_ass_curr_sess = false;
|
||||||
|
|
||||||
started = false;
|
started = false;
|
||||||
return true;
|
return true;
|
||||||
@@ -438,16 +515,22 @@ int blade_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun
|
|||||||
|
|
||||||
struct bladerf_metadata meta = {};
|
struct bladerf_metadata meta = {};
|
||||||
meta.timestamp = ts;
|
meta.timestamp = ts;
|
||||||
|
//static bool first_rx = true;
|
||||||
|
// meta.timestamp = (first_rx) ? ts : 0;
|
||||||
|
// meta.flags = (!first_rx) ? 0:BLADERF_META_FLAG_RX_NOW;
|
||||||
|
// if(first_rx)
|
||||||
|
// first_rx = false;
|
||||||
|
|
||||||
|
// Receive samples from the usrp until we have enough
|
||||||
while (rx_buffers[0]->avail_smpls(timestamp) < len) {
|
while (rx_buffers[0]->avail_smpls(timestamp) < len) {
|
||||||
thread_enable_cancel(false);
|
thread_enable_cancel(false);
|
||||||
int status = bladerf_sync_rx(dev, pkt_ptrs[0], len, &meta, 200U);
|
int status = bladerf_sync_rx(dev, pkt_ptrs[0], len, &meta, 200U);
|
||||||
thread_enable_cancel(true);
|
thread_enable_cancel(true);
|
||||||
|
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
LOGC(DDEV, ERROR) << "RX broken: " << bladerf_strerror(status);
|
std::cerr << "RX fucked: " << bladerf_strerror(status);
|
||||||
if (meta.flags & BLADERF_META_STATUS_OVERRUN)
|
if (meta.flags & BLADERF_META_STATUS_OVERRUN)
|
||||||
LOGC(DDEV, ERROR) << "RX borken, OVERRUN: " << bladerf_strerror(status);
|
std::cerr << "RX fucked OVER: " << bladerf_strerror(status);
|
||||||
|
|
||||||
size_t num_smpls = meta.actual_count;
|
size_t num_smpls = meta.actual_count;
|
||||||
;
|
;
|
||||||
@@ -468,6 +551,7 @@ int blade_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun
|
|||||||
meta.timestamp = ts + num_smpls;
|
meta.timestamp = ts + num_smpls;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We have enough samples
|
||||||
for (size_t i = 0; i < rx_buffers.size(); i++) {
|
for (size_t i = 0; i < rx_buffers.size(); i++) {
|
||||||
rc = rx_buffers[i]->read(bufs[i], len, timestamp);
|
rc = rx_buffers[i]->read(bufs[i], len, timestamp);
|
||||||
if ((rc < 0) || (rc != len)) {
|
if ((rc < 0) || (rc != len)) {
|
||||||
@@ -493,10 +577,13 @@ int blade_device::writeSamples(std::vector<short *> &bufs, int len, bool *underr
|
|||||||
|
|
||||||
thread_enable_cancel(false);
|
thread_enable_cancel(false);
|
||||||
int status = bladerf_sync_tx(dev, (const void *)bufs[0], len, &meta, 200U);
|
int status = bladerf_sync_tx(dev, (const void *)bufs[0], len, &meta, 200U);
|
||||||
|
//size_t num_smpls = tx_stream->send(bufs, len, metadata);
|
||||||
thread_enable_cancel(true);
|
thread_enable_cancel(true);
|
||||||
|
|
||||||
if (status != 0)
|
if (status != 0)
|
||||||
LOGC(DDEV, ERROR) << "TX broken: " << bladerf_strerror(status);
|
std::cerr << "TX fucked: " << bladerf_strerror(status);
|
||||||
|
|
||||||
|
// LOGCHAN(0, DDEV, INFO) << "tx " << timestamp << " " << len << " t+l: "<< timestamp+len << std::endl;
|
||||||
|
|
||||||
return len;
|
return len;
|
||||||
}
|
}
|
||||||
@@ -526,13 +613,27 @@ bool blade_device::set_freq(double freq, size_t chan, bool tx)
|
|||||||
|
|
||||||
bool blade_device::setTxFreq(double wFreq, size_t chan)
|
bool blade_device::setTxFreq(double wFreq, size_t chan)
|
||||||
{
|
{
|
||||||
|
uint16_t req_arfcn;
|
||||||
|
enum gsm_band req_band;
|
||||||
|
|
||||||
if (chan >= tx_freqs.size()) {
|
if (chan >= tx_freqs.size()) {
|
||||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ScopedLock lock(tune_lock);
|
ScopedLock lock(tune_lock);
|
||||||
|
|
||||||
if (!update_band_from_freq(wFreq, chan, true))
|
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 0);
|
||||||
|
if (req_arfcn == 0xffff) {
|
||||||
|
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
|
||||||
|
LOGCHAN(chan, DDEV, ALERT)
|
||||||
|
<< "Unknown GSM band for Tx Frequency " << wFreq << " Hz (ARFCN " << req_arfcn << " )";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_band(req_band))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!set_freq(wFreq, chan, true))
|
if (!set_freq(wFreq, chan, true))
|
||||||
@@ -543,13 +644,27 @@ bool blade_device::setTxFreq(double wFreq, size_t chan)
|
|||||||
|
|
||||||
bool blade_device::setRxFreq(double wFreq, size_t chan)
|
bool blade_device::setRxFreq(double wFreq, size_t chan)
|
||||||
{
|
{
|
||||||
|
uint16_t req_arfcn;
|
||||||
|
enum gsm_band req_band;
|
||||||
|
|
||||||
if (chan >= rx_freqs.size()) {
|
if (chan >= rx_freqs.size()) {
|
||||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
ScopedLock lock(tune_lock);
|
ScopedLock lock(tune_lock);
|
||||||
|
|
||||||
if (!update_band_from_freq(wFreq, chan, false))
|
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
|
||||||
|
if (req_arfcn == 0xffff) {
|
||||||
|
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
|
||||||
|
LOGCHAN(chan, DDEV, ALERT)
|
||||||
|
<< "Unknown GSM band for Rx Frequency " << wFreq << " Hz (ARFCN " << req_arfcn << " )";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_band(req_band))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return set_freq(wFreq, chan, false);
|
return set_freq(wFreq, chan, false);
|
||||||
@@ -582,6 +697,10 @@ bool blade_device::requiresRadioAlign()
|
|||||||
|
|
||||||
GSM::Time blade_device::minLatency()
|
GSM::Time blade_device::minLatency()
|
||||||
{
|
{
|
||||||
|
/* Empirical data from a handful of
|
||||||
|
relatively recent machines shows that the B100 will underrun when
|
||||||
|
the transmit threshold is reduced to a time of 6 and a half frames,
|
||||||
|
so we set a minimum 7 frame threshold. */
|
||||||
return GSM::Time(6, 7);
|
return GSM::Time(6, 7);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -605,7 +724,10 @@ double blade_device::fullScaleOutputValue()
|
|||||||
return (double)2047;
|
return (double)2047;
|
||||||
}
|
}
|
||||||
|
|
||||||
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
|
#ifndef IPCMAGIC
|
||||||
|
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
|
||||||
|
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
|
||||||
{
|
{
|
||||||
return new blade_device(type, cfg);
|
return new blade_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -22,12 +22,10 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "bandmanager.h"
|
|
||||||
#include "radioDevice.h"
|
#include "radioDevice.h"
|
||||||
#include "smpl_buf.h"
|
#include "smpl_buf.h"
|
||||||
|
|
||||||
@@ -35,6 +33,8 @@ extern "C" {
|
|||||||
#include <osmocom/gsm/gsm_utils.h>
|
#include <osmocom/gsm/gsm_utils.h>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#include <bladerf.h>
|
||||||
|
|
||||||
enum class blade_dev_type { BLADE1, BLADE2 };
|
enum class blade_dev_type { BLADE1, BLADE2 };
|
||||||
|
|
||||||
struct dev_band_desc {
|
struct dev_band_desc {
|
||||||
@@ -52,26 +52,13 @@ struct dev_band_desc {
|
|||||||
double rxgain2rssioffset_rel; /* dB */
|
double rxgain2rssioffset_rel; /* dB */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Device parameter descriptor */
|
class blade_device : public RadioDevice {
|
||||||
struct dev_desc {
|
|
||||||
unsigned channels;
|
|
||||||
double mcr;
|
|
||||||
double rate;
|
|
||||||
double offset;
|
|
||||||
std::string desc_str;
|
|
||||||
};
|
|
||||||
|
|
||||||
using dev_key = std::tuple<blade_dev_type, int, int>;
|
|
||||||
using dev_band_key = std::tuple<blade_dev_type, enum gsm_band>;
|
|
||||||
using power_map_t = std::map<dev_band_key, dev_band_desc>;
|
|
||||||
using dev_map_t = std::map<dev_key, dev_desc>;
|
|
||||||
|
|
||||||
class blade_device : public RadioDevice, public band_manager<power_map_t, dev_map_t> {
|
|
||||||
public:
|
public:
|
||||||
blade_device(InterfaceType iface, const struct trx_cfg *cfg);
|
blade_device(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chan_num, double offset,
|
||||||
|
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths);
|
||||||
~blade_device();
|
~blade_device();
|
||||||
|
|
||||||
int open();
|
int open(const std::string &args, int ref, bool swap_channels);
|
||||||
bool start();
|
bool start();
|
||||||
bool stop();
|
bool stop();
|
||||||
bool restart();
|
bool restart();
|
||||||
@@ -167,6 +154,9 @@ class blade_device : public RadioDevice, public band_manager<power_map_t, dev_ma
|
|||||||
|
|
||||||
std::vector<double> tx_gains, rx_gains;
|
std::vector<double> tx_gains, rx_gains;
|
||||||
std::vector<double> tx_freqs, rx_freqs;
|
std::vector<double> tx_freqs, rx_freqs;
|
||||||
|
bool band_ass_curr_sess; /* true if "band" was set after last POWEROFF */
|
||||||
|
enum gsm_band band;
|
||||||
|
struct dev_band_desc band_desc;
|
||||||
size_t tx_spp, rx_spp;
|
size_t tx_spp, rx_spp;
|
||||||
|
|
||||||
bool started;
|
bool started;
|
||||||
@@ -189,6 +179,9 @@ class blade_device : public RadioDevice, public band_manager<power_map_t, dev_ma
|
|||||||
bool flush_recv(size_t num_pkts);
|
bool flush_recv(size_t num_pkts);
|
||||||
|
|
||||||
bool set_freq(double freq, size_t chan, bool tx);
|
bool set_freq(double freq, size_t chan, bool tx);
|
||||||
|
void get_dev_band_desc(dev_band_desc &desc);
|
||||||
|
bool set_band(enum gsm_band req_band);
|
||||||
|
void assign_band_desc(enum gsm_band req_band);
|
||||||
|
|
||||||
Thread *async_event_thrd;
|
Thread *async_event_thrd;
|
||||||
Mutex tune_lock;
|
Mutex tune_lock;
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES)
|
|||||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
|
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
|
||||||
|
|
||||||
|
|
||||||
noinst_HEADERS = radioDevice.h smpl_buf.h bandmanager.h
|
noinst_HEADERS = radioDevice.h smpl_buf.h
|
||||||
|
|
||||||
noinst_LTLIBRARIES = libdevice_common.la
|
noinst_LTLIBRARIES = libdevice_common.la
|
||||||
|
|
||||||
|
|||||||
@@ -1,137 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
/*
|
|
||||||
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* Author: Eric Wild <ewild@sysmocom.de>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <string>
|
|
||||||
#include <tuple>
|
|
||||||
|
|
||||||
#include "Logger.h"
|
|
||||||
|
|
||||||
extern "C" {
|
|
||||||
#include <osmocom/gsm/gsm_utils.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename powermapt, typename devmapt>
|
|
||||||
class band_manager {
|
|
||||||
using powerkeyt = typename powermapt::key_type;
|
|
||||||
using powermappedt = typename powermapt::mapped_type;
|
|
||||||
using devkeyt = typename devmapt::key_type;
|
|
||||||
devkeyt m_dev_type;
|
|
||||||
const powermapt &m_power_map;
|
|
||||||
const devmapt &m_dev_map;
|
|
||||||
powerkeyt m_fallback;
|
|
||||||
enum gsm_band m_band;
|
|
||||||
powermappedt m_band_desc;
|
|
||||||
bool band_ass_curr_sess{}; /* true if "band" was set after last POWEROFF */
|
|
||||||
|
|
||||||
// looks up either first tuple element (->enum) or straight enum
|
|
||||||
template <typename T, typename std::enable_if<std::is_enum<T>::value>::type *dummy = nullptr>
|
|
||||||
auto key_helper(T &t) -> T
|
|
||||||
{
|
|
||||||
return t;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
auto key_helper(T t) -> typename std::tuple_element<0, T>::type
|
|
||||||
{
|
|
||||||
return std::get<0>(t);
|
|
||||||
}
|
|
||||||
|
|
||||||
void assign_band_desc(enum gsm_band req_band)
|
|
||||||
{
|
|
||||||
auto key = key_helper(m_dev_type);
|
|
||||||
auto fallback_key = key_helper(m_fallback);
|
|
||||||
auto it = m_power_map.find({ key, req_band });
|
|
||||||
if (it == m_power_map.end()) {
|
|
||||||
auto desc = m_dev_map.at(m_dev_type);
|
|
||||||
LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device " << desc.desc_str
|
|
||||||
<< " on band " << gsm_band_name(req_band) << ", using fallback..";
|
|
||||||
it = m_power_map.find({ fallback_key, req_band });
|
|
||||||
}
|
|
||||||
OSMO_ASSERT(it != m_power_map.end());
|
|
||||||
m_band_desc = it->second;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool set_band(enum gsm_band req_band)
|
|
||||||
{
|
|
||||||
if (band_ass_curr_sess && req_band != m_band) {
|
|
||||||
LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
|
|
||||||
<< " different from previous band " << gsm_band_name(m_band);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (req_band != m_band) {
|
|
||||||
m_band = req_band;
|
|
||||||
assign_band_desc(m_band);
|
|
||||||
}
|
|
||||||
band_ass_curr_sess = true;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
|
||||||
band_manager(const devkeyt &dev_type, const powermapt &power_map, const devmapt &dev_map, powerkeyt fallback)
|
|
||||||
: m_dev_type(dev_type), m_power_map(power_map), m_dev_map(dev_map), m_fallback(fallback),
|
|
||||||
m_band((enum gsm_band)0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
band_manager(const powermapt &power_map, const devmapt &dev_map)
|
|
||||||
: m_dev_type(dev_map.begin()->first), m_power_map(power_map), m_dev_map(dev_map),
|
|
||||||
m_fallback(m_power_map.begin()->first), m_band((enum gsm_band)0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
void band_reset()
|
|
||||||
{
|
|
||||||
band_ass_curr_sess = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
void update_band_dev(devkeyt dev_type) {
|
|
||||||
m_dev_type = dev_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
void get_dev_band_desc(powermappedt &desc)
|
|
||||||
{
|
|
||||||
if (m_band == 0) {
|
|
||||||
LOGC(DDEV, ERROR)
|
|
||||||
<< "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
|
|
||||||
assign_band_desc(GSM_BAND_900);
|
|
||||||
}
|
|
||||||
desc = m_band_desc;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool update_band_from_freq(double wFreq, int chan, bool is_tx)
|
|
||||||
{
|
|
||||||
enum gsm_band req_band;
|
|
||||||
auto dirstr = is_tx ? "Tx" : "Rx";
|
|
||||||
auto req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, !is_tx);
|
|
||||||
if (req_arfcn == 0xffff) {
|
|
||||||
LOGCHAN(chan, DDEV, ALERT)
|
|
||||||
<< "Unknown ARFCN for " << dirstr << " Frequency " << wFreq / 1000 << " kHz";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
|
|
||||||
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for " << dirstr << " Frequency " << wFreq
|
|
||||||
<< " Hz (ARFCN " << req_arfcn << " )";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return set_band(req_band);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -51,10 +51,13 @@ class RadioDevice {
|
|||||||
MULTI_ARFCN,
|
MULTI_ARFCN,
|
||||||
};
|
};
|
||||||
|
|
||||||
static RadioDevice *make(InterfaceType type, const struct trx_cfg *cfg);
|
static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type,
|
||||||
|
size_t chans = 1, double offset = 0.0,
|
||||||
|
const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""),
|
||||||
|
const std::vector<std::string>& rx_paths = std::vector<std::string>(1, ""));
|
||||||
|
|
||||||
/** Initialize the USRP */
|
/** Initialize the USRP */
|
||||||
virtual int open() = 0;
|
virtual int open(const std::string &args, int ref, bool swap_channels)=0;
|
||||||
|
|
||||||
virtual ~RadioDevice() { }
|
virtual ~RadioDevice() { }
|
||||||
|
|
||||||
@@ -161,25 +164,19 @@ class RadioDevice {
|
|||||||
double lo_offset;
|
double lo_offset;
|
||||||
std::vector<std::string> tx_paths, rx_paths;
|
std::vector<std::string> tx_paths, rx_paths;
|
||||||
std::vector<struct device_counters> m_ctr;
|
std::vector<struct device_counters> m_ctr;
|
||||||
const struct trx_cfg *cfg;
|
|
||||||
|
|
||||||
#define charp2str(a) ((a) ? std::string(a) : std::string(""))
|
RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chan_num, double offset,
|
||||||
|
const std::vector<std::string>& tx_paths,
|
||||||
RadioDevice(InterfaceType type, const struct trx_cfg *cfg)
|
const std::vector<std::string>& rx_paths):
|
||||||
: tx_sps(cfg->tx_sps), rx_sps(cfg->rx_sps), iface(type), chans(cfg->num_chans), lo_offset(cfg->offset),
|
tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chan_num), lo_offset(offset),
|
||||||
m_ctr(chans), cfg(cfg)
|
tx_paths(tx_paths), rx_paths(rx_paths)
|
||||||
{
|
{
|
||||||
/* Generate vector of rx/tx_path: */
|
|
||||||
for (unsigned int i = 0; i < cfg->num_chans; i++) {
|
|
||||||
rx_paths.push_back(charp2str(cfg->chans[i].rx_path));
|
|
||||||
tx_paths.push_back(charp2str(cfg->chans[i].tx_path));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iface == MULTI_ARFCN) {
|
if (iface == MULTI_ARFCN) {
|
||||||
LOGC(DDEV, INFO) << "Multi-ARFCN: " << chans << " logical chans -> 1 physical chans";
|
LOGC(DDEV, INFO) << "Multi-ARFCN: "<< chan_num << " logical chans -> 1 physical chans";
|
||||||
chans = 1;
|
chans = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
m_ctr.resize(chans);
|
||||||
for (size_t i = 0; i < chans; i++) {
|
for (size_t i = 0; i < chans; i++) {
|
||||||
memset(&m_ctr[i], 0, sizeof(m_ctr[i]));
|
memset(&m_ctr[i], 0, sizeof(m_ctr[i]));
|
||||||
m_ctr[i].chan = i;
|
m_ctr[i].chan = i;
|
||||||
|
|||||||
@@ -56,13 +56,20 @@ using namespace std;
|
|||||||
|
|
||||||
static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags);
|
static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags);
|
||||||
|
|
||||||
IPCDevice::IPCDevice(InterfaceType iface, const struct trx_cfg *cfg)
|
IPCDevice::IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
|
||||||
: RadioDevice(iface, cfg), sk_chan_state(chans, ipc_per_trx_sock_state()), tx_attenuation(),
|
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
|
||||||
tmp_state(IPC_IF_MSG_GREETING_REQ), shm(NULL), shm_dec(0), rx_buffers(chans), started(false), tx_gains(chans),
|
: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths), tx_attenuation(),
|
||||||
rx_gains(chans)
|
tmp_state(IPC_IF_MSG_GREETING_REQ), shm(NULL), shm_dec(0), started(false)
|
||||||
{
|
{
|
||||||
LOGC(DDEV, INFO) << "creating IPC device...";
|
LOGC(DDEV, INFO) << "creating IPC device...";
|
||||||
|
|
||||||
|
rx_gains.resize(chans);
|
||||||
|
tx_gains.resize(chans);
|
||||||
|
|
||||||
|
rx_buffers.resize(chans);
|
||||||
|
|
||||||
|
sk_chan_state.resize(chans, ipc_per_trx_sock_state());
|
||||||
|
|
||||||
/* Set up per-channel Rx timestamp based Ring buffers */
|
/* Set up per-channel Rx timestamp based Ring buffers */
|
||||||
for (size_t i = 0; i < rx_buffers.size(); i++)
|
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||||
rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
|
rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
|
||||||
@@ -536,9 +543,9 @@ void IPCDevice::ipc_sock_close(struct ipc_per_trx_sock_state *state)
|
|||||||
|
|
||||||
LOGP(DDEV, LOGL_NOTICE, "IPC socket has LOST connection\n");
|
LOGP(DDEV, LOGL_NOTICE, "IPC socket has LOST connection\n");
|
||||||
|
|
||||||
osmo_fd_unregister(bfd);
|
|
||||||
close(bfd->fd);
|
close(bfd->fd);
|
||||||
bfd->fd = -1;
|
bfd->fd = -1;
|
||||||
|
osmo_fd_unregister(bfd);
|
||||||
|
|
||||||
/* flush the queue */
|
/* flush the queue */
|
||||||
while (!llist_empty(&state->upqueue)) {
|
while (!llist_empty(&state->upqueue)) {
|
||||||
@@ -770,12 +777,11 @@ static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags)
|
|||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
int IPCDevice::open()
|
int IPCDevice::open(const std::string &args, int ref, bool swap_channels)
|
||||||
{
|
{
|
||||||
std::string k, v;
|
std::string k, v;
|
||||||
std::string::size_type keyend;
|
std::string::size_type keyend;
|
||||||
int rc;
|
int rc;
|
||||||
std::string args(cfg->dev_args);
|
|
||||||
|
|
||||||
if ((keyend = args.find('=')) != std::string::npos) {
|
if ((keyend = args.find('=')) != std::string::npos) {
|
||||||
k = args.substr(0, keyend++);
|
k = args.substr(0, keyend++);
|
||||||
@@ -810,7 +816,7 @@ int IPCDevice::open()
|
|||||||
while (tmp_state != IPC_IF_MSG_INFO_CNF)
|
while (tmp_state != IPC_IF_MSG_INFO_CNF)
|
||||||
osmo_select_main(0);
|
osmo_select_main(0);
|
||||||
|
|
||||||
ipc_tx_open_req(&master_sk_state, chans, cfg->clock_ref);
|
ipc_tx_open_req(&master_sk_state, chans, ref);
|
||||||
/* Wait until confirmation is recieved */
|
/* Wait until confirmation is recieved */
|
||||||
while (tmp_state != IPC_IF_MSG_OPEN_CNF)
|
while (tmp_state != IPC_IF_MSG_OPEN_CNF)
|
||||||
osmo_select_main(0);
|
osmo_select_main(0);
|
||||||
@@ -1259,15 +1265,16 @@ bool IPCDevice::setRxFreq(double wFreq, size_t chan)
|
|||||||
return send_chan_wait_rsp(chan, msg, IPC_IF_MSG_SETFREQ_CNF);
|
return send_chan_wait_rsp(chan, msg, IPC_IF_MSG_SETFREQ_CNF);
|
||||||
}
|
}
|
||||||
|
|
||||||
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
|
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
|
||||||
|
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
|
||||||
{
|
{
|
||||||
if (cfg->tx_sps != cfg->rx_sps) {
|
if (tx_sps != rx_sps) {
|
||||||
LOGC(DDEV, ERROR) << "IPC Requires tx_sps == rx_sps";
|
LOGC(DDEV, ERROR) << "IPC Requires tx_sps == rx_sps";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (cfg->offset != 0.0) {
|
if (lo_offset != 0.0) {
|
||||||
LOGC(DDEV, ERROR) << "IPC doesn't support lo_offset";
|
LOGC(DDEV, ERROR) << "IPC doesn't support lo_offset";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return new IPCDevice(type, cfg);
|
return new IPCDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -115,11 +115,12 @@ class IPCDevice : public RadioDevice {
|
|||||||
int ipc_chan_sock_write(osmo_fd *bfd);
|
int ipc_chan_sock_write(osmo_fd *bfd);
|
||||||
|
|
||||||
/** Object constructor */
|
/** Object constructor */
|
||||||
IPCDevice(InterfaceType iface, const struct trx_cfg *cfg);
|
IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
|
||||||
|
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths);
|
||||||
virtual ~IPCDevice() override;
|
virtual ~IPCDevice() override;
|
||||||
|
|
||||||
/** Instantiate the IPC */
|
/** Instantiate the IPC */
|
||||||
virtual int open() override;
|
virtual int open(const std::string &args, int ref, bool swap_channels) override;
|
||||||
|
|
||||||
/** Start the IPC */
|
/** Start the IPC */
|
||||||
virtual bool start() override;
|
virtual bool start() override;
|
||||||
@@ -198,7 +199,7 @@ class IPCDevice : public RadioDevice {
|
|||||||
virtual double minRxGain(void) override;
|
virtual double minRxGain(void) override;
|
||||||
|
|
||||||
/* FIXME: return rx_gains[chan] ? receive factor from IPC Driver? */
|
/* FIXME: return rx_gains[chan] ? receive factor from IPC Driver? */
|
||||||
double rssiOffset(size_t chan) override { return 0.0f; };
|
double rssiOffset(size_t chan) { return 0.0f; };
|
||||||
|
|
||||||
double setPowerAttenuation(int atten, size_t chan) override;
|
double setPowerAttenuation(int atten, size_t chan) override;
|
||||||
double getPowerAttenuation(size_t chan = 0) override;
|
double getPowerAttenuation(size_t chan = 0) override;
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
include $(top_srcdir)/Makefile.common
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
|
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
|
||||||
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(UHD_CFLAGS)
|
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
|
||||||
AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(UHD_CFLAGS)
|
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
|
||||||
|
AM_LDFLAGS = -lpthread -lrt
|
||||||
|
|
||||||
noinst_HEADERS = IPCDevice.h shm.h ipc_shm.h ipc_chan.h ipc_sock.h
|
noinst_HEADERS = IPCDevice.h shm.h ipc_shm.h ipc_chan.h ipc_sock.h
|
||||||
|
|
||||||
@@ -13,22 +14,30 @@ endif
|
|||||||
noinst_LTLIBRARIES = libdevice.la
|
noinst_LTLIBRARIES = libdevice.la
|
||||||
|
|
||||||
libdevice_la_SOURCES = IPCDevice.cpp shm.c ipc_shm.c ipc_chan.c ipc_sock.c
|
libdevice_la_SOURCES = IPCDevice.cpp shm.c ipc_shm.c ipc_chan.c ipc_sock.c
|
||||||
libdevice_la_CPPFLAGS = $(AM_CPPFLAGS) -DIPCMAGIC
|
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
|
||||||
libdevice_la_LIBADD = \
|
libdevice_la_CXXFLAGS = $(AM_CXXFLAGS) -DIPCMAGIC
|
||||||
$(top_builddir)/Transceiver52M/device/common/libdevice_common.la \
|
|
||||||
-lpthread \
|
#work around distclean issue on older autotools vers:
|
||||||
-lrt \
|
#a direct build of ../uhd/UHDDevice.cpp tries to clean
|
||||||
$(NULL)
|
#../uhd/.dep/UHDDevice.Plo twice and fails
|
||||||
|
uhddev_ipc.cpp:
|
||||||
|
echo "#include \"../uhd/UHDDevice.cpp\"" >$@
|
||||||
|
CLEANFILES= uhddev_ipc.cpp
|
||||||
|
BUILT_SOURCES = uhddev_ipc.cpp
|
||||||
|
|
||||||
if DEVICE_UHD
|
if DEVICE_UHD
|
||||||
|
|
||||||
bin_PROGRAMS = ipc-driver-test
|
bin_PROGRAMS = ipc-driver-test
|
||||||
#ipc_driver_test_SHORTNAME = drvt
|
#ipc_driver_test_SHORTNAME = drvt
|
||||||
ipc_driver_test_SOURCES = ipc-driver-test.c uhdwrap.cpp ../uhd/UHDDevice.cpp
|
ipc_driver_test_SOURCES = ipc-driver-test.c uhdwrap.cpp ipc_shm.c ipc_chan.c ipc_sock.c uhddev_ipc.cpp
|
||||||
ipc_driver_test_LDADD = \
|
ipc_driver_test_LDADD = \
|
||||||
libdevice.la \
|
shm.lo \
|
||||||
$(COMMON_LA) \
|
$(top_builddir)/Transceiver52M/device/common/libdevice_common.la \
|
||||||
|
$(COMMON_LA)
|
||||||
$(LIBOSMOCORE_LIBS) \
|
$(LIBOSMOCORE_LIBS) \
|
||||||
$(UHD_LIBS) \
|
|
||||||
$(NULL)
|
$(NULL)
|
||||||
|
ipc_driver_test_CXXFLAGS = $(AM_CXXFLAGS) $(UHD_CFLAGS)
|
||||||
|
ipc_driver_test_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
|
||||||
|
ipc_driver_test_CFLAGS = $(AM_CFLAGS) $(UHD_CFLAGS)
|
||||||
|
ipc_driver_test_LDFLAGS = $(AM_LDFLAGS) $(UHD_LIBS)
|
||||||
endif
|
endif
|
||||||
|
|||||||
@@ -97,9 +97,9 @@ void ipc_sock_close(struct ipc_sock_state *state)
|
|||||||
|
|
||||||
ipc_exit_requested = 1;
|
ipc_exit_requested = 1;
|
||||||
|
|
||||||
osmo_fd_unregister(bfd);
|
|
||||||
close(bfd->fd);
|
close(bfd->fd);
|
||||||
bfd->fd = -1;
|
bfd->fd = -1;
|
||||||
|
osmo_fd_unregister(bfd);
|
||||||
|
|
||||||
/* re-enable the generation of ACCEPT for new connections */
|
/* re-enable the generation of ACCEPT for new connections */
|
||||||
osmo_fd_read_enable(&state->listen_bfd);
|
osmo_fd_read_enable(&state->listen_bfd);
|
||||||
|
|||||||
@@ -35,12 +35,9 @@ extern "C" {
|
|||||||
#include "Threads.h"
|
#include "Threads.h"
|
||||||
#include "Utils.h"
|
#include "Utils.h"
|
||||||
|
|
||||||
// no vty source for cfg params here, so we have to build our own
|
int uhd_wrap::open(const std::string &args, int ref, bool swap_channels)
|
||||||
static struct trx_cfg actual_cfg = {};
|
|
||||||
|
|
||||||
int uhd_wrap::open()
|
|
||||||
{
|
{
|
||||||
int rv = uhd_device::open();
|
int rv = uhd_device::open(args, ref, swap_channels);
|
||||||
samps_per_buff_rx = rx_stream->get_max_num_samps();
|
samps_per_buff_rx = rx_stream->get_max_num_samps();
|
||||||
samps_per_buff_tx = tx_stream->get_max_num_samps();
|
samps_per_buff_tx = tx_stream->get_max_num_samps();
|
||||||
channel_count = usrp_dev->get_rx_num_channels();
|
channel_count = usrp_dev->get_rx_num_channels();
|
||||||
@@ -87,33 +84,36 @@ int uhd_wrap::wrap_read(TIMESTAMP *timestamp)
|
|||||||
|
|
||||||
extern "C" void *uhdwrap_open(struct ipc_sk_if_open_req *open_req)
|
extern "C" void *uhdwrap_open(struct ipc_sk_if_open_req *open_req)
|
||||||
{
|
{
|
||||||
actual_cfg.num_chans = open_req->num_chans;
|
unsigned int rx_sps, tx_sps;
|
||||||
actual_cfg.swap_channels = false;
|
|
||||||
/* FIXME: this is actually the sps value, not the sample rate!
|
|
||||||
* sample rate is looked up according to the sps rate by uhd backend */
|
|
||||||
actual_cfg.rx_sps = open_req->rx_sample_freq_num / open_req->rx_sample_freq_den;
|
|
||||||
actual_cfg.tx_sps = open_req->tx_sample_freq_num / open_req->tx_sample_freq_den;
|
|
||||||
|
|
||||||
/* FIXME: dev arg string* */
|
/* FIXME: dev arg string* */
|
||||||
/* FIXME: rx frontend bw? */
|
/* FIXME: rx frontend bw? */
|
||||||
/* FIXME: tx frontend bw? */
|
/* FIXME: tx frontend bw? */
|
||||||
|
ReferenceType cref;
|
||||||
switch (open_req->clockref) {
|
switch (open_req->clockref) {
|
||||||
case FEATURE_MASK_CLOCKREF_EXTERNAL:
|
case FEATURE_MASK_CLOCKREF_EXTERNAL:
|
||||||
actual_cfg.clock_ref = ReferenceType::REF_EXTERNAL;
|
cref = ReferenceType::REF_EXTERNAL;
|
||||||
break;
|
break;
|
||||||
case FEATURE_MASK_CLOCKREF_INTERNAL:
|
case FEATURE_MASK_CLOCKREF_INTERNAL:
|
||||||
default:
|
default:
|
||||||
actual_cfg.clock_ref = ReferenceType::REF_INTERNAL;
|
cref = ReferenceType::REF_INTERNAL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<std::string> tx_paths;
|
||||||
|
std::vector<std::string> rx_paths;
|
||||||
for (unsigned int i = 0; i < open_req->num_chans; i++) {
|
for (unsigned int i = 0; i < open_req->num_chans; i++) {
|
||||||
actual_cfg.chans[i].rx_path = open_req->chan_info[i].tx_path;
|
tx_paths.push_back(open_req->chan_info[i].tx_path);
|
||||||
actual_cfg.chans[i].tx_path = open_req->chan_info[i].rx_path;
|
rx_paths.push_back(open_req->chan_info[i].rx_path);
|
||||||
}
|
}
|
||||||
|
|
||||||
uhd_wrap *uhd_wrap_dev = new uhd_wrap(RadioDevice::NORMAL, &actual_cfg);
|
/* FIXME: this is actually the sps value, not the sample rate!
|
||||||
uhd_wrap_dev->open();
|
* sample rate is looked up according to the sps rate by uhd backend */
|
||||||
|
rx_sps = open_req->rx_sample_freq_num / open_req->rx_sample_freq_den;
|
||||||
|
tx_sps = open_req->tx_sample_freq_num / open_req->tx_sample_freq_den;
|
||||||
|
uhd_wrap *uhd_wrap_dev =
|
||||||
|
new uhd_wrap(tx_sps, rx_sps, RadioDevice::NORMAL, open_req->num_chans, 0.0, tx_paths, rx_paths);
|
||||||
|
uhd_wrap_dev->open("", cref, false);
|
||||||
|
|
||||||
return uhd_wrap_dev;
|
return uhd_wrap_dev;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ class uhd_wrap : public uhd_device {
|
|||||||
|
|
||||||
// void ipc_sock_close() override {};
|
// void ipc_sock_close() override {};
|
||||||
int wrap_read(TIMESTAMP *timestamp);
|
int wrap_read(TIMESTAMP *timestamp);
|
||||||
virtual int open() override;
|
virtual int open(const std::string &args, int ref, bool swap_channels) override;
|
||||||
|
|
||||||
// bool start() override;
|
// bool start() override;
|
||||||
// bool stop() override;
|
// bool stop() override;
|
||||||
|
|||||||
318
Transceiver52M/device/ipc2/IPCDevice.cpp
Normal file
318
Transceiver52M/device/ipc2/IPCDevice.cpp
Normal file
@@ -0,0 +1,318 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* Author: Eric Wild <ewild@sysmocom.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0+
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* See the COPYING file in the main directory for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <sys/time.h>
|
||||||
|
#include <osmocom/core/timer_compat.h>
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "Logger.h"
|
||||||
|
#include "Threads.h"
|
||||||
|
#include "IPCDevice.h"
|
||||||
|
#include "smpl_buf.h"
|
||||||
|
|
||||||
|
#define SAMPLE_BUF_SZ (1 << 20)
|
||||||
|
static const auto ONE_BIT_DURATION ((12./5200.)/(156.25*4.));
|
||||||
|
static const auto ONE_SAMPLE_DURATION_US ((ONE_BIT_DURATION/4.)*1000*1000);
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
IPCDevice2::IPCDevice2(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
|
||||||
|
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
|
||||||
|
: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths), rx_buffers(chans),
|
||||||
|
started(false), tx_gains(chans), rx_gains(chans)
|
||||||
|
{
|
||||||
|
LOGC(DDEV, INFO) << "creating IPC device...";
|
||||||
|
|
||||||
|
if (!(tx_sps == 4) || !(rx_sps == 4)) {
|
||||||
|
LOGC(DDEV, FATAL) << "IPC shm if create failed!";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set up per-channel Rx timestamp based Ring buffers */
|
||||||
|
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||||
|
rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
|
||||||
|
|
||||||
|
if (!m.create()) {
|
||||||
|
LOGC(DDEV, FATAL) << "IPC shm if create failed!";
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPCDevice2::~IPCDevice2()
|
||||||
|
{
|
||||||
|
LOGC(DDEV, INFO) << "Closing IPC device";
|
||||||
|
/* disable all channels */
|
||||||
|
|
||||||
|
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||||
|
delete rx_buffers[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
int IPCDevice2::open(const std::string &args, int ref, bool swap_channels)
|
||||||
|
{
|
||||||
|
std::string k, v;
|
||||||
|
|
||||||
|
/* configure antennas */
|
||||||
|
if (!set_antennas()) {
|
||||||
|
LOGC(DDEV, FATAL) << "IPC antenna setting failed";
|
||||||
|
goto out_close;
|
||||||
|
}
|
||||||
|
|
||||||
|
return iface == MULTI_ARFCN ? MULTI_ARFCN : NORMAL;
|
||||||
|
|
||||||
|
out_close:
|
||||||
|
LOGC(DDEV, FATAL) << "Error in IPC open, closing";
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPCDevice2::start()
|
||||||
|
{
|
||||||
|
LOGC(DDEV, INFO) << "starting IPC...";
|
||||||
|
|
||||||
|
if (started) {
|
||||||
|
LOGC(DDEV, ERR) << "Device already started";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int max_bufs_to_flush = 120;
|
||||||
|
flush_recv(max_bufs_to_flush);
|
||||||
|
|
||||||
|
started = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPCDevice2::stop()
|
||||||
|
{
|
||||||
|
if (!started)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
LOGC(DDEV, NOTICE) << "All channels stopped, terminating...";
|
||||||
|
|
||||||
|
/* reset internal buffer timestamps */
|
||||||
|
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||||
|
rx_buffers[i]->reset();
|
||||||
|
|
||||||
|
started = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
double IPCDevice2::maxRxGain()
|
||||||
|
{
|
||||||
|
return 70;
|
||||||
|
}
|
||||||
|
|
||||||
|
double IPCDevice2::minRxGain()
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IPCDevice2::getNominalTxPower(size_t chan)
|
||||||
|
{
|
||||||
|
return 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
double IPCDevice2::setPowerAttenuation(int atten, size_t chan)
|
||||||
|
{
|
||||||
|
return atten;
|
||||||
|
}
|
||||||
|
|
||||||
|
double IPCDevice2::getPowerAttenuation(size_t chan)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double IPCDevice2::setRxGain(double dB, size_t chan)
|
||||||
|
{
|
||||||
|
if (dB > maxRxGain())
|
||||||
|
dB = maxRxGain();
|
||||||
|
if (dB < minRxGain())
|
||||||
|
dB = minRxGain();
|
||||||
|
|
||||||
|
LOGCHAN(chan, DDEV, NOTICE) << "Setting RX gain to " << dB << " dB";
|
||||||
|
|
||||||
|
return dB;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPCDevice2::flush_recv(size_t num_pkts)
|
||||||
|
{
|
||||||
|
ts_initial = 10000;
|
||||||
|
|
||||||
|
LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPCDevice2::setRxAntenna(const std::string &ant, size_t chan)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string IPCDevice2::getRxAntenna(size_t chan)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPCDevice2::setTxAntenna(const std::string &ant, size_t chan)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string IPCDevice2::getTxAntenna(size_t chan)
|
||||||
|
{
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPCDevice2::requiresRadioAlign()
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
GSM::Time IPCDevice2::minLatency()
|
||||||
|
{
|
||||||
|
/* UNUSED */
|
||||||
|
return GSM::Time(0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the starting write Timestamp*/
|
||||||
|
TIMESTAMP IPCDevice2::initialWriteTimestamp(void)
|
||||||
|
{
|
||||||
|
return ts_initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns the starting read Timestamp*/
|
||||||
|
TIMESTAMP IPCDevice2::initialReadTimestamp(void)
|
||||||
|
{
|
||||||
|
return ts_initial;
|
||||||
|
}
|
||||||
|
|
||||||
|
static timespec readtime, writetime;
|
||||||
|
static void wait_for_sample_time(timespec* last, unsigned int len) {
|
||||||
|
#if 1
|
||||||
|
timespec ts, diff;
|
||||||
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
||||||
|
timespecsub(&ts, last, &diff);
|
||||||
|
auto elapsed_us = (diff.tv_sec * 1000000) + (diff.tv_nsec / 1000);
|
||||||
|
auto max_wait_us = ONE_SAMPLE_DURATION_US * len;
|
||||||
|
if(elapsed_us < max_wait_us)
|
||||||
|
usleep(max_wait_us-elapsed_us);
|
||||||
|
*last = ts;
|
||||||
|
#else
|
||||||
|
usleep(ONE_SAMPLE_DURATION_US * 625);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: Assumes sequential reads
|
||||||
|
int IPCDevice2::readSamples(std::vector<short *> &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun)
|
||||||
|
{
|
||||||
|
int rc, num_smpls; //, expect_smpls;
|
||||||
|
ssize_t avail_smpls;
|
||||||
|
unsigned int i = 0;
|
||||||
|
|
||||||
|
*overrun = false;
|
||||||
|
*underrun = false;
|
||||||
|
|
||||||
|
timestamp += 0;
|
||||||
|
|
||||||
|
/* Check that timestamp is valid */
|
||||||
|
rc = rx_buffers[0]->avail_smpls(timestamp);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc);
|
||||||
|
LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Receive samples from HW until we have enough */
|
||||||
|
while ((avail_smpls = rx_buffers[i]->avail_smpls(timestamp)) < len) {
|
||||||
|
uint64_t recv_timestamp = timestamp;
|
||||||
|
|
||||||
|
m.read_ul(len - avail_smpls, &recv_timestamp, reinterpret_cast<sample_t *>(bufs[0]));
|
||||||
|
num_smpls = len - avail_smpls;
|
||||||
|
wait_for_sample_time(&readtime, num_smpls);
|
||||||
|
|
||||||
|
if (num_smpls == -ETIMEDOUT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
LOGCHAN(i, DDEV, DEBUG)
|
||||||
|
"Received timestamp = " << (TIMESTAMP)recv_timestamp << " (" << num_smpls << ")";
|
||||||
|
|
||||||
|
rc = rx_buffers[i]->write(bufs[i], num_smpls, (TIMESTAMP)recv_timestamp);
|
||||||
|
if (rc < 0) {
|
||||||
|
LOGCHAN(i, DDEV, ERROR)
|
||||||
|
<< rx_buffers[i]->str_code(rc) << " num smpls: " << num_smpls << " chan: " << i;
|
||||||
|
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
|
||||||
|
if (rc != smpl_buf::ERROR_OVERFLOW)
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We have enough samples */
|
||||||
|
|
||||||
|
rc = rx_buffers[i]->read(bufs[i], len, timestamp);
|
||||||
|
if ((rc < 0) || (rc != len)) {
|
||||||
|
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_code(rc) << ". " << rx_buffers[i]->str_status(timestamp)
|
||||||
|
<< ", (len=" << len << ")";
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IPCDevice2::writeSamples(std::vector<short *> &bufs, int len, bool *underrun, unsigned long long timestamp)
|
||||||
|
{
|
||||||
|
*underrun = false;
|
||||||
|
|
||||||
|
LOGCHAN(0, DDEV, DEBUG) << "send buffer of len " << len << " timestamp " << std::hex << timestamp;
|
||||||
|
|
||||||
|
// rc = ipc_shm_enqueue(shm_io_tx_streams[i], timestamp, len, (uint16_t *)bufs[i]);
|
||||||
|
m.write_dl(len, timestamp, reinterpret_cast<sample_t *>(bufs[0]));
|
||||||
|
wait_for_sample_time(&writetime, len);
|
||||||
|
|
||||||
|
return len;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPCDevice2::updateAlignment(TIMESTAMP timestamp)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPCDevice2::setTxFreq(double wFreq, size_t chan)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IPCDevice2::setRxFreq(double wFreq, size_t chan)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
|
||||||
|
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
|
||||||
|
{
|
||||||
|
if (tx_sps != rx_sps) {
|
||||||
|
LOGC(DDEV, ERROR) << "IPC Requires tx_sps == rx_sps";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (lo_offset != 0.0) {
|
||||||
|
LOGC(DDEV, ERROR) << "IPC doesn't support lo_offset";
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
return new IPCDevice2(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
|
||||||
|
}
|
||||||
186
Transceiver52M/device/ipc2/IPCDevice.h
Normal file
186
Transceiver52M/device/ipc2/IPCDevice.h
Normal file
@@ -0,0 +1,186 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2022 sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* Author: Eric Wild <ewild@sysmocom.de>
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0+
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
* See the COPYING file in the main directory for details.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef _IPC_DEVICE_H_
|
||||||
|
#define _IPC_DEVICE_H_
|
||||||
|
|
||||||
|
|
||||||
|
#include <climits>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#ifdef HAVE_CONFIG_H
|
||||||
|
#include "config.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include "radioDevice.h"
|
||||||
|
#include "ipcif.h"
|
||||||
|
|
||||||
|
class smpl_buf;
|
||||||
|
|
||||||
|
class IPCDevice2 : public RadioDevice {
|
||||||
|
trxmsif m;
|
||||||
|
protected:
|
||||||
|
std::vector<smpl_buf *> rx_buffers;
|
||||||
|
double actualSampleRate;
|
||||||
|
|
||||||
|
bool started;
|
||||||
|
|
||||||
|
TIMESTAMP ts_initial;
|
||||||
|
|
||||||
|
std::vector<double> tx_gains, rx_gains;
|
||||||
|
|
||||||
|
bool flush_recv(size_t num_pkts);
|
||||||
|
void update_stream_stats_rx(size_t chan, bool *overrun);
|
||||||
|
void update_stream_stats_tx(size_t chan, bool *underrun);
|
||||||
|
|
||||||
|
bool send_chan_wait_rsp(uint32_t chan, struct msgb *msg_to_send, uint32_t expected_rsp_msg_id);
|
||||||
|
bool send_all_chan_wait_rsp(uint32_t msgid_to_send, uint32_t msgid_to_expect);
|
||||||
|
|
||||||
|
public:
|
||||||
|
/** Object constructor */
|
||||||
|
IPCDevice2(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
|
||||||
|
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths);
|
||||||
|
virtual ~IPCDevice2() override;
|
||||||
|
|
||||||
|
/** Instantiate the IPC */
|
||||||
|
virtual int open(const std::string &args, int ref, bool swap_channels) override;
|
||||||
|
|
||||||
|
/** Start the IPC */
|
||||||
|
virtual bool start() override;
|
||||||
|
|
||||||
|
/** Stop the IPC */
|
||||||
|
virtual bool stop() override;
|
||||||
|
|
||||||
|
/* FIXME: any != USRP1 will do for now... */
|
||||||
|
enum TxWindowType getWindowType() override
|
||||||
|
{
|
||||||
|
return TX_WINDOW_FIXED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
Read samples from the IPC.
|
||||||
|
@param buf preallocated buf to contain read result
|
||||||
|
@param len number of samples desired
|
||||||
|
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
|
||||||
|
@param timestamp The timestamp of the first samples to be read
|
||||||
|
@param underrun Set if IPC does not have data to transmit, e.g. data not being sent fast enough
|
||||||
|
@return The number of samples actually read
|
||||||
|
*/
|
||||||
|
virtual int readSamples(std::vector<short *> &buf, int len, bool *overrun, TIMESTAMP timestamp = 0xffffffff,
|
||||||
|
bool *underrun = NULL) override;
|
||||||
|
/**
|
||||||
|
Write samples to the IPC.
|
||||||
|
@param buf Contains the data to be written.
|
||||||
|
@param len number of samples to write.
|
||||||
|
@param underrun Set if IPC does not have data to transmit, e.g. data not being sent fast enough
|
||||||
|
@param timestamp The timestamp of the first sample of the data buffer.
|
||||||
|
@return The number of samples actually written
|
||||||
|
*/
|
||||||
|
virtual int writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
|
||||||
|
TIMESTAMP timestamp = 0xffffffff) override;
|
||||||
|
|
||||||
|
/** Update the alignment between the read and write timestamps */
|
||||||
|
virtual bool updateAlignment(TIMESTAMP timestamp) override;
|
||||||
|
|
||||||
|
/** Set the transmitter frequency */
|
||||||
|
virtual bool setTxFreq(double wFreq, size_t chan = 0) override;
|
||||||
|
|
||||||
|
/** Set the receiver frequency */
|
||||||
|
virtual bool setRxFreq(double wFreq, size_t chan = 0) override;
|
||||||
|
|
||||||
|
/** Returns the starting write Timestamp*/
|
||||||
|
virtual TIMESTAMP initialWriteTimestamp(void) override;
|
||||||
|
|
||||||
|
/** Returns the starting read Timestamp*/
|
||||||
|
virtual TIMESTAMP initialReadTimestamp(void) override;
|
||||||
|
|
||||||
|
/** returns the full-scale transmit amplitude **/
|
||||||
|
virtual double fullScaleInputValue() override
|
||||||
|
{
|
||||||
|
return (double)SHRT_MAX * 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** returns the full-scale receive amplitude **/
|
||||||
|
virtual double fullScaleOutputValue() override
|
||||||
|
{
|
||||||
|
return (double)SHRT_MAX * 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** sets the receive chan gain, returns the gain setting **/
|
||||||
|
virtual double setRxGain(double dB, size_t chan = 0) override;
|
||||||
|
|
||||||
|
/** get the current receive gain */
|
||||||
|
virtual double getRxGain(size_t chan = 0) override
|
||||||
|
{
|
||||||
|
return rx_gains[chan];
|
||||||
|
}
|
||||||
|
|
||||||
|
/** return maximum Rx Gain **/
|
||||||
|
virtual double maxRxGain(void) override;
|
||||||
|
|
||||||
|
/** return minimum Rx Gain **/
|
||||||
|
virtual double minRxGain(void) override;
|
||||||
|
|
||||||
|
/* FIXME: return rx_gains[chan] ? receive factor from IPC Driver? */
|
||||||
|
double rssiOffset(size_t chan) override
|
||||||
|
{
|
||||||
|
return 0.0f;
|
||||||
|
};
|
||||||
|
|
||||||
|
double setPowerAttenuation(int atten, size_t chan) override;
|
||||||
|
double getPowerAttenuation(size_t chan = 0) override;
|
||||||
|
|
||||||
|
virtual int getNominalTxPower(size_t chan = 0) override;
|
||||||
|
|
||||||
|
/** sets the RX path to use, returns true if successful and false otherwise */
|
||||||
|
virtual bool setRxAntenna(const std::string &ant, size_t chan = 0) override;
|
||||||
|
|
||||||
|
/* return the used RX path */
|
||||||
|
virtual std::string getRxAntenna(size_t chan = 0) override;
|
||||||
|
|
||||||
|
/** sets the RX path to use, returns true if successful and false otherwise */
|
||||||
|
virtual bool setTxAntenna(const std::string &ant, size_t chan = 0) override;
|
||||||
|
|
||||||
|
/* return the used RX path */
|
||||||
|
virtual std::string getTxAntenna(size_t chan = 0) override;
|
||||||
|
|
||||||
|
/** return whether user drives synchronization of Tx/Rx of USRP */
|
||||||
|
virtual bool requiresRadioAlign() override;
|
||||||
|
|
||||||
|
/** return whether user drives synchronization of Tx/Rx of USRP */
|
||||||
|
virtual GSM::Time minLatency() override;
|
||||||
|
|
||||||
|
/** Return internal status values */
|
||||||
|
virtual inline double getTxFreq(size_t chan = 0) override
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
virtual inline double getRxFreq(size_t chan = 0) override
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
virtual inline double getSampleRate() override
|
||||||
|
{
|
||||||
|
return actualSampleRate;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // _IPC_DEVICE_H_
|
||||||
14
Transceiver52M/device/ipc2/Makefile.am
Normal file
14
Transceiver52M/device/ipc2/Makefile.am
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
|
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
|
||||||
|
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
|
||||||
|
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
|
||||||
|
AM_LDFLAGS = -lpthread -lrt
|
||||||
|
|
||||||
|
noinst_HEADERS = IPCDevice.h
|
||||||
|
|
||||||
|
noinst_LTLIBRARIES = libdevice.la
|
||||||
|
|
||||||
|
libdevice_la_SOURCES = IPCDevice.cpp
|
||||||
|
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
|
||||||
|
libdevice_la_CXXFLAGS = $(AM_CXXFLAGS) -DIPCMAGIC
|
||||||
387
Transceiver52M/device/ipc2/ipcif.h
Normal file
387
Transceiver52M/device/ipc2/ipcif.h
Normal file
@@ -0,0 +1,387 @@
|
|||||||
|
/*
|
||||||
|
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* Author: Eric Wild <ewild@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <complex>
|
||||||
|
#include <cassert>
|
||||||
|
#include <deque>
|
||||||
|
#include <mutex>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "shmif.h"
|
||||||
|
|
||||||
|
const int max_ul_rdlen = 1024 * 10;
|
||||||
|
const int max_dl_rdlen = 1024 * 10;
|
||||||
|
using sample_t = std::complex<int16_t>;
|
||||||
|
struct shm_if {
|
||||||
|
std::atomic<bool> ms_connected;
|
||||||
|
struct {
|
||||||
|
shm::sema r;
|
||||||
|
shm::sema w;
|
||||||
|
std::atomic<uint64_t> ts;
|
||||||
|
std::atomic<uint64_t> ts_req;
|
||||||
|
std::atomic<size_t> len_written_sps; // ->
|
||||||
|
sample_t buffer[max_ul_rdlen];
|
||||||
|
} ul;
|
||||||
|
struct {
|
||||||
|
shm::sema r;
|
||||||
|
shm::sema w;
|
||||||
|
std::atomic<uint64_t> ts;
|
||||||
|
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);
|
||||||
|
};
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
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")
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool create()
|
||||||
|
{
|
||||||
|
m.create();
|
||||||
|
ptr = m.p();
|
||||||
|
return m.isgood();
|
||||||
|
}
|
||||||
|
bool connect()
|
||||||
|
{
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
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 */
|
||||||
|
}
|
||||||
|
};
|
||||||
375
Transceiver52M/device/ipc2/shmif.h
Normal file
375
Transceiver52M/device/ipc2/shmif.h
Normal file
@@ -0,0 +1,375 @@
|
|||||||
|
/*
|
||||||
|
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* Author: Eric Wild <ewild@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <atomic>
|
||||||
|
#include <iostream>
|
||||||
|
#include <cassert>
|
||||||
|
#include <cstring>
|
||||||
|
#include <mutex>
|
||||||
|
#include <sstream>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/mman.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <pthread.h>
|
||||||
|
#include <cerrno>
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
public:
|
||||||
|
shmmutex()
|
||||||
|
{
|
||||||
|
pthread_mutexattr_t attr;
|
||||||
|
pthread_mutexattr_init(&attr);
|
||||||
|
pthread_mutexattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
|
||||||
|
pthread_mutexattr_setrobust(&attr, PTHREAD_MUTEX_ROBUST);
|
||||||
|
pthread_mutex_init(&mutex, &attr);
|
||||||
|
pthread_mutexattr_destroy(&attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
~shmmutex()
|
||||||
|
{
|
||||||
|
pthread_mutex_destroy(&mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void lock()
|
||||||
|
{
|
||||||
|
pthread_mutex_lock(&mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool try_lock()
|
||||||
|
{
|
||||||
|
return pthread_mutex_trylock(&mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
void unlock()
|
||||||
|
{
|
||||||
|
pthread_mutex_unlock(&mutex);
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_t *p()
|
||||||
|
{
|
||||||
|
return &mutex;
|
||||||
|
}
|
||||||
|
shmmutex(const shmmutex &) = delete;
|
||||||
|
shmmutex &operator=(const shmmutex &) = delete;
|
||||||
|
};
|
||||||
|
|
||||||
|
class shmcond {
|
||||||
|
pthread_cond_t cond;
|
||||||
|
|
||||||
|
public:
|
||||||
|
shmcond()
|
||||||
|
{
|
||||||
|
pthread_condattr_t attr;
|
||||||
|
pthread_condattr_init(&attr);
|
||||||
|
pthread_condattr_setpshared(&attr, PTHREAD_PROCESS_SHARED);
|
||||||
|
pthread_cond_init(&cond, &attr);
|
||||||
|
pthread_condattr_destroy(&attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
~shmcond()
|
||||||
|
{
|
||||||
|
pthread_cond_destroy(&cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
void wait(shmmutex *lock)
|
||||||
|
{
|
||||||
|
pthread_cond_wait(&cond, lock->p());
|
||||||
|
}
|
||||||
|
|
||||||
|
void signal()
|
||||||
|
{
|
||||||
|
pthread_cond_signal(&cond);
|
||||||
|
}
|
||||||
|
|
||||||
|
void signal_all()
|
||||||
|
{
|
||||||
|
pthread_cond_broadcast(&cond);
|
||||||
|
}
|
||||||
|
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 {
|
||||||
|
char shmname[512];
|
||||||
|
size_t IFT_sz = sizeof(IFT);
|
||||||
|
IFT *shmptr;
|
||||||
|
bool good;
|
||||||
|
int ipc_shm_setup(const char *shm_name)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
int rc;
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
if ((fd = shm_open(shm_name, O_CREAT | O_RDWR | O_TRUNC, S_IRUSR | S_IWUSR)) < 0) {
|
||||||
|
rc = -errno;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ftruncate(fd, IFT_sz) < 0) {
|
||||||
|
rc = -errno;
|
||||||
|
shm_unlink(shm_name);
|
||||||
|
::close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ptr = mmap(NULL, IFT_sz, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
|
||||||
|
rc = -errno;
|
||||||
|
shm_unlink(shm_name);
|
||||||
|
::close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
shmptr = new (ptr) IFT(); //static_cast<IFT *>(ptr);
|
||||||
|
::close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ipc_shm_connect(const char *shm_name)
|
||||||
|
{
|
||||||
|
int fd;
|
||||||
|
int rc;
|
||||||
|
void *ptr;
|
||||||
|
|
||||||
|
if ((fd = shm_open(shm_name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)) < 0) {
|
||||||
|
rc = -errno;
|
||||||
|
return rc;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct stat shm_stat;
|
||||||
|
if (fstat(fd, &shm_stat) < 0) {
|
||||||
|
rc = -errno;
|
||||||
|
shm_unlink(shm_name);
|
||||||
|
::close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((ptr = mmap(NULL, shm_stat.st_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
|
||||||
|
rc = -errno;
|
||||||
|
shm_unlink(shm_name);
|
||||||
|
::close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
|
shmptr = static_cast<IFT *>(ptr);
|
||||||
|
::close(fd);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
using IFT_t = IFT;
|
||||||
|
explicit shm(const char *name) : good(false)
|
||||||
|
{
|
||||||
|
strncpy((char *)shmname, name, 512);
|
||||||
|
}
|
||||||
|
void create()
|
||||||
|
{
|
||||||
|
if (ipc_shm_setup(shmname) == 0)
|
||||||
|
good = true;
|
||||||
|
}
|
||||||
|
void open()
|
||||||
|
{
|
||||||
|
if (ipc_shm_connect(shmname) == 0)
|
||||||
|
good = true;
|
||||||
|
}
|
||||||
|
bool isgood() const
|
||||||
|
{
|
||||||
|
return good;
|
||||||
|
}
|
||||||
|
void close()
|
||||||
|
{
|
||||||
|
if (isgood())
|
||||||
|
shm_unlink(shmname);
|
||||||
|
}
|
||||||
|
IFT *p()
|
||||||
|
{
|
||||||
|
return shmptr;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace shm
|
||||||
@@ -52,16 +52,39 @@ extern "C" {
|
|||||||
#define LMS_DEV_SDR_MINI_PREFIX_NAME "LimeSDR-Mini"
|
#define LMS_DEV_SDR_MINI_PREFIX_NAME "LimeSDR-Mini"
|
||||||
#define LMS_DEV_NET_MICRO_PREFIX_NAME "LimeNET-Micro"
|
#define LMS_DEV_NET_MICRO_PREFIX_NAME "LimeNET-Micro"
|
||||||
|
|
||||||
|
/* Device parameter descriptor */
|
||||||
|
struct dev_desc {
|
||||||
|
/* Does LimeSuite allow switching the clock source for this device?
|
||||||
|
* LimeSDR-Mini does not have switches but needs soldering to select
|
||||||
|
* external/internal clock. Any call to LMS_SetClockFreq() will fail.
|
||||||
|
*/
|
||||||
|
bool clock_src_switchable;
|
||||||
|
/* Does LimeSuite allow using REF_INTERNAL for this device?
|
||||||
|
* LimeNET-Micro does not like selecting internal clock
|
||||||
|
*/
|
||||||
|
bool clock_src_int_usable;
|
||||||
|
/* Sample rate coef (without having TX/RX samples per symbol into account) */
|
||||||
|
double rate;
|
||||||
|
/* Sample rate coef (without having TX/RX samples per symbol into account), if multi-arfcn is enabled */
|
||||||
|
double rate_multiarfcn;
|
||||||
|
/* Coefficient multiplied by TX sample rate in order to shift Tx time */
|
||||||
|
double ts_offset_coef;
|
||||||
|
/* Coefficient multiplied by TX sample rate in order to shift Tx time, if multi-arfcn is enabled */
|
||||||
|
double ts_offset_coef_multiarfcn;
|
||||||
|
/* Device Name Prefix as presented by LimeSuite API LMS_GetDeviceInfo() */
|
||||||
|
std::string name_prefix;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<enum lms_dev_type, struct dev_desc> dev_param_map {
|
||||||
static const dev_map_t dev_param_map {
|
|
||||||
{ LMS_DEV_SDR_USB, { true, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_SDR_USB_PREFIX_NAME } },
|
{ LMS_DEV_SDR_USB, { true, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_SDR_USB_PREFIX_NAME } },
|
||||||
{ LMS_DEV_SDR_MINI, { false, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 8.2e-5, LMS_DEV_SDR_MINI_PREFIX_NAME } },
|
{ LMS_DEV_SDR_MINI, { false, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 8.2e-5, LMS_DEV_SDR_MINI_PREFIX_NAME } },
|
||||||
{ LMS_DEV_NET_MICRO, { true, false, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_NET_MICRO_PREFIX_NAME } },
|
{ LMS_DEV_NET_MICRO, { true, false, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_NET_MICRO_PREFIX_NAME } },
|
||||||
{ LMS_DEV_UNKNOWN, { true, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, "UNKNOWN" } },
|
{ LMS_DEV_UNKNOWN, { true, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, "UNKNOWN" } },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const power_map_t dev_band_nom_power_param_map {
|
typedef std::tuple<lms_dev_type, enum gsm_band> dev_band_key;
|
||||||
|
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
|
||||||
|
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
|
||||||
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_850), { 73.0, 11.2, -6.0 } },
|
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_850), { 73.0, 11.2, -6.0 } },
|
||||||
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_900), { 73.0, 10.8, -6.0 } },
|
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_900), { 73.0, 10.8, -6.0 } },
|
||||||
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_1800), { 65.0, -3.5, -17.0 } }, /* FIXME: OS#4583: 1800Mhz is failing above TxGain=65, which is around -3.5dBm (already < 0 dBm) */
|
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_1800), { 65.0, -3.5, -17.0 } }, /* FIXME: OS#4583: 1800Mhz is failing above TxGain=65, which is around -3.5dBm (already < 0 dBm) */
|
||||||
@@ -99,8 +122,8 @@ static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
|
|||||||
enum lms_dev_type dev_type = it->first;
|
enum lms_dev_type dev_type = it->first;
|
||||||
struct dev_desc desc = it->second;
|
struct dev_desc desc = it->second;
|
||||||
|
|
||||||
if (strncmp(device_info->deviceName, desc.desc_str.c_str(), desc.desc_str.length()) == 0) {
|
if (strncmp(device_info->deviceName, desc.name_prefix.c_str(), desc.name_prefix.length()) == 0) {
|
||||||
LOGC(DDEV, INFO) << "Device identified as " << desc.desc_str;
|
LOGC(DDEV, INFO) << "Device identified as " << desc.name_prefix;
|
||||||
return dev_type;
|
return dev_type;
|
||||||
}
|
}
|
||||||
it++;
|
it++;
|
||||||
@@ -108,10 +131,12 @@ static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
|
|||||||
return LMS_DEV_UNKNOWN;
|
return LMS_DEV_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
LMSDevice::LMSDevice(InterfaceType iface, const struct trx_cfg *cfg)
|
LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
|
||||||
: RadioDevice(iface, cfg),
|
const std::vector<std::string>& tx_paths,
|
||||||
band_manager(m_dev_type, dev_band_nom_power_param_map, dev_param_map, {LMS_DEV_SDR_USB, GSM_BAND_850}), m_lms_dev(NULL),
|
const std::vector<std::string>& rx_paths):
|
||||||
started(false), m_dev_type(LMS_DEV_UNKNOWN)
|
RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
|
||||||
|
m_lms_dev(NULL), started(false), band_ass_curr_sess(false), band((enum gsm_band)0),
|
||||||
|
m_dev_type(LMS_DEV_UNKNOWN)
|
||||||
{
|
{
|
||||||
LOGC(DDEV, INFO) << "creating LMS device...";
|
LOGC(DDEV, INFO) << "creating LMS device...";
|
||||||
|
|
||||||
@@ -198,7 +223,48 @@ int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::str
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
int LMSDevice::open()
|
void LMSDevice::assign_band_desc(enum gsm_band req_band)
|
||||||
|
{
|
||||||
|
dev_band_map_it it;
|
||||||
|
|
||||||
|
it = dev_band_nom_power_param_map.find(dev_band_key(m_dev_type, req_band));
|
||||||
|
if (it == dev_band_nom_power_param_map.end()) {
|
||||||
|
dev_desc desc = dev_param_map.at(m_dev_type);
|
||||||
|
LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device "
|
||||||
|
<< desc.name_prefix << " on band " << gsm_band_name(req_band)
|
||||||
|
<< ", using LimeSDR-USB ones as fallback";
|
||||||
|
it = dev_band_nom_power_param_map.find(dev_band_key(LMS_DEV_SDR_USB, req_band));
|
||||||
|
}
|
||||||
|
OSMO_ASSERT(it != dev_band_nom_power_param_map.end());
|
||||||
|
band_desc = it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LMSDevice::set_band(enum gsm_band req_band)
|
||||||
|
{
|
||||||
|
if (band_ass_curr_sess && req_band != band) {
|
||||||
|
LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
|
||||||
|
<< " different from previous band " << gsm_band_name(band);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req_band != band) {
|
||||||
|
band = req_band;
|
||||||
|
assign_band_desc(band);
|
||||||
|
}
|
||||||
|
band_ass_curr_sess = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void LMSDevice::get_dev_band_desc(dev_band_desc& desc)
|
||||||
|
{
|
||||||
|
if (band == 0) {
|
||||||
|
LOGC(DDEV, ERROR) << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
|
||||||
|
assign_band_desc(GSM_BAND_900);
|
||||||
|
}
|
||||||
|
desc = band_desc;
|
||||||
|
}
|
||||||
|
|
||||||
|
int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
|
||||||
{
|
{
|
||||||
lms_info_str_t* info_list;
|
lms_info_str_t* info_list;
|
||||||
lms_range_t range_sr;
|
lms_range_t range_sr;
|
||||||
@@ -226,9 +292,9 @@ int LMSDevice::open()
|
|||||||
for (i = 0; i < n; i++)
|
for (i = 0; i < n; i++)
|
||||||
LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i];
|
LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i];
|
||||||
|
|
||||||
dev_id = info_list_find(info_list, n, cfg->dev_args);
|
dev_id = info_list_find(info_list, n, args);
|
||||||
if (dev_id == -1) {
|
if (dev_id == -1) {
|
||||||
LOGC(DDEV, ERROR) << "No LMS device found with address '" << cfg->dev_args << "'";
|
LOGC(DDEV, ERROR) << "No LMS device found with address '" << args << "'";
|
||||||
delete[] info_list;
|
delete[] info_list;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -245,15 +311,14 @@ int LMSDevice::open()
|
|||||||
|
|
||||||
m_dev_type = parse_dev_type(m_lms_dev);
|
m_dev_type = parse_dev_type(m_lms_dev);
|
||||||
dev_desc = dev_param_map.at(m_dev_type);
|
dev_desc = dev_param_map.at(m_dev_type);
|
||||||
update_band_dev(m_dev_type);
|
|
||||||
|
|
||||||
if ((cfg->clock_ref != REF_EXTERNAL) && (cfg->clock_ref != REF_INTERNAL)) {
|
if ((ref != REF_EXTERNAL) && (ref != REF_INTERNAL)){
|
||||||
LOGC(DDEV, ERROR) << "Invalid reference type";
|
LOGC(DDEV, ERROR) << "Invalid reference type";
|
||||||
goto out_close;
|
goto out_close;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* if reference clock is external, setup must happen _before_ calling LMS_Init */
|
/* if reference clock is external, setup must happen _before_ calling LMS_Init */
|
||||||
if (cfg->clock_ref == REF_EXTERNAL) {
|
if (ref == REF_EXTERNAL) {
|
||||||
LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz";
|
LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz";
|
||||||
/* FIXME: Assume an external 10 MHz reference clock. make
|
/* FIXME: Assume an external 10 MHz reference clock. make
|
||||||
external reference frequency configurable */
|
external reference frequency configurable */
|
||||||
@@ -268,7 +333,7 @@ int LMSDevice::open()
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* if reference clock is internal, setup must happen _after_ calling LMS_Init */
|
/* if reference clock is internal, setup must happen _after_ calling LMS_Init */
|
||||||
if (cfg->clock_ref == REF_INTERNAL) {
|
if (ref == REF_INTERNAL) {
|
||||||
LOGC(DDEV, INFO) << "Setting Internal clock reference";
|
LOGC(DDEV, INFO) << "Setting Internal clock reference";
|
||||||
/* Internal freq param is not used */
|
/* Internal freq param is not used */
|
||||||
if (!do_clock_src_freq(REF_INTERNAL, 0))
|
if (!do_clock_src_freq(REF_INTERNAL, 0))
|
||||||
@@ -404,7 +469,7 @@ bool LMSDevice::stop()
|
|||||||
LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]);
|
LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
band_reset();
|
band_ass_curr_sess = false;
|
||||||
|
|
||||||
started = false;
|
started = false;
|
||||||
return true;
|
return true;
|
||||||
@@ -421,8 +486,8 @@ bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
|
|||||||
break;
|
break;
|
||||||
case REF_INTERNAL:
|
case REF_INTERNAL:
|
||||||
if (!dev_desc.clock_src_int_usable) {
|
if (!dev_desc.clock_src_int_usable) {
|
||||||
LOGC(DDEV, ERROR)
|
LOGC(DDEV, ERROR) << "Device type " << dev_desc.name_prefix
|
||||||
<< "Device type " << dev_desc.desc_str << " doesn't support internal reference clock";
|
<< " doesn't support internal reference clock";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
/* According to lms using LMS_CLOCK_EXTREF with a
|
/* According to lms using LMS_CLOCK_EXTREF with a
|
||||||
@@ -440,8 +505,8 @@ bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
|
|||||||
if (LMS_SetClockFreq(m_lms_dev, lms_clk_id, freq) < 0)
|
if (LMS_SetClockFreq(m_lms_dev, lms_clk_id, freq) < 0)
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
LOGC(DDEV, INFO)
|
LOGC(DDEV, INFO) << "Device type " << dev_desc.name_prefix
|
||||||
<< "Device type " << dev_desc.desc_str << " doesn't support switching clock source through SW";
|
<< " doesn't support switching clock source through SW";
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -934,6 +999,9 @@ bool LMSDevice::updateAlignment(TIMESTAMP timestamp)
|
|||||||
|
|
||||||
bool LMSDevice::setTxFreq(double wFreq, size_t chan)
|
bool LMSDevice::setTxFreq(double wFreq, size_t chan)
|
||||||
{
|
{
|
||||||
|
uint16_t req_arfcn;
|
||||||
|
enum gsm_band req_band;
|
||||||
|
|
||||||
if (chan >= chans) {
|
if (chan >= chans) {
|
||||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
||||||
return false;
|
return false;
|
||||||
@@ -941,7 +1009,18 @@ bool LMSDevice::setTxFreq(double wFreq, size_t chan)
|
|||||||
|
|
||||||
LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz";
|
LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz";
|
||||||
|
|
||||||
if (!update_band_from_freq(wFreq, chan, true))
|
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
|
||||||
|
if (req_arfcn == 0xffff) {
|
||||||
|
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
|
||||||
|
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
|
||||||
|
<< " Hz (ARFCN " << req_arfcn << " )";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_band(req_band))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
|
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
|
||||||
@@ -954,9 +1033,23 @@ bool LMSDevice::setTxFreq(double wFreq, size_t chan)
|
|||||||
|
|
||||||
bool LMSDevice::setRxFreq(double wFreq, size_t chan)
|
bool LMSDevice::setRxFreq(double wFreq, size_t chan)
|
||||||
{
|
{
|
||||||
|
uint16_t req_arfcn;
|
||||||
|
enum gsm_band req_band;
|
||||||
|
|
||||||
LOGCHAN(chan, DDEV, NOTICE) << "Setting Rx Freq to " << wFreq << " Hz";
|
LOGCHAN(chan, DDEV, NOTICE) << "Setting Rx Freq to " << wFreq << " Hz";
|
||||||
|
|
||||||
if (!update_band_from_freq(wFreq, chan, false))
|
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
|
||||||
|
if (req_arfcn == 0xffff) {
|
||||||
|
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
|
||||||
|
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq
|
||||||
|
<< " Hz (ARFCN " << req_arfcn << " )";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_band(req_band))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) {
|
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) {
|
||||||
@@ -967,15 +1060,18 @@ bool LMSDevice::setRxFreq(double wFreq, size_t chan)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
|
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
|
||||||
|
InterfaceType iface, size_t chans, double lo_offset,
|
||||||
|
const std::vector < std::string > &tx_paths,
|
||||||
|
const std::vector < std::string > &rx_paths)
|
||||||
{
|
{
|
||||||
if (cfg->tx_sps != cfg->rx_sps) {
|
if (tx_sps != rx_sps) {
|
||||||
LOGC(DDEV, ERROR) << "LMS requires tx_sps == rx_sps";
|
LOGC(DDEV, ERROR) << "LMS Requires tx_sps == rx_sps";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (cfg->offset != 0.0) {
|
if (lo_offset != 0.0) {
|
||||||
LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset";
|
LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return new LMSDevice(type, cfg);
|
return new LMSDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,13 +18,11 @@
|
|||||||
#ifndef _LMS_DEVICE_H_
|
#ifndef _LMS_DEVICE_H_
|
||||||
#define _LMS_DEVICE_H_
|
#define _LMS_DEVICE_H_
|
||||||
|
|
||||||
#include <map>
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#ifdef HAVE_CONFIG_H
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "radioDevice.h"
|
#include "radioDevice.h"
|
||||||
#include "bandmanager.h"
|
|
||||||
#include "smpl_buf.h"
|
#include "smpl_buf.h"
|
||||||
|
|
||||||
#include <sys/time.h>
|
#include <sys/time.h>
|
||||||
@@ -71,35 +69,8 @@ struct dev_band_desc {
|
|||||||
double rxgain2rssioffset_rel; /* dB */
|
double rxgain2rssioffset_rel; /* dB */
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Device parameter descriptor */
|
|
||||||
struct dev_desc {
|
|
||||||
/* Does LimeSuite allow switching the clock source for this device?
|
|
||||||
* LimeSDR-Mini does not have switches but needs soldering to select
|
|
||||||
* external/internal clock. Any call to LMS_SetClockFreq() will fail.
|
|
||||||
*/
|
|
||||||
bool clock_src_switchable;
|
|
||||||
/* Does LimeSuite allow using REF_INTERNAL for this device?
|
|
||||||
* LimeNET-Micro does not like selecting internal clock
|
|
||||||
*/
|
|
||||||
bool clock_src_int_usable;
|
|
||||||
/* Sample rate coef (without having TX/RX samples per symbol into account) */
|
|
||||||
double rate;
|
|
||||||
/* Sample rate coef (without having TX/RX samples per symbol into account), if multi-arfcn is enabled */
|
|
||||||
double rate_multiarfcn;
|
|
||||||
/* Coefficient multiplied by TX sample rate in order to shift Tx time */
|
|
||||||
double ts_offset_coef;
|
|
||||||
/* Coefficient multiplied by TX sample rate in order to shift Tx time, if multi-arfcn is enabled */
|
|
||||||
double ts_offset_coef_multiarfcn;
|
|
||||||
/* Device Name Prefix as presented by LimeSuite API LMS_GetDeviceInfo() */
|
|
||||||
std::string desc_str;
|
|
||||||
};
|
|
||||||
|
|
||||||
using dev_band_key_t = std::tuple<lms_dev_type, gsm_band>;
|
|
||||||
using power_map_t = std::map<dev_band_key_t, dev_band_desc>;
|
|
||||||
using dev_map_t = std::map<lms_dev_type, struct dev_desc>;
|
|
||||||
|
|
||||||
/** A class to handle a LimeSuite supported device */
|
/** A class to handle a LimeSuite supported device */
|
||||||
class LMSDevice:public RadioDevice, public band_manager<power_map_t, dev_map_t> {
|
class LMSDevice:public RadioDevice {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
lms_device_t *m_lms_dev;
|
lms_device_t *m_lms_dev;
|
||||||
@@ -116,6 +87,9 @@ private:
|
|||||||
TIMESTAMP ts_initial, ts_offset;
|
TIMESTAMP ts_initial, ts_offset;
|
||||||
|
|
||||||
std::vector<double> tx_gains, rx_gains;
|
std::vector<double> tx_gains, rx_gains;
|
||||||
|
bool band_ass_curr_sess; /* true if "band" was set after last POWEROFF */
|
||||||
|
enum gsm_band band;
|
||||||
|
struct dev_band_desc band_desc;
|
||||||
|
|
||||||
enum lms_dev_type m_dev_type;
|
enum lms_dev_type m_dev_type;
|
||||||
|
|
||||||
@@ -127,14 +101,19 @@ private:
|
|||||||
void update_stream_stats_rx(size_t chan, bool *overrun);
|
void update_stream_stats_rx(size_t chan, bool *overrun);
|
||||||
void update_stream_stats_tx(size_t chan, bool *underrun);
|
void update_stream_stats_tx(size_t chan, bool *underrun);
|
||||||
bool do_clock_src_freq(enum ReferenceType ref, double freq);
|
bool do_clock_src_freq(enum ReferenceType ref, double freq);
|
||||||
|
void get_dev_band_desc(dev_band_desc& desc);
|
||||||
|
bool set_band(enum gsm_band req_band);
|
||||||
|
void assign_band_desc(enum gsm_band req_band);
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** Object constructor */
|
/** Object constructor */
|
||||||
LMSDevice(InterfaceType iface, const struct trx_cfg *cfg);
|
LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
|
||||||
|
const std::vector<std::string>& tx_paths,
|
||||||
|
const std::vector<std::string>& rx_paths);
|
||||||
~LMSDevice();
|
~LMSDevice();
|
||||||
|
|
||||||
/** Instantiate the LMS */
|
/** Instantiate the LMS */
|
||||||
int open();
|
int open(const std::string &args, int ref, bool swap_channels);
|
||||||
|
|
||||||
/** Start the LMS */
|
/** Start the LMS */
|
||||||
bool start();
|
bool start();
|
||||||
@@ -142,8 +121,7 @@ public:
|
|||||||
/** Stop the LMS */
|
/** Stop the LMS */
|
||||||
bool stop();
|
bool stop();
|
||||||
|
|
||||||
enum TxWindowType getWindowType()
|
enum TxWindowType getWindowType() {
|
||||||
{
|
|
||||||
return TX_WINDOW_LMS1;
|
return TX_WINDOW_LMS1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,19 @@ extern "C" {
|
|||||||
* USRP1 with timestamps is not supported by UHD.
|
* USRP1 with timestamps is not supported by UHD.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
static const dev_map_t dev_param_map {
|
/* Device Type, Tx-SPS, Rx-SPS */
|
||||||
|
typedef std::tuple<uhd_dev_type, int, int> dev_key;
|
||||||
|
|
||||||
|
/* Device parameter descriptor */
|
||||||
|
struct dev_desc {
|
||||||
|
unsigned channels;
|
||||||
|
double mcr;
|
||||||
|
double rate;
|
||||||
|
double offset;
|
||||||
|
std::string str;
|
||||||
|
};
|
||||||
|
|
||||||
|
static const std::map<dev_key, dev_desc> dev_param_map {
|
||||||
{ std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } },
|
{ std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } },
|
||||||
{ std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } },
|
{ std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } },
|
||||||
{ std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } },
|
{ std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } },
|
||||||
@@ -117,7 +129,9 @@ static const dev_map_t dev_param_map {
|
|||||||
{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
|
{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
|
||||||
};
|
};
|
||||||
|
|
||||||
static const power_map_t dev_band_nom_power_param_map {
|
typedef std::tuple<uhd_dev_type, enum gsm_band> dev_band_key;
|
||||||
|
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
|
||||||
|
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
|
||||||
{ std::make_tuple(B200, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
|
{ std::make_tuple(B200, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
|
||||||
{ std::make_tuple(B200, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
|
{ std::make_tuple(B200, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
|
||||||
{ std::make_tuple(B200, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
|
{ std::make_tuple(B200, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
|
||||||
@@ -206,10 +220,15 @@ static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
|
|||||||
return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
|
return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
|
||||||
}
|
}
|
||||||
|
|
||||||
uhd_device::uhd_device(InterfaceType iface, const struct trx_cfg *cfg)
|
uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
|
||||||
: RadioDevice(iface, cfg), band_manager(dev_band_nom_power_param_map, dev_param_map), rx_gain_min(0.0),
|
InterfaceType iface, size_t chan_num, double lo_offset,
|
||||||
rx_gain_max(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), drop_cnt(0), prev_ts(0, 0),
|
const std::vector<std::string>& tx_paths,
|
||||||
ts_initial(0), ts_offset(0), async_event_thrd(NULL)
|
const std::vector<std::string>& rx_paths)
|
||||||
|
: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
|
||||||
|
rx_gain_min(0.0), rx_gain_max(0.0), band_ass_curr_sess(false),
|
||||||
|
band((enum gsm_band)0), tx_spp(0), rx_spp(0),
|
||||||
|
started(false), aligned(false), drop_cnt(0),
|
||||||
|
prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -221,6 +240,47 @@ uhd_device::~uhd_device()
|
|||||||
delete rx_buffers[i];
|
delete rx_buffers[i];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void uhd_device::assign_band_desc(enum gsm_band req_band)
|
||||||
|
{
|
||||||
|
dev_band_map_it it;
|
||||||
|
|
||||||
|
it = dev_band_nom_power_param_map.find(dev_band_key(dev_type, req_band));
|
||||||
|
if (it == dev_band_nom_power_param_map.end()) {
|
||||||
|
dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
|
||||||
|
LOGC(DDEV, ERROR) << "No Power parameters exist for device "
|
||||||
|
<< desc.str << " on band " << gsm_band_name(req_band)
|
||||||
|
<< ", using B210 ones as fallback";
|
||||||
|
it = dev_band_nom_power_param_map.find(dev_band_key(B210, req_band));
|
||||||
|
}
|
||||||
|
OSMO_ASSERT(it != dev_band_nom_power_param_map.end())
|
||||||
|
band_desc = it->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool uhd_device::set_band(enum gsm_band req_band)
|
||||||
|
{
|
||||||
|
if (band_ass_curr_sess && req_band != band) {
|
||||||
|
LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
|
||||||
|
<< " different from previous band " << gsm_band_name(band);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req_band != band) {
|
||||||
|
band = req_band;
|
||||||
|
assign_band_desc(band);
|
||||||
|
}
|
||||||
|
band_ass_curr_sess = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void uhd_device::get_dev_band_desc(dev_band_desc& desc)
|
||||||
|
{
|
||||||
|
if (band == 0) {
|
||||||
|
LOGC(DDEV, ERROR) << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
|
||||||
|
assign_band_desc(GSM_BAND_900);
|
||||||
|
}
|
||||||
|
desc = band_desc;
|
||||||
|
}
|
||||||
|
|
||||||
void uhd_device::init_gains()
|
void uhd_device::init_gains()
|
||||||
{
|
{
|
||||||
double tx_gain_min, tx_gain_max;
|
double tx_gain_min, tx_gain_max;
|
||||||
@@ -285,7 +345,7 @@ void uhd_device::set_rates()
|
|||||||
rx_rate = usrp_dev->get_rx_rate();
|
rx_rate = usrp_dev->get_rx_rate();
|
||||||
|
|
||||||
ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
|
ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
|
||||||
LOGC(DDEV, INFO) << "Rates configured for " << desc.desc_str;
|
LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
|
||||||
}
|
}
|
||||||
|
|
||||||
double uhd_device::setRxGain(double db, size_t chan)
|
double uhd_device::setRxGain(double db, size_t chan)
|
||||||
@@ -295,9 +355,6 @@ double uhd_device::setRxGain(double db, size_t chan)
|
|||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg->overrides.ul_gain_override)
|
|
||||||
return rx_gains[chan];
|
|
||||||
|
|
||||||
usrp_dev->set_rx_gain(db, chan);
|
usrp_dev->set_rx_gain(db, chan);
|
||||||
rx_gains[chan] = usrp_dev->get_rx_gain(chan);
|
rx_gains[chan] = usrp_dev->get_rx_gain(chan);
|
||||||
|
|
||||||
@@ -340,9 +397,6 @@ double uhd_device::setPowerAttenuation(int atten, size_t chan) {
|
|||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg->overrides.dl_gain_override)
|
|
||||||
return atten; // ensures caller does not apply digital attenuation
|
|
||||||
|
|
||||||
get_dev_band_desc(desc);
|
get_dev_band_desc(desc);
|
||||||
tx_power = desc.nom_out_tx_power - atten;
|
tx_power = desc.nom_out_tx_power - atten;
|
||||||
db = TxPower2TxGain(desc, tx_power);
|
db = TxPower2TxGain(desc, tx_power);
|
||||||
@@ -494,7 +548,7 @@ void uhd_device::set_channels(bool swap)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int uhd_device::open()
|
int uhd_device::open(const std::string &args, int ref, bool swap_channels)
|
||||||
{
|
{
|
||||||
const char *refstr;
|
const char *refstr;
|
||||||
int clock_lock_attempts = 15;
|
int clock_lock_attempts = 15;
|
||||||
@@ -510,10 +564,10 @@ int uhd_device::open()
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Find UHD devices
|
// Find UHD devices
|
||||||
uhd::device_addr_t addr(cfg->dev_args);
|
uhd::device_addr_t addr(args);
|
||||||
uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
|
uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
|
||||||
if (dev_addrs.size() == 0) {
|
if (dev_addrs.size() == 0) {
|
||||||
LOGC(DDEV, ALERT) << "No UHD devices found with address '" << cfg->dev_args << "'";
|
LOGC(DDEV, ALERT) << "No UHD devices found with address '" << args << "'";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -522,7 +576,7 @@ int uhd_device::open()
|
|||||||
try {
|
try {
|
||||||
usrp_dev = uhd::usrp::multi_usrp::make(addr);
|
usrp_dev = uhd::usrp::multi_usrp::make(addr);
|
||||||
} catch(uhd::key_error::exception &e) {
|
} catch(uhd::key_error::exception &e) {
|
||||||
LOGC(DDEV, ALERT) << "UHD make failed, device " << cfg->dev_args << ", exception:\n" << e.what();
|
LOGC(DDEV, ALERT) << "UHD make failed, device " << args << ", exception:\n" << e.what();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -530,15 +584,13 @@ int uhd_device::open()
|
|||||||
if (!parse_dev_type())
|
if (!parse_dev_type())
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
update_band_dev(dev_key(dev_type, tx_sps, rx_sps));
|
|
||||||
|
|
||||||
if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
|
if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
|
||||||
LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater";
|
LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater";
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
set_channels(cfg->swap_channels);
|
set_channels(swap_channels);
|
||||||
} catch (const std::exception &e) {
|
} catch (const std::exception &e) {
|
||||||
LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
|
LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
|
||||||
return -1;
|
return -1;
|
||||||
@@ -555,7 +607,7 @@ int uhd_device::open()
|
|||||||
rx_gains.resize(chans);
|
rx_gains.resize(chans);
|
||||||
rx_buffers.resize(chans);
|
rx_buffers.resize(chans);
|
||||||
|
|
||||||
switch (cfg->clock_ref) {
|
switch (ref) {
|
||||||
case REF_INTERNAL:
|
case REF_INTERNAL:
|
||||||
refstr = "internal";
|
refstr = "internal";
|
||||||
break;
|
break;
|
||||||
@@ -634,32 +686,6 @@ int uhd_device::open()
|
|||||||
// Print configuration
|
// Print configuration
|
||||||
LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
|
LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
|
||||||
|
|
||||||
if (cfg->overrides.dl_freq_override) {
|
|
||||||
uhd::tune_request_t treq_tx = uhd::tune_request_t(cfg->overrides.dl_freq, 0);
|
|
||||||
auto tres = usrp_dev->set_tx_freq(treq_tx, 0);
|
|
||||||
tx_freqs[0] = usrp_dev->get_tx_freq(0);
|
|
||||||
LOGCHAN(0, DDEV, INFO) << "OVERRIDE set_freq(" << tx_freqs[0] << ", TX): " << tres.to_pp_string() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cfg->overrides.ul_freq_override) {
|
|
||||||
uhd::tune_request_t treq_rx = uhd::tune_request_t(cfg->overrides.ul_freq, 0);
|
|
||||||
auto tres = usrp_dev->set_rx_freq(treq_rx, 0);
|
|
||||||
rx_freqs[0] = usrp_dev->get_rx_freq(0);
|
|
||||||
LOGCHAN(0, DDEV, INFO) << "OVERRIDE set_freq(" << rx_freqs[0] << ", RX): " << tres.to_pp_string() << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cfg->overrides.ul_gain_override) {
|
|
||||||
usrp_dev->set_rx_gain(cfg->overrides.ul_gain, 0);
|
|
||||||
rx_gains[0] = usrp_dev->get_rx_gain(0);
|
|
||||||
LOGCHAN(0, DDEV, INFO) << " OVERRIDE RX gain:" << rx_gains[0] << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cfg->overrides.dl_gain_override) {
|
|
||||||
usrp_dev->set_tx_gain(cfg->overrides.dl_gain, 0);
|
|
||||||
tx_gains[0] = usrp_dev->get_tx_gain(0);
|
|
||||||
LOGCHAN(0, DDEV, INFO) << " OVERRIDE TX gain:" << tx_gains[0] << std::endl;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (iface == MULTI_ARFCN)
|
if (iface == MULTI_ARFCN)
|
||||||
return MULTI_ARFCN;
|
return MULTI_ARFCN;
|
||||||
|
|
||||||
@@ -770,7 +796,7 @@ bool uhd_device::stop()
|
|||||||
for (size_t i = 0; i < rx_buffers.size(); i++)
|
for (size_t i = 0; i < rx_buffers.size(); i++)
|
||||||
rx_buffers[i]->reset();
|
rx_buffers[i]->reset();
|
||||||
|
|
||||||
band_reset();
|
band_ass_curr_sess = false;
|
||||||
|
|
||||||
started = false;
|
started = false;
|
||||||
return true;
|
return true;
|
||||||
@@ -1010,22 +1036,17 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
|
|||||||
{
|
{
|
||||||
std::vector<double> freqs;
|
std::vector<double> freqs;
|
||||||
uhd::tune_result_t tres;
|
uhd::tune_result_t tres;
|
||||||
std::string str_dir = tx ? "Tx" : "Rx";
|
|
||||||
|
|
||||||
if (cfg->overrides.dl_freq_override || cfg->overrides.ul_freq_override)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!update_band_from_freq(freq, chan, tx))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
uhd::tune_request_t treq = select_freq(freq, chan, tx);
|
uhd::tune_request_t treq = select_freq(freq, chan, tx);
|
||||||
|
std::string str_dir;
|
||||||
|
|
||||||
if (tx) {
|
if (tx) {
|
||||||
tres = usrp_dev->set_tx_freq(treq, chan);
|
tres = usrp_dev->set_tx_freq(treq, chan);
|
||||||
tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
|
tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
|
||||||
|
str_dir = "Tx";
|
||||||
} else {
|
} else {
|
||||||
tres = usrp_dev->set_rx_freq(treq, chan);
|
tres = usrp_dev->set_rx_freq(treq, chan);
|
||||||
rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
|
rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
|
||||||
|
str_dir = "Rx";
|
||||||
}
|
}
|
||||||
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
|
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
|
||||||
|
|
||||||
@@ -1055,20 +1076,59 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
|
|||||||
|
|
||||||
bool uhd_device::setTxFreq(double wFreq, size_t chan)
|
bool uhd_device::setTxFreq(double wFreq, size_t chan)
|
||||||
{
|
{
|
||||||
|
uint16_t req_arfcn;
|
||||||
|
enum gsm_band req_band;
|
||||||
|
|
||||||
if (chan >= tx_freqs.size()) {
|
if (chan >= tx_freqs.size()) {
|
||||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
ScopedLock lock(tune_lock);
|
||||||
|
|
||||||
return set_freq(wFreq, chan, true);
|
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
|
||||||
|
if (req_arfcn == 0xffff) {
|
||||||
|
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
|
||||||
|
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
|
||||||
|
<< " Hz (ARFCN " << req_arfcn << " )";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_band(req_band))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!set_freq(wFreq, chan, true))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool uhd_device::setRxFreq(double wFreq, size_t chan)
|
bool uhd_device::setRxFreq(double wFreq, size_t chan)
|
||||||
{
|
{
|
||||||
|
uint16_t req_arfcn;
|
||||||
|
enum gsm_band req_band;
|
||||||
|
|
||||||
if (chan >= rx_freqs.size()) {
|
if (chan >= rx_freqs.size()) {
|
||||||
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
ScopedLock lock(tune_lock);
|
||||||
|
|
||||||
|
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
|
||||||
|
if (req_arfcn == 0xffff) {
|
||||||
|
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
|
||||||
|
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq
|
||||||
|
<< " Hz (ARFCN " << req_arfcn << " )";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!set_band(req_band))
|
||||||
|
return false;
|
||||||
|
|
||||||
return set_freq(wFreq, chan, false);
|
return set_freq(wFreq, chan, false);
|
||||||
}
|
}
|
||||||
@@ -1318,8 +1378,11 @@ std::string uhd_device::str_code(uhd::async_metadata_t metadata)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#ifndef IPCMAGIC
|
#ifndef IPCMAGIC
|
||||||
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
|
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
|
||||||
|
InterfaceType iface, size_t chans, double lo_offset,
|
||||||
|
const std::vector<std::string>& tx_paths,
|
||||||
|
const std::vector<std::string>& rx_paths)
|
||||||
{
|
{
|
||||||
return new uhd_device(type, cfg);
|
return new uhd_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|||||||
@@ -30,7 +30,6 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "bandmanager.h"
|
|
||||||
#include "radioDevice.h"
|
#include "radioDevice.h"
|
||||||
#include "smpl_buf.h"
|
#include "smpl_buf.h"
|
||||||
|
|
||||||
@@ -72,19 +71,6 @@ struct dev_band_desc {
|
|||||||
double rxgain2rssioffset_rel; /* dB */
|
double rxgain2rssioffset_rel; /* dB */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct dev_desc {
|
|
||||||
unsigned channels;
|
|
||||||
double mcr;
|
|
||||||
double rate;
|
|
||||||
double offset;
|
|
||||||
std::string desc_str;
|
|
||||||
};
|
|
||||||
|
|
||||||
using dev_key = std::tuple<uhd_dev_type, int, int>;
|
|
||||||
using dev_band_key = std::tuple<uhd_dev_type, enum gsm_band>;
|
|
||||||
using power_map_t = std::map<dev_band_key, dev_band_desc>;
|
|
||||||
using dev_map_t = std::map<dev_key, dev_desc>;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
uhd_device - UHD implementation of the Device interface. Timestamped samples
|
uhd_device - UHD implementation of the Device interface. Timestamped samples
|
||||||
are sent to and received from the device. An intermediate buffer
|
are sent to and received from the device. An intermediate buffer
|
||||||
@@ -92,19 +78,19 @@ using dev_map_t = std::map<dev_key, dev_desc>;
|
|||||||
Events and errors such as underruns are reported asynchronously
|
Events and errors such as underruns are reported asynchronously
|
||||||
by the device and received in a separate thread.
|
by the device and received in a separate thread.
|
||||||
*/
|
*/
|
||||||
class uhd_device : public RadioDevice, public band_manager<power_map_t, dev_map_t> {
|
class uhd_device : public RadioDevice {
|
||||||
public:
|
public:
|
||||||
uhd_device(InterfaceType iface, const struct trx_cfg *cfg);
|
uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
|
||||||
|
size_t chan_num, double offset,
|
||||||
|
const std::vector<std::string>& tx_paths,
|
||||||
|
const std::vector<std::string>& rx_paths);
|
||||||
~uhd_device();
|
~uhd_device();
|
||||||
|
|
||||||
int open();
|
int open(const std::string &args, int ref, bool swap_channels);
|
||||||
bool start();
|
bool start();
|
||||||
bool stop();
|
bool stop();
|
||||||
bool restart();
|
bool restart();
|
||||||
enum TxWindowType getWindowType()
|
enum TxWindowType getWindowType() { return tx_window; }
|
||||||
{
|
|
||||||
return tx_window;
|
|
||||||
}
|
|
||||||
|
|
||||||
int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
||||||
TIMESTAMP timestamp, bool *underrun);
|
TIMESTAMP timestamp, bool *underrun);
|
||||||
@@ -174,6 +160,9 @@ protected:
|
|||||||
|
|
||||||
std::vector<double> tx_gains, rx_gains;
|
std::vector<double> tx_gains, rx_gains;
|
||||||
std::vector<double> tx_freqs, rx_freqs;
|
std::vector<double> tx_freqs, rx_freqs;
|
||||||
|
bool band_ass_curr_sess; /* true if "band" was set after last POWEROFF */
|
||||||
|
enum gsm_band band;
|
||||||
|
struct dev_band_desc band_desc;
|
||||||
size_t tx_spp, rx_spp;
|
size_t tx_spp, rx_spp;
|
||||||
|
|
||||||
bool started;
|
bool started;
|
||||||
@@ -202,6 +191,10 @@ protected:
|
|||||||
|
|
||||||
uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
|
uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
|
||||||
bool set_freq(double freq, size_t chan, bool tx);
|
bool set_freq(double freq, size_t chan, bool tx);
|
||||||
|
void get_dev_band_desc(dev_band_desc& desc);
|
||||||
|
bool set_band(enum gsm_band req_band);
|
||||||
|
void assign_band_desc(enum gsm_band req_band);
|
||||||
|
|
||||||
Thread *async_event_thrd;
|
Thread *async_event_thrd;
|
||||||
|
Mutex tune_lock;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -60,7 +60,11 @@ const dboardConfigType dboardConfig = TXA_RXB;
|
|||||||
|
|
||||||
const double USRPDevice::masterClockRate = 52.0e6;
|
const double USRPDevice::masterClockRate = 52.0e6;
|
||||||
|
|
||||||
USRPDevice::USRPDevice(InterfaceType iface, const struct trx_cfg *cfg) : RadioDevice(iface, cfg)
|
USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
|
||||||
|
size_t chan_num, double lo_offset,
|
||||||
|
const std::vector<std::string>& tx_paths,
|
||||||
|
const std::vector<std::string>& rx_paths):
|
||||||
|
RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths)
|
||||||
{
|
{
|
||||||
LOGC(DDEV, INFO) << "creating USRP device...";
|
LOGC(DDEV, INFO) << "creating USRP device...";
|
||||||
|
|
||||||
@@ -90,7 +94,7 @@ USRPDevice::USRPDevice(InterfaceType iface, const struct trx_cfg *cfg) : RadioDe
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
int USRPDevice::open()
|
int USRPDevice::open(const std::string &, int, bool)
|
||||||
{
|
{
|
||||||
writeLock.unlock();
|
writeLock.unlock();
|
||||||
|
|
||||||
@@ -583,7 +587,8 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
|
|||||||
{
|
{
|
||||||
#ifndef SWLOOPBACK
|
#ifndef SWLOOPBACK
|
||||||
short data[] = {0x00,0x02,0x00,0x00};
|
short data[] = {0x00,0x02,0x00,0x00};
|
||||||
/* FIXME: big endian */
|
uint32_t *wordPtr = (uint32_t *) data;
|
||||||
|
*wordPtr = host_to_usrp_u32(*wordPtr);
|
||||||
bool tmpUnderrun;
|
bool tmpUnderrun;
|
||||||
|
|
||||||
std::vector<short *> buf(1, data);
|
std::vector<short *> buf(1, data);
|
||||||
@@ -654,19 +659,22 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
|
|||||||
bool USRPDevice::setRxFreq(double wFreq) { return true;};
|
bool USRPDevice::setRxFreq(double wFreq) { return true;};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
|
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
|
||||||
|
InterfaceType iface, size_t chans, double lo_offset,
|
||||||
|
const std::vector<std::string>& tx_paths,
|
||||||
|
const std::vector<std::string>& rx_paths)
|
||||||
{
|
{
|
||||||
if (cfg->tx_sps != cfg->rx_sps) {
|
if (tx_sps != rx_sps) {
|
||||||
LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
|
LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (cfg->num_chans != 1) {
|
if (chans != 1) {
|
||||||
LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
|
LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
if (cfg->offset != 0.0) {
|
if (lo_offset != 0.0) {
|
||||||
LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
|
LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
return new USRPDevice(type, cfg);
|
return new USRPDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,10 +104,12 @@ private:
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
/** Object constructor */
|
/** Object constructor */
|
||||||
USRPDevice(InterfaceType iface, const struct trx_cfg *cfg);
|
USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
|
||||||
|
const std::vector<std::string>& tx_paths,
|
||||||
|
const std::vector<std::string>& rx_paths);
|
||||||
|
|
||||||
/** Instantiate the USRP */
|
/** Instantiate the USRP */
|
||||||
int open();
|
int open(const std::string &, int, bool);
|
||||||
|
|
||||||
/** Start the USRP */
|
/** Start the USRP */
|
||||||
bool start();
|
bool start();
|
||||||
@@ -115,10 +117,7 @@ private:
|
|||||||
/** Stop the USRP */
|
/** Stop the USRP */
|
||||||
bool stop();
|
bool stop();
|
||||||
|
|
||||||
enum TxWindowType getWindowType()
|
enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
|
||||||
{
|
|
||||||
return TX_WINDOW_USRP1;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Read samples from the USRP.
|
Read samples from the USRP.
|
||||||
|
|||||||
@@ -38,7 +38,6 @@
|
|||||||
#define STEALING_BIT 1
|
#define STEALING_BIT 1
|
||||||
#define N_TRAIN_BITS 26
|
#define N_TRAIN_BITS 26
|
||||||
#define N_SYNC_BITS 64
|
#define N_SYNC_BITS 64
|
||||||
#define N_ACCESS_BITS 41
|
|
||||||
#define USEFUL_BITS 142 //(2*(DATA_BITS+STEALING_BIT) + N_TRAIN_BITS )
|
#define USEFUL_BITS 142 //(2*(DATA_BITS+STEALING_BIT) + N_TRAIN_BITS )
|
||||||
#define FCCH_BITS USEFUL_BITS
|
#define FCCH_BITS USEFUL_BITS
|
||||||
#define BURST_SIZE (USEFUL_BITS+2*TAIL_BITS)
|
#define BURST_SIZE (USEFUL_BITS+2*TAIL_BITS)
|
||||||
@@ -74,12 +73,6 @@ static const unsigned char SYNC_BITS[] = {
|
|||||||
0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1
|
0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1
|
||||||
};
|
};
|
||||||
|
|
||||||
static const unsigned char ACCESS_BITS [] = {
|
|
||||||
0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
|
|
||||||
0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0,
|
|
||||||
0, 1, 1, 1, 1, 0, 0, 0
|
|
||||||
};
|
|
||||||
|
|
||||||
const unsigned FCCH_FRAMES[] = { 0, 10, 20, 30, 40 };
|
const unsigned FCCH_FRAMES[] = { 0, 10, 20, 30, 40 };
|
||||||
const unsigned SCH_FRAMES[] = { 1, 11, 21, 31, 41 };
|
const unsigned SCH_FRAMES[] = { 1, 11, 21, 31, 41 };
|
||||||
|
|
||||||
|
|||||||
@@ -39,13 +39,13 @@
|
|||||||
#include "viterbi_detector.h"
|
#include "viterbi_detector.h"
|
||||||
#include "grgsm_vitac.h"
|
#include "grgsm_vitac.h"
|
||||||
|
|
||||||
gr_complex d_acc_training_seq[N_ACCESS_BITS]; ///<encoded training sequence of a RACH burst
|
//signalVector mChanResp;
|
||||||
gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a SCH burst
|
gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a SCH burst
|
||||||
gr_complex d_norm_training_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS]; ///<encoded training sequences of a normal and dummy burst
|
gr_complex d_norm_training_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS]; ///<encoded training sequences of a normal and dummy burst
|
||||||
const int d_chan_imp_length = CHAN_IMP_RESP_LENGTH;
|
const int d_chan_imp_length = CHAN_IMP_RESP_LENGTH;
|
||||||
|
|
||||||
void initvita()
|
void initvita() {
|
||||||
{
|
|
||||||
/**
|
/**
|
||||||
* Prepare SCH sequence bits
|
* Prepare SCH sequence bits
|
||||||
*
|
*
|
||||||
@@ -53,72 +53,59 @@ void initvita()
|
|||||||
* Burst and two guard periods
|
* Burst and two guard periods
|
||||||
* (one guard period is an arbitrary overlap)
|
* (one guard period is an arbitrary overlap)
|
||||||
*/
|
*/
|
||||||
gmsk_mapper(SYNC_BITS, N_SYNC_BITS, 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)
|
for (auto &i : d_sch_training_seq)
|
||||||
i = conj(i);
|
i = conj(i);
|
||||||
|
|
||||||
/* ab */
|
|
||||||
gmsk_mapper(ACCESS_BITS, N_ACCESS_BITS, d_acc_training_seq, gr_complex(0.0, -1.0));
|
|
||||||
for (auto &i : d_acc_training_seq)
|
|
||||||
i = conj(i);
|
|
||||||
|
|
||||||
/* Prepare bits of training sequences */
|
/* Prepare bits of training sequences */
|
||||||
for (int i = 0; i < TRAIN_SEQ_NUM; i++) {
|
for (int i = 0; i < TRAIN_SEQ_NUM; i++) {
|
||||||
/**
|
/**
|
||||||
* If first bit of the sequence is 0
|
* If first bit of the sequence is 0
|
||||||
* => first symbol is 1, else -1
|
* => 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);
|
gr_complex startpoint = train_seq[i][0] == 0 ?
|
||||||
gmsk_mapper(train_seq[i], N_TRAIN_BITS, d_norm_training_seq[i], startpoint);
|
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])
|
for (auto &i : d_norm_training_seq[i])
|
||||||
i = conj(i);
|
i = conj(i);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
template <unsigned int burst_size>
|
MULTI_VER_TARGET_ATTR
|
||||||
NO_UBSAN static void detect_burst_generic(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start,
|
void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
|
||||||
char *output_binary, int ss)
|
|
||||||
{
|
{
|
||||||
std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
|
std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
|
||||||
unsigned int stop_states[2] = { 4, 12 };
|
unsigned int stop_states[2] = { 4, 12 };
|
||||||
gr_complex filtered_burst[burst_size];
|
gr_complex filtered_burst[BURST_SIZE];
|
||||||
gr_complex rhh[CHAN_IMP_RESP_LENGTH];
|
gr_complex rhh[CHAN_IMP_RESP_LENGTH];
|
||||||
float output[burst_size];
|
float output[BURST_SIZE];
|
||||||
int start_state = ss;
|
int start_state = 3;
|
||||||
|
|
||||||
|
// 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);
|
autocorrelation(chan_imp_resp, &rhh_temp[0], d_chan_imp_length * d_OSR);
|
||||||
for (int ii = 0; ii < d_chan_imp_length; ii++)
|
for (int ii = 0; ii < d_chan_imp_length; ii++)
|
||||||
rhh[ii] = conj(rhh_temp[ii * d_OSR]);
|
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 (unsigned int i = 0; i < burst_size; i++)
|
for (int i = 0; i < BURST_SIZE; i++)
|
||||||
output_binary[i] = (char)(output[i] * -127); // pre flip bits!
|
output_binary[i] = output[i] * -127; // pre flip bits!
|
||||||
}
|
}
|
||||||
|
|
||||||
NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary,
|
void
|
||||||
int ss)
|
gmsk_mapper(const unsigned char* input,
|
||||||
{
|
int nitems, gr_complex* gmsk_output, gr_complex start_point)
|
||||||
return detect_burst_generic<BURST_SIZE>(input, chan_imp_resp, burst_start, output_binary, ss);
|
|
||||||
}
|
|
||||||
NO_UBSAN void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary,
|
|
||||||
int ss)
|
|
||||||
{
|
|
||||||
return detect_burst_generic<8 + 41 + 36 + 3>(input, chan_imp_resp, burst_start, output_binary, ss);
|
|
||||||
}
|
|
||||||
|
|
||||||
NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
|
|
||||||
{
|
|
||||||
return detect_burst_nb(input, chan_imp_resp, burst_start, output_binary, 3);
|
|
||||||
}
|
|
||||||
NO_UBSAN void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
|
|
||||||
{
|
|
||||||
return detect_burst_ab(input, chan_imp_resp, burst_start, output_binary, 3);
|
|
||||||
}
|
|
||||||
|
|
||||||
void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output, gr_complex start_point)
|
|
||||||
{
|
{
|
||||||
gr_complex j = gr_complex(0.0, 1.0);
|
gr_complex j = gr_complex(0.0, 1.0);
|
||||||
gmsk_output[0] = start_point;
|
gmsk_output[0] = start_point;
|
||||||
@@ -135,13 +122,16 @@ void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output
|
|||||||
encoded_symbol = current_symbol * previous_symbol;
|
encoded_symbol = current_symbol * previous_symbol;
|
||||||
|
|
||||||
/* And do GMSK mapping */
|
/* 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;
|
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);
|
gr_complex result(0.0, 0.0);
|
||||||
|
|
||||||
@@ -152,7 +142,9 @@ gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_c
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Computes autocorrelation for positive arguments */
|
/* 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--) {
|
for (int k = nitems - 1; k >= 0; k--) {
|
||||||
out[k] = gr_complex(0, 0);
|
out[k] = gr_complex(0, 0);
|
||||||
@@ -161,7 +153,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++) {
|
for (int n = 0; n < nitems; n++) {
|
||||||
int a = n * d_OSR;
|
int a = n * d_OSR;
|
||||||
@@ -176,45 +170,66 @@ inline void mafi(const gr_complex *input, int nitems, gr_complex *filter, int fi
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int search_start_pos, int search_stop_pos,
|
int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int search_center, int search_start_pos,
|
||||||
gr_complex *tseq, int tseqlen, float *corr_max)
|
int search_stop_pos, gr_complex *tseq, int tseqlen, float *corr_max)
|
||||||
{
|
{
|
||||||
const int num_search_windows = search_stop_pos - search_start_pos;
|
std::vector<gr_complex> correlation_buffer;
|
||||||
const int power_search_window_len = d_chan_imp_length * d_OSR;
|
|
||||||
std::vector<float> window_energy_buffer;
|
std::vector<float> window_energy_buffer;
|
||||||
std::vector<float> power_buffer;
|
std::vector<float> power_buffer;
|
||||||
std::vector<gr_complex> correlation_buffer;
|
|
||||||
|
|
||||||
power_buffer.reserve(num_search_windows);
|
for (int ii = search_start_pos; ii < search_stop_pos; ii++) {
|
||||||
correlation_buffer.reserve(num_search_windows);
|
gr_complex correlation = correlate_sequence(tseq, tseqlen, &input[ii]);
|
||||||
window_energy_buffer.reserve(num_search_windows);
|
|
||||||
|
|
||||||
for (int ii = 0; ii < num_search_windows; ii++) {
|
|
||||||
gr_complex correlation = correlate_sequence(tseq, tseqlen, &input[search_start_pos + ii]);
|
|
||||||
correlation_buffer.push_back(correlation);
|
correlation_buffer.push_back(correlation);
|
||||||
power_buffer.push_back(std::pow(abs(correlation), 2));
|
power_buffer.push_back(std::pow(abs(correlation), 2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int strongest_corr_nr = max_element(power_buffer.begin(), power_buffer.end()) - power_buffer.begin();
|
||||||
|
|
||||||
/* Compute window energies */
|
/* Compute window energies */
|
||||||
float windowSum = 0;
|
auto window_energy_start_offset = strongest_corr_nr - 6 * d_OSR;
|
||||||
|
window_energy_start_offset = window_energy_start_offset < 0 ? 0 : window_energy_start_offset; //can end up out of range..
|
||||||
|
auto window_energy_end_offset = strongest_corr_nr + 6 * d_OSR + d_chan_imp_length * d_OSR;
|
||||||
|
auto iter = power_buffer.begin() + window_energy_start_offset;
|
||||||
|
auto iter_end = power_buffer.begin() + window_energy_end_offset;
|
||||||
|
while (iter != iter_end) {
|
||||||
|
std::vector<float>::iterator iter_ii = iter;
|
||||||
|
bool loop_end = false;
|
||||||
|
float energy = 0;
|
||||||
|
|
||||||
// first window
|
int len = d_chan_imp_length * d_OSR;
|
||||||
for (int i = 0; i < power_search_window_len; i++) {
|
for (int ii = 0; ii < len; ii++, iter_ii++) {
|
||||||
windowSum += power_buffer[i];
|
if (iter_ii == power_buffer.end()) {
|
||||||
}
|
loop_end = true;
|
||||||
window_energy_buffer.push_back(windowSum);
|
break;
|
||||||
|
|
||||||
// slide windows
|
|
||||||
for (int i = power_search_window_len; i < num_search_windows; i++) {
|
|
||||||
windowSum += power_buffer[i] - power_buffer[i - power_search_window_len];
|
|
||||||
window_energy_buffer.push_back(windowSum);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int strongest_window_nr = std::max_element(window_energy_buffer.begin(), window_energy_buffer.end()) -
|
energy += (*iter_ii);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loop_end)
|
||||||
|
break;
|
||||||
|
|
||||||
|
window_energy_buffer.push_back(energy);
|
||||||
|
iter++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Calculate the strongest window number */
|
||||||
|
int strongest_window_nr = window_energy_start_offset +
|
||||||
|
max_element(window_energy_buffer.begin(), window_energy_buffer.end()) -
|
||||||
window_energy_buffer.begin();
|
window_energy_buffer.begin();
|
||||||
|
|
||||||
|
// auto window_search_start = window_energy_buffer.begin() + strongest_corr_nr - 5* d_OSR;
|
||||||
|
// auto window_search_end = window_energy_buffer.begin() + strongest_corr_nr + 10* d_OSR;
|
||||||
|
// window_search_end = window_search_end >= window_energy_buffer.end() ? window_energy_buffer.end() : window_search_end;
|
||||||
|
|
||||||
|
// /* Calculate the strongest window number */
|
||||||
|
// int strongest_window_nr = max_element(window_search_start, window_search_end /* - d_chan_imp_length * d_OSR*/) - window_energy_buffer.begin();
|
||||||
|
|
||||||
|
// if (strongest_window_nr < 0)
|
||||||
|
// strongest_window_nr = 0;
|
||||||
|
|
||||||
float max_correlation = 0;
|
float max_correlation = 0;
|
||||||
for (int ii = 0; ii < power_search_window_len; ii++) {
|
for (int ii = 0; ii < d_chan_imp_length * d_OSR; ii++) {
|
||||||
gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
|
gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
|
||||||
if (abs(correlation) > max_correlation)
|
if (abs(correlation) > max_correlation)
|
||||||
max_correlation = abs(correlation);
|
max_correlation = abs(correlation);
|
||||||
@@ -227,27 +242,7 @@ int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int se
|
|||||||
* Compute first sample position, which corresponds
|
* Compute first sample position, which corresponds
|
||||||
* to the first sample of the impulse response
|
* to the first sample of the impulse response
|
||||||
*/
|
*/
|
||||||
return search_start_pos + strongest_window_nr;
|
return search_start_pos + strongest_window_nr - search_center * d_OSR;
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
8 ext tail bits
|
|
||||||
41 sync seq
|
|
||||||
36 encrypted bits
|
|
||||||
3 tail bits
|
|
||||||
68.25 extended tail bits (!)
|
|
||||||
|
|
||||||
center at 8+5 (actually known tb -> known isi, start at 8?) FIXME
|
|
||||||
*/
|
|
||||||
int get_access_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int max_delay)
|
|
||||||
{
|
|
||||||
const int search_center = 8 + 5;
|
|
||||||
const int search_start_pos = (search_center - 5) * d_OSR + 1;
|
|
||||||
const int search_stop_pos = (search_center + 5 + d_chan_imp_length + max_delay) * d_OSR;
|
|
||||||
const auto tseq = &d_acc_training_seq[TRAIN_BEGINNING];
|
|
||||||
const auto tseqlen = N_ACCESS_BITS - (2 * TRAIN_BEGINNING);
|
|
||||||
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
|
|
||||||
search_center * d_OSR;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -265,8 +260,8 @@ int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, f
|
|||||||
const int search_stop_pos = (search_center + 5 + d_chan_imp_length) * d_OSR;
|
const int search_stop_pos = (search_center + 5 + d_chan_imp_length) * d_OSR;
|
||||||
const auto tseq = &d_norm_training_seq[bcc][TRAIN_BEGINNING];
|
const auto tseq = &d_norm_training_seq[bcc][TRAIN_BEGINNING];
|
||||||
const auto tseqlen = N_TRAIN_BITS - (2 * TRAIN_BEGINNING);
|
const auto tseqlen = N_TRAIN_BITS - (2 * TRAIN_BEGINNING);
|
||||||
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
|
return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen,
|
||||||
search_center * d_OSR;
|
corr_max);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -286,9 +281,8 @@ int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp)
|
|||||||
|
|
||||||
// strongest_window_nr + chan_imp_resp_center + SYNC_POS *d_OSR - 48 * d_OSR - 2 * d_OSR + 2 ;
|
// strongest_window_nr + chan_imp_resp_center + SYNC_POS *d_OSR - 48 * d_OSR - 2 * d_OSR + 2 ;
|
||||||
float corr_max;
|
float corr_max;
|
||||||
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, &corr_max) -
|
return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen,
|
||||||
search_center * d_OSR;
|
&corr_max);
|
||||||
;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max)
|
int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max)
|
||||||
@@ -297,9 +291,9 @@ int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_r
|
|||||||
const int search_center = SYNC_POS + TRAIN_BEGINNING;
|
const int search_center = SYNC_POS + TRAIN_BEGINNING;
|
||||||
const int search_start_pos = 0;
|
const int search_start_pos = 0;
|
||||||
// FIXME: proper end offset
|
// FIXME: proper end offset
|
||||||
const int search_stop_pos = len - (N_SYNC_BITS * 8);
|
const int search_stop_pos = len - (N_SYNC_BITS*8);
|
||||||
auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
|
auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
|
||||||
|
|
||||||
return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
|
return get_chan_imp_resp(input, chan_imp_resp, search_center, search_start_pos, search_stop_pos, tseq, tseqlen,
|
||||||
search_center * d_OSR;
|
corr_max);
|
||||||
}
|
}
|
||||||
@@ -24,9 +24,6 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
#include "constants.h"
|
#include "constants.h"
|
||||||
|
|
||||||
/* may only be used for for the DEFINITIONS!
|
|
||||||
* see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91664
|
|
||||||
*/
|
|
||||||
#if defined(__has_attribute)
|
#if defined(__has_attribute)
|
||||||
#if __has_attribute(target_clones) && defined(__x86_64) && true
|
#if __has_attribute(target_clones) && defined(__x86_64) && true
|
||||||
#define MULTI_VER_TARGET_ATTR __attribute__((target_clones("avx", "sse4.2", "sse3", "sse2", "sse", "default")))
|
#define MULTI_VER_TARGET_ATTR __attribute__((target_clones("avx", "sse4.2", "sse3", "sse2", "sse", "default")))
|
||||||
@@ -35,26 +32,6 @@
|
|||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* ... but apparently clang disagrees... */
|
|
||||||
#if defined(__clang__)
|
|
||||||
#define MULTI_VER_TARGET_ATTR_CLANGONLY MULTI_VER_TARGET_ATTR
|
|
||||||
#else
|
|
||||||
#define MULTI_VER_TARGET_ATTR_CLANGONLY
|
|
||||||
#endif
|
|
||||||
|
|
||||||
/* ancient gcc < 8 has no attribute, clang always pretends to be gcc 4 */
|
|
||||||
#if !defined(__clang__) && __GNUC__ < 8
|
|
||||||
#define NO_UBSAN __attribute__((no_sanitize_undefined))
|
|
||||||
#else
|
|
||||||
#if defined(__has_attribute)
|
|
||||||
#if __has_attribute(no_sanitize)
|
|
||||||
#define NO_UBSAN __attribute__((no_sanitize("undefined")))
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#define NO_UBSAN
|
|
||||||
#endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SYNC_SEARCH_RANGE 30
|
#define SYNC_SEARCH_RANGE 30
|
||||||
const int d_OSR(4);
|
const int d_OSR(4);
|
||||||
|
|
||||||
@@ -63,11 +40,8 @@ void initvita();
|
|||||||
int process_vita_burst(gr_complex *input, int tsc, unsigned char *output_binary);
|
int process_vita_burst(gr_complex *input, int tsc, unsigned char *output_binary);
|
||||||
int process_vita_sc_burst(gr_complex *input, int tsc, unsigned char *output_binary, int *offset);
|
int process_vita_sc_burst(gr_complex *input, int tsc, unsigned char *output_binary, int *offset);
|
||||||
|
|
||||||
void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary, int ss);
|
MULTI_VER_TARGET_ATTR
|
||||||
void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary, int ss);
|
void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary);
|
||||||
void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary);
|
|
||||||
void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary);
|
|
||||||
|
|
||||||
void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output, gr_complex start_point);
|
void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output, gr_complex start_point);
|
||||||
gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_complex *input);
|
gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_complex *input);
|
||||||
inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems);
|
inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems);
|
||||||
@@ -75,7 +49,6 @@ inline void mafi(const gr_complex *input, int nitems, gr_complex *filter, int fi
|
|||||||
int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp);
|
int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp);
|
||||||
int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int bcc);
|
int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int bcc);
|
||||||
int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max);
|
int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max);
|
||||||
int get_access_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int max_delay);
|
|
||||||
|
|
||||||
enum class btype { NB, SCH };
|
enum class btype { NB, SCH };
|
||||||
struct fdata {
|
struct fdata {
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
@@ -38,18 +39,16 @@ const size_t BLADE_NUM_BUFFERS = 32 * 1;
|
|||||||
const size_t NUM_TRANSFERS = 16 * 2;
|
const size_t NUM_TRANSFERS = 16 * 2;
|
||||||
const int SAMPLE_SCALE_FACTOR = 15; // actually 16 but sigproc complains about clipping..
|
const int SAMPLE_SCALE_FACTOR = 15; // actually 16 but sigproc complains about clipping..
|
||||||
|
|
||||||
// see https://en.cppreference.com/w/cpp/language/parameter_pack "Brace-enclosed initializers" example
|
template <typename Arg, typename... Args> void expand_args(std::ostream &out, Arg &&arg, Args &&...args)
|
||||||
template <typename Arg, typename... Args>
|
|
||||||
void expand_args(std::ostream &out, Arg &&arg, Args &&...args)
|
|
||||||
{
|
{
|
||||||
out << '(' << std::forward<Arg>(arg);
|
out << '(' << std::forward<Arg>(arg);
|
||||||
(void)(int[]){ 0, (void((out << "," << std::forward<Args>(args))), 0)... };
|
(void)(int[]){ 0, (void((out << "," << std::forward<Args>(args))), 0)... };
|
||||||
out << ')' << std::endl;
|
out << ')' << std::endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <class R, class... Args>
|
template <class R, class... Args> using RvalFunc = R (*)(Args...);
|
||||||
using RvalFunc = R (*)(Args...);
|
|
||||||
|
|
||||||
|
// specialisation for funcs which return a value
|
||||||
template <class R, class... Args>
|
template <class R, class... Args>
|
||||||
R exec_and_check(RvalFunc<R, Args...> func, const char *fname, const char *finame, const char *funcname, int line,
|
R exec_and_check(RvalFunc<R, Args...> func, const char *fname, const char *finame, const char *funcname, int line,
|
||||||
Args... args)
|
Args... args)
|
||||||
@@ -69,8 +68,7 @@ R exec_and_check(RvalFunc<R, Args...> func, const char *fname, const char *finam
|
|||||||
#pragma pack(push, 1)
|
#pragma pack(push, 1)
|
||||||
using blade_sample_type = std::complex<int16_t>;
|
using blade_sample_type = std::complex<int16_t>;
|
||||||
enum class blade_speed_buffer_type { HS, SS };
|
enum class blade_speed_buffer_type { HS, SS };
|
||||||
template <blade_speed_buffer_type T>
|
template <blade_speed_buffer_type T> struct blade_usb_message {
|
||||||
struct blade_usb_message {
|
|
||||||
uint32_t reserved;
|
uint32_t reserved;
|
||||||
uint64_t ts;
|
uint64_t ts;
|
||||||
uint32_t meta_flags;
|
uint32_t meta_flags;
|
||||||
@@ -79,8 +77,7 @@ struct blade_usb_message {
|
|||||||
|
|
||||||
static_assert(sizeof(blade_usb_message<blade_speed_buffer_type::SS>) == 2048, "blade buffer mismatch!");
|
static_assert(sizeof(blade_usb_message<blade_speed_buffer_type::SS>) == 2048, "blade buffer mismatch!");
|
||||||
static_assert(sizeof(blade_usb_message<blade_speed_buffer_type::HS>) == 1024, "blade buffer mismatch!");
|
static_assert(sizeof(blade_usb_message<blade_speed_buffer_type::HS>) == 1024, "blade buffer mismatch!");
|
||||||
template <unsigned int SZ, blade_speed_buffer_type T>
|
template <unsigned int SZ, blade_speed_buffer_type T> struct blade_otw_buffer {
|
||||||
struct blade_otw_buffer {
|
|
||||||
static_assert((SZ >= 2 && !(SZ % 2)), "min size is 2x usb buffer!");
|
static_assert((SZ >= 2 && !(SZ % 2)), "min size is 2x usb buffer!");
|
||||||
blade_usb_message<T> m[SZ];
|
blade_usb_message<T> m[SZ];
|
||||||
int actual_samples_per_msg()
|
int actual_samples_per_msg()
|
||||||
@@ -112,7 +109,7 @@ struct blade_otw_buffer {
|
|||||||
int readall(blade_sample_type *outaddr)
|
int readall(blade_sample_type *outaddr)
|
||||||
{
|
{
|
||||||
blade_sample_type *addr = outaddr;
|
blade_sample_type *addr = outaddr;
|
||||||
for (unsigned int i = 0; i < SZ; i++) {
|
for (int i = 0; i < SZ; i++) {
|
||||||
memcpy(addr, &m[i].d[0], actual_samples_per_msg() * sizeof(blade_sample_type));
|
memcpy(addr, &m[i].d[0], actual_samples_per_msg() * sizeof(blade_sample_type));
|
||||||
addr += actual_samples_per_msg();
|
addr += actual_samples_per_msg();
|
||||||
}
|
}
|
||||||
@@ -160,7 +157,7 @@ struct blade_otw_buffer {
|
|||||||
{
|
{
|
||||||
assert(num <= actual_samples_per_buffer());
|
assert(num <= actual_samples_per_buffer());
|
||||||
int len_rem = num;
|
int len_rem = num;
|
||||||
for (unsigned int i = 0; i < SZ; i++) {
|
for (int i = 0; i < SZ; i++) {
|
||||||
m[i] = {};
|
m[i] = {};
|
||||||
m[i].ts = first_ts + i * actual_samples_per_msg();
|
m[i].ts = first_ts + i * actual_samples_per_msg();
|
||||||
if (len_rem) {
|
if (len_rem) {
|
||||||
@@ -176,8 +173,7 @@ struct blade_otw_buffer {
|
|||||||
};
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
template <unsigned int SZ, blade_speed_buffer_type T>
|
template <unsigned int SZ, blade_speed_buffer_type T> struct blade_otw_buffer_helper {
|
||||||
struct blade_otw_buffer_helper {
|
|
||||||
static_assert((SZ >= 1024 && ((SZ & (SZ - 1)) == 0)), "only buffer size multiples of 1024 allowed!");
|
static_assert((SZ >= 1024 && ((SZ & (SZ - 1)) == 0)), "only buffer size multiples of 1024 allowed!");
|
||||||
static blade_otw_buffer<SZ / 512, T> x;
|
static blade_otw_buffer<SZ / 512, T> x;
|
||||||
};
|
};
|
||||||
@@ -186,19 +182,16 @@ using dev_buf_t = typeof(blade_otw_buffer_helper<BLADE_BUFFER_SIZE, blade_speed_
|
|||||||
// using buf_in_use = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
|
// using buf_in_use = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
|
||||||
using bh_fn_t = std::function<int(dev_buf_t *)>;
|
using bh_fn_t = std::function<int(dev_buf_t *)>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> struct blade_hw {
|
||||||
struct blade_hw {
|
|
||||||
struct bladerf *dev;
|
struct bladerf *dev;
|
||||||
struct bladerf_stream *rx_stream;
|
struct bladerf_stream *rx_stream;
|
||||||
struct bladerf_stream *tx_stream;
|
struct bladerf_stream *tx_stream;
|
||||||
// using pkt2buf = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
|
// using pkt2buf = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
|
||||||
using tx_buf_q_type = spsc_cond_timeout<BLADE_NUM_BUFFERS, dev_buf_t *, true, false>;
|
using tx_buf_q_type = spsc_cond<BLADE_NUM_BUFFERS, dev_buf_t *, true, false>;
|
||||||
const unsigned int rxFullScale, txFullScale;
|
const unsigned int rxFullScale, txFullScale;
|
||||||
const int rxtxdelay;
|
const int rxtxdelay;
|
||||||
|
|
||||||
float rxgain, txgain;
|
float rxgain, txgain;
|
||||||
static std::atomic<bool> stop_lower_threads_flag;
|
|
||||||
double rxfreq_cache, txfreq_cache;
|
|
||||||
|
|
||||||
struct ms_trx_config {
|
struct ms_trx_config {
|
||||||
int tx_freq;
|
int tx_freq;
|
||||||
@@ -223,23 +216,21 @@ struct blade_hw {
|
|||||||
{
|
{
|
||||||
close_device();
|
close_device();
|
||||||
}
|
}
|
||||||
blade_hw()
|
blade_hw() : rxFullScale(2047), txFullScale(2047), rxtxdelay(-60)
|
||||||
: rxFullScale(2047), txFullScale(2047), rxtxdelay(-60), rxgain(30), txgain(30), rxfreq_cache(0),
|
|
||||||
txfreq_cache(0)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void close_device()
|
void close_device()
|
||||||
{
|
{
|
||||||
if (dev) {
|
if (dev) {
|
||||||
if (tx_stream) {
|
|
||||||
bladerf_deinit_stream(tx_stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (rx_stream) {
|
if (rx_stream) {
|
||||||
bladerf_deinit_stream(rx_stream);
|
bladerf_deinit_stream(rx_stream);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (tx_stream) {
|
||||||
|
bladerf_deinit_stream(tx_stream);
|
||||||
|
}
|
||||||
|
|
||||||
bladerf_enable_module(dev, BLADERF_MODULE_RX, false);
|
bladerf_enable_module(dev, BLADERF_MODULE_RX, false);
|
||||||
bladerf_enable_module(dev, BLADERF_MODULE_TX, false);
|
bladerf_enable_module(dev, BLADERF_MODULE_TX, false);
|
||||||
|
|
||||||
@@ -293,51 +284,46 @@ struct blade_hw {
|
|||||||
(bladerf_bandwidth *)NULL);
|
(bladerf_bandwidth *)NULL);
|
||||||
|
|
||||||
blade_check(bladerf_set_gain_mode, dev, BLADERF_CHANNEL_RX(0), BLADERF_GAIN_MGC);
|
blade_check(bladerf_set_gain_mode, dev, BLADERF_CHANNEL_RX(0), BLADERF_GAIN_MGC);
|
||||||
setRxGain(rxgain, 0);
|
blade_check(bladerf_set_gain, dev, BLADERF_CHANNEL_RX(0), (bladerf_gain)30);
|
||||||
setTxGain(txgain, 0);
|
blade_check(bladerf_set_gain, dev, BLADERF_CHANNEL_TX(0), (bladerf_gain)30);
|
||||||
|
usleep(1000);
|
||||||
|
blade_check(bladerf_enable_module, dev, BLADERF_MODULE_RX, true);
|
||||||
|
usleep(1000);
|
||||||
|
blade_check(bladerf_enable_module, dev, BLADERF_MODULE_TX, true);
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
|
|
||||||
bladerf_set_stream_timeout(dev, BLADERF_TX, 10);
|
|
||||||
bladerf_set_stream_timeout(dev, BLADERF_RX, 10);
|
|
||||||
|
|
||||||
blade_check(bladerf_init_stream, &rx_stream, dev, getrxcb(rxh), &buf_mgmt.rx_samples, BLADE_NUM_BUFFERS,
|
blade_check(bladerf_init_stream, &rx_stream, dev, getrxcb(rxh), &buf_mgmt.rx_samples, BLADE_NUM_BUFFERS,
|
||||||
BLADERF_FORMAT_SC16_Q11_META, BLADE_BUFFER_SIZE, NUM_TRANSFERS, (void *)this);
|
BLADERF_FORMAT_SC16_Q11_META, BLADE_BUFFER_SIZE, NUM_TRANSFERS, (void *)this);
|
||||||
|
|
||||||
blade_check(bladerf_init_stream, &tx_stream, dev, gettxcb(txh), &buf_mgmt.tx_samples, BLADE_NUM_BUFFERS,
|
blade_check(bladerf_init_stream, &tx_stream, dev, gettxcb(txh), &buf_mgmt.tx_samples, BLADE_NUM_BUFFERS,
|
||||||
BLADERF_FORMAT_SC16_Q11_META, BLADE_BUFFER_SIZE, NUM_TRANSFERS, (void *)this);
|
BLADERF_FORMAT_SC16_Q11_META, BLADE_BUFFER_SIZE, NUM_TRANSFERS, (void *)this);
|
||||||
|
|
||||||
for (unsigned int i = 0; i < BLADE_NUM_BUFFERS; i++) {
|
for (int i = 0; i < BLADE_NUM_BUFFERS; i++) {
|
||||||
auto cur_buffer = reinterpret_cast<tx_buf_q_type::elem_t *>(buf_mgmt.tx_samples);
|
auto cur_buffer = reinterpret_cast<tx_buf_q_type::elem_t *>(buf_mgmt.tx_samples);
|
||||||
buf_mgmt.bufptrqueue.spsc_push(&cur_buffer[i]);
|
buf_mgmt.bufptrqueue.spsc_push(&cur_buffer[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
setRxGain(20);
|
||||||
}
|
setTxGain(30);
|
||||||
|
|
||||||
void actually_enable_streams()
|
|
||||||
{
|
|
||||||
blade_check(bladerf_enable_module, dev, BLADERF_MODULE_RX, true);
|
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
blade_check(bladerf_enable_module, dev, BLADERF_MODULE_TX, true);
|
|
||||||
|
// bladerf_set_stream_timeout(dev, BLADERF_TX, 4);
|
||||||
|
// bladerf_set_stream_timeout(dev, BLADERF_RX, 4);
|
||||||
|
|
||||||
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tuneTx(double freq, size_t chan = 0)
|
bool tuneTx(double freq, size_t chan = 0)
|
||||||
{
|
{
|
||||||
if (txfreq_cache == freq)
|
|
||||||
return true;
|
|
||||||
msleep(15);
|
msleep(15);
|
||||||
blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_TX(0), (bladerf_frequency)freq);
|
blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_TX(0), (bladerf_frequency)freq);
|
||||||
txfreq_cache = freq;
|
|
||||||
msleep(15);
|
msleep(15);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
bool tuneRx(double freq, size_t chan = 0)
|
bool tuneRx(double freq, size_t chan = 0)
|
||||||
{
|
{
|
||||||
if (rxfreq_cache == freq)
|
|
||||||
return true;
|
|
||||||
msleep(15);
|
msleep(15);
|
||||||
blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_RX(0), (bladerf_frequency)freq);
|
blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_RX(0), (bladerf_frequency)freq);
|
||||||
rxfreq_cache = freq;
|
|
||||||
msleep(15);
|
msleep(15);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@@ -393,9 +379,6 @@ struct blade_hw {
|
|||||||
static int to_skip = 0;
|
static int to_skip = 0;
|
||||||
dev_buf_t *rcd = (dev_buf_t *)samples;
|
dev_buf_t *rcd = (dev_buf_t *)samples;
|
||||||
|
|
||||||
if (stop_lower_threads_flag)
|
|
||||||
return BLADERF_STREAM_SHUTDOWN;
|
|
||||||
|
|
||||||
if (to_skip < 120) // prevents weird overflows on startup
|
if (to_skip < 120) // prevents weird overflows on startup
|
||||||
to_skip++;
|
to_skip++;
|
||||||
else {
|
else {
|
||||||
@@ -418,9 +401,6 @@ struct blade_hw {
|
|||||||
if (samples) // put buffer address back into queue, ready to be reused
|
if (samples) // put buffer address back into queue, ready to be reused
|
||||||
trx->buf_mgmt.bufptrqueue.spsc_push(&ptr);
|
trx->buf_mgmt.bufptrqueue.spsc_push(&ptr);
|
||||||
|
|
||||||
if (stop_lower_threads_flag)
|
|
||||||
return BLADERF_STREAM_SHUTDOWN;
|
|
||||||
|
|
||||||
return BLADERF_STREAM_NO_DATA;
|
return BLADERF_STREAM_NO_DATA;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -428,41 +408,33 @@ struct blade_hw {
|
|||||||
auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
|
auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
|
||||||
{
|
{
|
||||||
auto fn = [this] {
|
auto fn = [this] {
|
||||||
int status = 0;
|
int status;
|
||||||
if (!stop_lower_threads_flag)
|
|
||||||
status = bladerf_stream(rx_stream, BLADERF_RX_X1);
|
status = bladerf_stream(rx_stream, BLADERF_RX_X1);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
|
std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
|
||||||
|
|
||||||
return 0;
|
return NULL;
|
||||||
};
|
};
|
||||||
return fn;
|
return fn;
|
||||||
}
|
}
|
||||||
auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
|
auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
|
||||||
{
|
{
|
||||||
auto fn = [this] {
|
auto fn = [this] {
|
||||||
int status = 0;
|
int status;
|
||||||
if (!stop_lower_threads_flag)
|
|
||||||
status = bladerf_stream(tx_stream, BLADERF_TX_X1);
|
status = bladerf_stream(tx_stream, BLADERF_TX_X1);
|
||||||
if (status < 0)
|
if (status < 0)
|
||||||
std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
|
std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
|
||||||
|
|
||||||
return 0;
|
return NULL;
|
||||||
};
|
};
|
||||||
return fn;
|
return fn;
|
||||||
}
|
}
|
||||||
|
|
||||||
void submit_burst_ts(blade_sample_type *buffer, int len, uint64_t ts)
|
void submit_burst_ts(blade_sample_type *buffer, int len, uint64_t ts)
|
||||||
{
|
{
|
||||||
|
//get empty bufer from list
|
||||||
tx_buf_q_type::elem_t rcd;
|
tx_buf_q_type::elem_t rcd;
|
||||||
|
|
||||||
// exit by submitting a dummy buffer to assure the libbladerf stream mutex is happy (thread!)
|
|
||||||
if (!buffer) {
|
|
||||||
bladerf_submit_stream_buffer(tx_stream, (void *)BLADERF_STREAM_SHUTDOWN, 1000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
//get empty bufer from list
|
|
||||||
while (!buf_mgmt.bufptrqueue.spsc_pop(&rcd))
|
while (!buf_mgmt.bufptrqueue.spsc_pop(&rcd))
|
||||||
buf_mgmt.bufptrqueue.spsc_prep_pop();
|
buf_mgmt.bufptrqueue.spsc_prep_pop();
|
||||||
assert(rcd != nullptr);
|
assert(rcd != nullptr);
|
||||||
|
|||||||
197
Transceiver52M/ms/ipc_specific.h
Normal file
197
Transceiver52M/ms/ipc_specific.h
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
/*
|
||||||
|
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* Author: Eric Wild <ewild@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <cassert>
|
||||||
|
#include <complex>
|
||||||
|
#include <cstring>
|
||||||
|
#include <functional>
|
||||||
|
#include <iostream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
#include <Timeval.h>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
// #define MTX_LOG_ENABLED
|
||||||
|
#include <ipcif.h>
|
||||||
|
|
||||||
|
// typedef unsigned long long TIMESTAMP;
|
||||||
|
using blade_sample_type = std::complex<int16_t>;
|
||||||
|
const int SAMPLE_SCALE_FACTOR = 1;
|
||||||
|
|
||||||
|
struct uhd_buf_wrap {
|
||||||
|
uint64_t ts;
|
||||||
|
uint32_t num_samps;
|
||||||
|
blade_sample_type *buf;
|
||||||
|
auto actual_samples_per_buffer()
|
||||||
|
{
|
||||||
|
return num_samps;
|
||||||
|
}
|
||||||
|
long get_first_ts()
|
||||||
|
{
|
||||||
|
return ts; //md->time_spec.to_ticks(rxticks);
|
||||||
|
}
|
||||||
|
int readall(blade_sample_type *outaddr)
|
||||||
|
{
|
||||||
|
memcpy(outaddr, buf, num_samps * sizeof(blade_sample_type));
|
||||||
|
return num_samps;
|
||||||
|
}
|
||||||
|
int read_n(blade_sample_type *outaddr, int start, int num)
|
||||||
|
{
|
||||||
|
// assert(start >= 0);
|
||||||
|
auto to_read = std::min((int)num_samps - start, num);
|
||||||
|
// assert(to_read >= 0);
|
||||||
|
memcpy(outaddr, buf + start, to_read * sizeof(blade_sample_type));
|
||||||
|
return to_read;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
using dev_buf_t = uhd_buf_wrap;
|
||||||
|
using bh_fn_t = std::function<int(dev_buf_t *)>;
|
||||||
|
|
||||||
|
template <typename T> struct ipc_hw {
|
||||||
|
// uhd::usrp::multi_usrp::sptr dev;
|
||||||
|
// uhd::rx_streamer::sptr rx_stream;
|
||||||
|
// uhd::tx_streamer::sptr tx_stream;
|
||||||
|
blade_sample_type *one_pkt_buf;
|
||||||
|
std::vector<blade_sample_type *> pkt_ptrs;
|
||||||
|
size_t rx_spp;
|
||||||
|
double rxticks;
|
||||||
|
const unsigned int rxFullScale, txFullScale;
|
||||||
|
const int rxtxdelay;
|
||||||
|
float rxgain, txgain;
|
||||||
|
trxmsif m;
|
||||||
|
|
||||||
|
virtual ~ipc_hw()
|
||||||
|
{
|
||||||
|
delete[] one_pkt_buf;
|
||||||
|
}
|
||||||
|
ipc_hw() : rxFullScale(32767), txFullScale(32767), rxtxdelay(0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
bool tuneTx(double freq, size_t chan = 0)
|
||||||
|
{
|
||||||
|
msleep(25);
|
||||||
|
// dev->set_tx_freq(freq, chan);
|
||||||
|
msleep(25);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
bool tuneRx(double freq, size_t chan = 0)
|
||||||
|
{
|
||||||
|
msleep(25);
|
||||||
|
// dev->set_rx_freq(freq, chan);
|
||||||
|
msleep(25);
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
bool tuneRxOffset(double offset, size_t chan = 0)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
|
||||||
|
double setRxGain(double dB, size_t chan = 0)
|
||||||
|
{
|
||||||
|
rxgain = dB;
|
||||||
|
msleep(25);
|
||||||
|
// dev->set_rx_gain(dB, chan);
|
||||||
|
msleep(25);
|
||||||
|
return dB;
|
||||||
|
};
|
||||||
|
double setTxGain(double dB, size_t chan = 0)
|
||||||
|
{
|
||||||
|
txgain = dB;
|
||||||
|
msleep(25);
|
||||||
|
// dev->set_tx_gain(dB, chan);
|
||||||
|
msleep(25);
|
||||||
|
return dB;
|
||||||
|
};
|
||||||
|
int setPowerAttenuation(int atten, size_t chan = 0)
|
||||||
|
{
|
||||||
|
return atten;
|
||||||
|
};
|
||||||
|
|
||||||
|
int init_device(bh_fn_t rxh, bh_fn_t txh)
|
||||||
|
{
|
||||||
|
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];
|
||||||
|
|
||||||
|
uint64_t t;
|
||||||
|
|
||||||
|
int len = 508 * 2;
|
||||||
|
m.read_dl(508 * 2, &t, pbuf);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
|
||||||
|
{
|
||||||
|
auto fn = [this, burst_handler] {
|
||||||
|
pthread_setname_np(pthread_self(), "rxrun");
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
rx_cb(burst_handler);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
|
||||||
|
{
|
||||||
|
auto fn = [] {
|
||||||
|
// wait_for_shm_open();
|
||||||
|
// dummy
|
||||||
|
};
|
||||||
|
return fn;
|
||||||
|
}
|
||||||
|
void submit_burst_ts(blade_sample_type *buffer, int len, uint64_t ts)
|
||||||
|
{
|
||||||
|
// FIXME: missing
|
||||||
|
}
|
||||||
|
|
||||||
|
void set_name_aff_sched(const char *name, int cpunum, int schedtype, int prio)
|
||||||
|
{
|
||||||
|
pthread_setname_np(pthread_self(), name);
|
||||||
|
|
||||||
|
}
|
||||||
|
void signal_start()
|
||||||
|
{
|
||||||
|
m.signal_read_start();
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -27,60 +27,28 @@
|
|||||||
#include <sys/eventfd.h>
|
#include <sys/eventfd.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
|
#include <stdatomic.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
/*
|
||||||
|
classic lamport circular lockfree spsc queue:
|
||||||
|
every "side" only writes its own ptr, but may read the other sides ptr
|
||||||
|
|
||||||
|
notify reader using eventfd as soon as element is added, reader then reads until
|
||||||
|
read fails
|
||||||
|
-> reader pops in a loop until FALSE and might get spurious events because it
|
||||||
|
read before it was notified, which is fine
|
||||||
|
-> writing pushes *the same data* in a loop until TRUE, blocks
|
||||||
|
|
||||||
|
shutting this down requires
|
||||||
|
1) to stop reading and pushing
|
||||||
|
2) ONE side to take care of the eventfds
|
||||||
|
*/
|
||||||
|
|
||||||
namespace spsc_detail
|
namespace spsc_detail
|
||||||
{
|
{
|
||||||
template <bool block_read, bool block_write>
|
template <bool block_read, bool block_write> class spsc_cond_detail {
|
||||||
class spsc_cond_timeout_detail {
|
|
||||||
std::condition_variable cond_r, cond_w;
|
|
||||||
std::mutex lr, lw;
|
|
||||||
std::atomic_int r_flag, w_flag;
|
|
||||||
const int timeout_ms = 200;
|
|
||||||
|
|
||||||
public:
|
|
||||||
explicit spsc_cond_timeout_detail() : r_flag(0), w_flag(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
~spsc_cond_timeout_detail()
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
ssize_t spsc_check_r()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lk(lr);
|
|
||||||
if (cond_r.wait_for(lk, std::chrono::milliseconds(timeout_ms), [&] { return r_flag != 0; })) {
|
|
||||||
r_flag--;
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ssize_t spsc_check_w()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lk(lw);
|
|
||||||
if (cond_w.wait_for(lk, std::chrono::milliseconds(timeout_ms), [&] { return w_flag != 0; })) {
|
|
||||||
w_flag--;
|
|
||||||
return 1;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
void spsc_notify_r()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lk(lr);
|
|
||||||
r_flag++;
|
|
||||||
cond_r.notify_one();
|
|
||||||
}
|
|
||||||
void spsc_notify_w()
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> lk(lw);
|
|
||||||
w_flag++;
|
|
||||||
cond_w.notify_one();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
template <bool block_read, bool block_write>
|
|
||||||
class spsc_cond_detail {
|
|
||||||
std::condition_variable cond_r, cond_w;
|
std::condition_variable cond_r, cond_w;
|
||||||
std::mutex lr, lw;
|
std::mutex lr, lw;
|
||||||
std::atomic_int r_flag, w_flag;
|
std::atomic_int r_flag, w_flag;
|
||||||
@@ -125,8 +93,7 @@ class spsc_cond_detail {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// originally designed for select loop integration
|
// originally designed for select loop integration
|
||||||
template <bool block_read, bool block_write>
|
template <bool block_read, bool block_write> class spsc_efd_detail {
|
||||||
class spsc_efd_detail {
|
|
||||||
int efd_r, efd_w; /* eventfds used to block/notify readers/writers */
|
int efd_r, efd_w; /* eventfds used to block/notify readers/writers */
|
||||||
|
|
||||||
public:
|
public:
|
||||||
@@ -244,6 +211,3 @@ template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
|
|||||||
class spsc_evfd : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_efd_detail> {};
|
class spsc_evfd : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_efd_detail> {};
|
||||||
template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
|
template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
|
||||||
class spsc_cond : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_detail> {};
|
class spsc_cond : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_detail> {};
|
||||||
template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
|
|
||||||
class spsc_cond_timeout
|
|
||||||
: public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_timeout_detail> {};
|
|
||||||
@@ -24,10 +24,13 @@ extern "C" {
|
|||||||
#include <osmocom/bb/trxcon/trxcon_fsm.h>
|
#include <osmocom/bb/trxcon/trxcon_fsm.h>
|
||||||
#include <osmocom/bb/trxcon/l1ctl_server.h>
|
#include <osmocom/bb/trxcon/l1ctl_server.h>
|
||||||
}
|
}
|
||||||
#include "ms_trxcon_if.h"
|
|
||||||
|
|
||||||
static struct l1ctl_server_cfg server_cfg;
|
static struct l1ctl_server_cfg server_cfg;
|
||||||
static struct l1ctl_server *server = NULL;
|
static struct l1ctl_server *server = NULL;
|
||||||
|
namespace trxcon
|
||||||
|
{
|
||||||
|
extern struct trxcon_inst *g_trxcon;
|
||||||
|
}
|
||||||
|
|
||||||
static int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg)
|
static int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg)
|
||||||
{
|
{
|
||||||
@@ -38,9 +41,9 @@ static int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg)
|
|||||||
|
|
||||||
static void l1ctl_conn_accept_cb(struct l1ctl_client *l1c)
|
static void l1ctl_conn_accept_cb(struct l1ctl_client *l1c)
|
||||||
{
|
{
|
||||||
l1c->log_prefix = talloc_strdup(l1c, g_trxcon->log_prefix);
|
l1c->log_prefix = talloc_strdup(l1c, trxcon::g_trxcon->log_prefix);
|
||||||
l1c->priv = g_trxcon;
|
l1c->priv = trxcon::g_trxcon;
|
||||||
g_trxcon->l2if = l1c;
|
trxcon::g_trxcon->l2if = l1c;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void l1ctl_conn_close_cb(struct l1ctl_client *l1c)
|
static void l1ctl_conn_close_cb(struct l1ctl_client *l1c)
|
||||||
@@ -53,19 +56,22 @@ static void l1ctl_conn_close_cb(struct l1ctl_client *l1c)
|
|||||||
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_L2IF_FAILURE, NULL);
|
osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_L2IF_FAILURE, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool trxc_l1ctl_init(void *tallctx)
|
namespace trxcon
|
||||||
|
{
|
||||||
|
void trxc_l1ctl_init(void *tallctx)
|
||||||
{
|
{
|
||||||
/* Start the L1CTL server */
|
/* Start the L1CTL server */
|
||||||
server_cfg = (struct l1ctl_server_cfg){
|
server_cfg = (struct l1ctl_server_cfg){
|
||||||
/* TODO: make path configurable */
|
.sock_path = "/tmp/osmocom_l2",
|
||||||
.sock_path = "/tmp/osmocom_l2", .num_clients_max = 1,
|
.num_clients_max = 1,
|
||||||
.conn_read_cb = &l1ctl_rx_cb, .conn_accept_cb = &l1ctl_conn_accept_cb,
|
.conn_read_cb = &l1ctl_rx_cb,
|
||||||
|
.conn_accept_cb = &l1ctl_conn_accept_cb,
|
||||||
.conn_close_cb = &l1ctl_conn_close_cb,
|
.conn_close_cb = &l1ctl_conn_close_cb,
|
||||||
};
|
};
|
||||||
|
|
||||||
server = l1ctl_server_alloc(tallctx, &server_cfg);
|
server = l1ctl_server_alloc(tallctx, &server_cfg);
|
||||||
if (server == NULL) {
|
if (server == NULL) {
|
||||||
return false;
|
return;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
} // namespace trxcon
|
||||||
@@ -1,98 +1,103 @@
|
|||||||
/*
|
/*
|
||||||
* OsmocomBB <-> SDR connection bridge
|
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
*
|
|
||||||
* (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
|
|
||||||
*
|
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*
|
*
|
||||||
|
* Author: Eric Wild <ewild@sysmocom.de>
|
||||||
|
*
|
||||||
* This program is free software; you can redistribute it and/or modify
|
* This program is free software; you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
* the Free Software Foundation; either version 2 of the License, or
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
* (at your option) any later version.
|
* (at your option) any later version.
|
||||||
*
|
*
|
||||||
* This program is distributed in the hope that it will be useful,
|
* This program is distributed in the hope that it will be useful,
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
extern "C" {
|
||||||
#include <osmocom/core/application.h>
|
#include <osmocom/core/application.h>
|
||||||
#include <osmocom/core/logging.h>
|
|
||||||
#include <osmocom/core/utils.h>
|
|
||||||
|
|
||||||
#include <osmocom/bb/trxcon/trxcon.h>
|
|
||||||
#include <osmocom/bb/trxcon/logging.h>
|
#include <osmocom/bb/trxcon/logging.h>
|
||||||
|
#include <osmocom/bb/trxcon/trxcon.h>
|
||||||
|
#include <osmocom/bb/l1sched/l1sched.h>
|
||||||
|
|
||||||
static struct log_info_cat trxcon_log_info_cat[] = {
|
}
|
||||||
[DAPP] = {
|
|
||||||
.name = "DAPP",
|
|
||||||
.description = "Application",
|
|
||||||
.color = "\033[1;35m",
|
|
||||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
|
||||||
},
|
|
||||||
[DL1C] = {
|
|
||||||
.name = "DL1C",
|
|
||||||
.description = "Layer 1 control interface",
|
|
||||||
.color = "\033[1;31m",
|
|
||||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
|
||||||
},
|
|
||||||
[DL1D] = {
|
|
||||||
.name = "DL1D",
|
|
||||||
.description = "Layer 1 data",
|
|
||||||
.color = "\033[1;31m",
|
|
||||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
|
||||||
},
|
|
||||||
[DTRXC] = {
|
|
||||||
.name = "DTRXC",
|
|
||||||
.description = "Transceiver control interface",
|
|
||||||
.color = "\033[1;33m",
|
|
||||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
|
||||||
},
|
|
||||||
[DTRXD] = {
|
|
||||||
.name = "DTRXD",
|
|
||||||
.description = "Transceiver data interface",
|
|
||||||
.color = "\033[1;33m",
|
|
||||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
|
||||||
},
|
|
||||||
[DSCH] = {
|
|
||||||
.name = "DSCH",
|
|
||||||
.description = "Scheduler management",
|
|
||||||
.color = "\033[1;36m",
|
|
||||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
|
||||||
},
|
|
||||||
[DSCHD] = {
|
|
||||||
.name = "DSCHD",
|
|
||||||
.description = "Scheduler data",
|
|
||||||
.color = "\033[1;36m",
|
|
||||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
|
||||||
},
|
|
||||||
[DGPRS] = {
|
|
||||||
.name = "DGPRS",
|
|
||||||
.description = "L1 GPRS (MAC layer)",
|
|
||||||
.color = "\033[1;36m",
|
|
||||||
.enabled = 1, .loglevel = LOGL_NOTICE,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
static const struct log_info trxcon_log_info = {
|
|
||||||
.cat = trxcon_log_info_cat,
|
|
||||||
.num_cat = ARRAY_SIZE(trxcon_log_info_cat),
|
|
||||||
};
|
|
||||||
|
|
||||||
static const int trxcon_log_cfg[] = {
|
static const int trxcon_log_cfg[] = {
|
||||||
[TRXCON_LOGC_FSM] = DAPP,
|
[TRXCON_LOGC_FSM] = DAPP,
|
||||||
[TRXCON_LOGC_L1C] = DL1C,
|
[TRXCON_LOGC_L1C] = DL1C,
|
||||||
[TRXCON_LOGC_L1D] = DL1D,
|
[TRXCON_LOGC_L1D] = DL1D,
|
||||||
[TRXCON_LOGC_SCHC] = DSCH,
|
[TRXCON_LOGC_SCHC] = DSCH,
|
||||||
[TRXCON_LOGC_SCHD] = DSCHD,
|
[TRXCON_LOGC_SCHD] = DSCHD,
|
||||||
[TRXCON_LOGC_GPRS] = DGPRS,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static struct log_info_cat trxcon_log_info_cat[] = {
|
||||||
|
[DAPP] = {
|
||||||
|
.name = "DAPP",
|
||||||
|
.color = "\033[1;35m",
|
||||||
|
.description = "Application",
|
||||||
|
.loglevel = LOGL_NOTICE, .enabled = 1,
|
||||||
|
},
|
||||||
|
[DL1C] = {
|
||||||
|
.name = "DL1C",
|
||||||
|
.color = "\033[1;31m",
|
||||||
|
.description = "Layer 1 control interface",
|
||||||
|
.loglevel = LOGL_NOTICE, .enabled = 1,
|
||||||
|
},
|
||||||
|
[DL1D] = {
|
||||||
|
.name = "DL1D",
|
||||||
|
.color = "\033[1;31m",
|
||||||
|
.description = "Layer 1 data",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DTRXC] = {
|
||||||
|
.name = "DTRXC",
|
||||||
|
.color = "\033[1;33m",
|
||||||
|
.description = "Transceiver control interface",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DTRXD] = {
|
||||||
|
.name = "DTRXD",
|
||||||
|
.color = "\033[1;33m",
|
||||||
|
.description = "Transceiver data interface",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 1,
|
||||||
|
},
|
||||||
|
[DSCH] = {
|
||||||
|
.name = "DSCH",
|
||||||
|
.color = "\033[1;36m",
|
||||||
|
.description = "Scheduler management",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 0,
|
||||||
|
},
|
||||||
|
[DSCHD] = {
|
||||||
|
.name = "DSCHD",
|
||||||
|
.color = "\033[1;36m",
|
||||||
|
.description = "Scheduler data",
|
||||||
|
.loglevel = LOGL_NOTICE,
|
||||||
|
.enabled = 0,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
static struct log_info trxcon_log_info = {
|
||||||
|
.cat = trxcon_log_info_cat,
|
||||||
|
.num_cat = ARRAY_SIZE(trxcon_log_info_cat),
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace trxcon
|
||||||
|
{
|
||||||
|
|
||||||
void trxc_log_init(void *tallctx)
|
void trxc_log_init(void *tallctx)
|
||||||
{
|
{
|
||||||
osmo_init_logging2(tallctx, &trxcon_log_info);
|
osmo_init_logging2(tallctx, &trxcon_log_info);
|
||||||
log_target_file_switch_to_wqueue(osmo_stderr_target);
|
// log_parse_category_mask(osmo_stderr_target, "");
|
||||||
|
|
||||||
trxcon_set_log_cfg(&trxcon_log_cfg[0], ARRAY_SIZE(trxcon_log_cfg));
|
trxcon_set_log_cfg(&trxcon_log_cfg[0], ARRAY_SIZE(trxcon_log_cfg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
} // namespace trxcon
|
||||||
@@ -29,10 +29,16 @@
|
|||||||
#include <thread>
|
#include <thread>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
|
|
||||||
|
#include "sigProcLib.h"
|
||||||
|
|
||||||
#include "ms.h"
|
#include "ms.h"
|
||||||
|
#include "ms_rx_burst.h"
|
||||||
|
#include "grgsm_vitac/grgsm_vitac.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "sch.h"
|
#include "sch.h"
|
||||||
|
#include "convolve.h"
|
||||||
|
#include "convert.h"
|
||||||
}
|
}
|
||||||
|
|
||||||
dummylog ms_trx::dummy_log;
|
dummylog ms_trx::dummy_log;
|
||||||
@@ -43,13 +49,196 @@ const int offset_start = -15;
|
|||||||
static int offset_ctr = 0;
|
static int offset_ctr = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <>
|
void tx_test(ms_trx *t, ts_hitter_q_t *q, unsigned int *tsc)
|
||||||
std::atomic<bool> ms_trx::base::stop_lower_threads_flag(false);
|
{
|
||||||
|
sched_param sch_params;
|
||||||
|
sch_params.sched_priority = sched_get_priority_max(SCHED_FIFO);
|
||||||
|
pthread_setschedparam(pthread_self(), SCHED_FIFO, &sch_params);
|
||||||
|
|
||||||
|
auto burst = genRandAccessBurst(0, 4, 0);
|
||||||
|
scaleVector(*burst, t->txFullScale * 0.7);
|
||||||
|
|
||||||
|
// float -> int16
|
||||||
|
blade_sample_type burst_buf[burst->size()];
|
||||||
|
convert_and_scale<int16_t, float>(burst_buf, burst->begin(), burst->size() * 2, 1);
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
GSM::Time target;
|
||||||
|
while (!q->spsc_pop(&target)) {
|
||||||
|
q->spsc_prep_pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << std::endl << "\x1B[32m hitting " << target.FN() << "\033[0m" << std::endl;
|
||||||
|
|
||||||
|
int timing_advance = 0;
|
||||||
|
int64_t now_ts;
|
||||||
|
GSM::Time now_time;
|
||||||
|
target.incTN(3); // ul dl offset
|
||||||
|
int target_fn = target.FN();
|
||||||
|
int target_tn = target.TN();
|
||||||
|
t->timekeeper.get_both(&now_time, &now_ts);
|
||||||
|
|
||||||
|
auto diff_fn = GSM::FNDelta(target_fn, now_time.FN());
|
||||||
|
int diff_tn = (target_tn - (int)now_time.TN()) % 8;
|
||||||
|
auto tosend = GSM::Time(diff_fn, 0);
|
||||||
|
|
||||||
|
if (diff_tn > 0)
|
||||||
|
tosend.incTN(diff_tn);
|
||||||
|
else if (diff_tn < 0)
|
||||||
|
tosend.decTN(-diff_tn);
|
||||||
|
|
||||||
|
// in thory fn equal and tn+3 equal is also a problem...
|
||||||
|
if (diff_fn < 0 || (diff_fn == 0 && (now_time.TN() - target_tn < 1))) {
|
||||||
|
std::cerr << "## TX too late?! fn DIFF:" << diff_fn << " tn LOCAL: " << now_time.TN()
|
||||||
|
<< " tn OTHER: " << target_tn << std::endl;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto check = now_time + tosend;
|
||||||
|
int64_t send_ts =
|
||||||
|
now_ts + tosend.FN() * 8 * ONE_TS_BURST_LEN + tosend.TN() * ONE_TS_BURST_LEN - timing_advance;
|
||||||
|
|
||||||
|
#ifdef DBGXX
|
||||||
|
std::cerr << "## fn DIFF: " << diff_fn << " ## tn DIFF: " << diff_tn << " tn LOCAL: " << now_time.TN()
|
||||||
|
<< " tn OTHER: " << target_tn << " tndiff" << diff_tn << " tosend:" << tosend.FN() << ":"
|
||||||
|
<< tosend.TN() << " calc: " << check.FN() << ":" << check.TN() << " target: " << target.FN()
|
||||||
|
<< ":" << target.TN() << " ts now: " << now_ts << " target ts:" << send_ts << std::endl;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
unsigned int pad = 4 * 25;
|
||||||
|
blade_sample_type buf2[burst->size() + pad];
|
||||||
|
memset(buf2, 0, pad * sizeof(blade_sample_type));
|
||||||
|
memcpy(&buf2[pad], burst_buf, burst->size() * sizeof(blade_sample_type));
|
||||||
|
|
||||||
|
assert(target.FN() == check.FN());
|
||||||
|
assert(target.TN() == check.TN());
|
||||||
|
assert(target.FN() % 51 == 21);
|
||||||
|
#ifdef DBGXX
|
||||||
|
auto this_offset = offset_start + (offset_ctr++ % offsetrange);
|
||||||
|
std::cerr << "-- O " << this_offset << std::endl;
|
||||||
|
send_ts = now_ts - timing_advance +
|
||||||
|
((target.FN() * 8 + (int)target.TN()) - (now_time.FN() * 8 + (int)now_time.TN())) *
|
||||||
|
ONE_TS_BURST_LEN;
|
||||||
|
#endif
|
||||||
|
t->submit_burst_ts(buf2, burst->size() + pad, send_ts - pad);
|
||||||
|
#ifdef DBGXX
|
||||||
|
signalVector test(burst->size() + pad);
|
||||||
|
convert_and_scale<float, int16_t>(test.begin(), buf2, burst->size() * 2 + pad, 1.f / float(scale));
|
||||||
|
estim_burst_params ebp;
|
||||||
|
auto det = detectAnyBurst(test, 0, 4, 4, CorrType::RACH, 40, &ebp);
|
||||||
|
if (det > 0)
|
||||||
|
std::cerr << "## Y " << ebp.toa << std::endl;
|
||||||
|
else
|
||||||
|
std::cerr << "## NOOOOOOOOO " << ebp.toa << std::endl;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#ifdef SYNCTHINGONLY
|
||||||
|
template <typename A> auto parsec(std::vector<std::string> &v, A &itr, std::string arg, bool *rv)
|
||||||
|
{
|
||||||
|
if (*itr == arg) {
|
||||||
|
*rv = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename A, typename B, typename C>
|
||||||
|
bool parsec(std::vector<std::string> &v, A &itr, std::string arg, B f, C *rv)
|
||||||
|
{
|
||||||
|
if (*itr == arg) {
|
||||||
|
itr++;
|
||||||
|
if (itr != v.end()) {
|
||||||
|
*rv = f(itr->c_str());
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
template <typename A> bool parsec(std::vector<std::string> &v, A &itr, std::string arg, int scale, int *rv)
|
||||||
|
{
|
||||||
|
return parsec(
|
||||||
|
v, itr, arg, [scale](const char *v) -> auto{ return atoi(v) * scale; }, rv);
|
||||||
|
}
|
||||||
|
template <typename A> bool parsec(std::vector<std::string> &v, A &itr, std::string arg, int scale, unsigned int *rv)
|
||||||
|
{
|
||||||
|
return parsec(
|
||||||
|
v, itr, arg, [scale](const char *v) -> auto{ return atoi(v) * scale; }, rv);
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char *argv[])
|
||||||
|
{
|
||||||
|
cpu_set_t cpuset;
|
||||||
|
|
||||||
|
CPU_ZERO(&cpuset);
|
||||||
|
CPU_SET(2, &cpuset);
|
||||||
|
|
||||||
|
auto rv = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
|
||||||
|
if (rv < 0) {
|
||||||
|
std::cerr << "affinity: errreur! " << std::strerror(errno);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int default_tx_freq(881000 * 1000), default_rx_freq(926000 * 1000);
|
||||||
|
unsigned int grx = 20, gtx = 20;
|
||||||
|
bool tx_flag = false;
|
||||||
|
pthread_setname_np(pthread_self(), "main");
|
||||||
|
convolve_init();
|
||||||
|
convert_init();
|
||||||
|
sigProcLibSetup();
|
||||||
|
initvita();
|
||||||
|
|
||||||
|
int status = 0;
|
||||||
|
auto trx = new ms_trx();
|
||||||
|
trx->do_auto_gain = true;
|
||||||
|
|
||||||
|
std::vector<std::string> args(argv + 1, argv + argc);
|
||||||
|
for (auto i = args.begin(); i != args.end(); ++i) {
|
||||||
|
parsec(args, i, "-r", 1000, &default_rx_freq);
|
||||||
|
parsec(args, i, "-t", 1000, &default_tx_freq);
|
||||||
|
parsec(args, i, "-gr", 1, &grx);
|
||||||
|
parsec(args, i, "-gt", 1, >x);
|
||||||
|
parsec(args, i, "-tx", &tx_flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cerr << "usage: " << argv[0] << " <rxfreq in khz, i.e. 926000> [txfreq in khz, i.e. 881000] [TX]"
|
||||||
|
<< std::endl
|
||||||
|
<< "rx" << (argc == 1 ? " (default) " : " ") << default_rx_freq << "hz, tx " << default_tx_freq
|
||||||
|
<< "hz" << std::endl
|
||||||
|
<< "gain rx " << grx << " gain tx " << gtx << std::endl
|
||||||
|
<< (tx_flag ? "##!!## RACH TX ACTIVE ##!!##" : "-- no rach tx --") << std::endl;
|
||||||
|
|
||||||
|
status = trx->init_dev_and_streams();
|
||||||
|
if (status < 0)
|
||||||
|
return status;
|
||||||
|
trx->tuneRx(default_rx_freq);
|
||||||
|
trx->tuneTx(default_tx_freq);
|
||||||
|
trx->setRxGain(grx);
|
||||||
|
trx->setTxGain(gtx);
|
||||||
|
|
||||||
|
if (status == 0) {
|
||||||
|
// FIXME: hacks! needs exit flag for detached threads!
|
||||||
|
|
||||||
|
std::thread(rcv_bursts_test, &trx->rxqueue, &trx->mTSC, trx->rxFullScale).detach();
|
||||||
|
if (tx_flag)
|
||||||
|
std::thread(tx_test, trx, &trx->ts_hitter_q, &trx->mTSC).detach();
|
||||||
|
trx->start();
|
||||||
|
do {
|
||||||
|
sleep(1);
|
||||||
|
} while (1);
|
||||||
|
|
||||||
|
trx->stop_threads();
|
||||||
|
}
|
||||||
|
delete trx;
|
||||||
|
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
int ms_trx::init_dev_and_streams()
|
int ms_trx::init_dev_and_streams()
|
||||||
{
|
{
|
||||||
int status = 0;
|
int status = 0;
|
||||||
status = init_device(rx_bh(), tx_bh());
|
status = base::init_device(rx_bh(), tx_bh());
|
||||||
if (status < 0) {
|
if (status < 0) {
|
||||||
std::cerr << "failed to init dev!" << std::endl;
|
std::cerr << "failed to init dev!" << std::endl;
|
||||||
return -1;
|
return -1;
|
||||||
@@ -69,29 +258,24 @@ bh_fn_t ms_trx::rx_bh()
|
|||||||
bh_fn_t ms_trx::tx_bh()
|
bh_fn_t ms_trx::tx_bh()
|
||||||
{
|
{
|
||||||
return [this](dev_buf_t *rcd) -> int {
|
return [this](dev_buf_t *rcd) -> int {
|
||||||
#pragma GCC diagnostic push
|
#pragma unused(rcd)
|
||||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
|
||||||
auto y = this;
|
auto y = this;
|
||||||
#pragma GCC diagnostic pop
|
#pragma unused(y)
|
||||||
/* nothing to do here */
|
/* nothing to do here */
|
||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
void ms_trx::start_lower_ms()
|
void ms_trx::start()
|
||||||
{
|
{
|
||||||
if (stop_lower_threads_flag)
|
|
||||||
return;
|
|
||||||
auto fn = get_rx_burst_handler_fn(rx_bh());
|
auto fn = get_rx_burst_handler_fn(rx_bh());
|
||||||
lower_rx_task = std::thread(fn);
|
rx_task = std::thread(fn);
|
||||||
set_name_aff_sched(lower_rx_task.native_handle(), sched_params::thread_names::RXRUN);
|
set_name_aff_sched(rx_task.native_handle(), "rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2);
|
||||||
|
|
||||||
usleep(1000);
|
usleep(1000);
|
||||||
auto fn2 = get_tx_burst_handler_fn(tx_bh());
|
auto fn2 = get_tx_burst_handler_fn(tx_bh());
|
||||||
lower_tx_task = std::thread(fn2);
|
tx_task = std::thread(fn2);
|
||||||
set_name_aff_sched(lower_tx_task.native_handle(), sched_params::thread_names::TXRUN);
|
set_name_aff_sched(tx_task.native_handle(), "txrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1);
|
||||||
|
|
||||||
actually_enable_streams();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ms_trx::set_upper_ready(bool is_ready)
|
void ms_trx::set_upper_ready(bool is_ready)
|
||||||
@@ -101,14 +285,10 @@ void ms_trx::set_upper_ready(bool is_ready)
|
|||||||
|
|
||||||
void ms_trx::stop_threads()
|
void ms_trx::stop_threads()
|
||||||
{
|
{
|
||||||
std::cerr << "killing threads..." << std::endl;
|
std::cerr << "killing threads...\r\n" << std::endl;
|
||||||
stop_lower_threads_flag = true;
|
|
||||||
close_device();
|
close_device();
|
||||||
std::cerr << "dev closed..." << std::endl;
|
rx_task.join();
|
||||||
lower_rx_task.join();
|
tx_task.join();
|
||||||
std::cerr << "L rx dead..." << std::endl;
|
|
||||||
lower_tx_task.join();
|
|
||||||
std::cerr << "L tx dead..." << std::endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ms_trx::submit_burst(blade_sample_type *buffer, int len, GSM::Time target)
|
void ms_trx::submit_burst(blade_sample_type *buffer, int len, GSM::Time target)
|
||||||
@@ -129,26 +309,25 @@ void ms_trx::submit_burst(blade_sample_type *buffer, int len, GSM::Time target)
|
|||||||
else
|
else
|
||||||
tosend.decTN(-diff_tn);
|
tosend.decTN(-diff_tn);
|
||||||
|
|
||||||
// in theory fn equal and tn+3 equal is also a problem...
|
// in thory fn equal and tn+3 equal is also a problem...
|
||||||
if (diff_fn < 0 || (diff_fn == 0 && (now_time.TN() - target_tn < 1))) {
|
if (diff_fn < 0 || (diff_fn == 0 && (now_time.TN() - target_tn < 1))) {
|
||||||
std::cerr << "## TX too late?! fn DIFF:" << diff_fn << " tn LOCAL: " << now_time.TN()
|
std::cerr << "## TX too late?! fn DIFF:" << diff_fn << " tn LOCAL: " << now_time.TN()
|
||||||
<< " tn OTHER: " << target_tn << std::endl;
|
<< " tn OTHER: " << target_tn << std::endl;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto check = now_time + tosend;
|
||||||
int64_t send_ts = now_ts + tosend.FN() * 8 * ONE_TS_BURST_LEN + tosend.TN() * ONE_TS_BURST_LEN - timing_advance;
|
int64_t send_ts = now_ts + tosend.FN() * 8 * ONE_TS_BURST_LEN + tosend.TN() * ONE_TS_BURST_LEN - timing_advance;
|
||||||
#ifdef DBGXX
|
#ifdef DBGXX
|
||||||
auto check = now_time + tosend;
|
|
||||||
std::cerr << "## fn DIFF: " << diff_fn << " ## tn DIFF: " << diff_tn << " tn LOCAL/OTHER: " << now_time.TN()
|
std::cerr << "## fn DIFF: " << diff_fn << " ## tn DIFF: " << diff_tn << " tn LOCAL/OTHER: " << now_time.TN()
|
||||||
<< "/" << target_tn << " tndiff" << diff_tn << " tosend:" << tosend.FN() << ":" << tosend.TN()
|
<< "/" << target_tn << " tndiff" << diff_tn << " tosend:" << tosend.FN() << ":" << tosend.TN()
|
||||||
<< " check: " << check.FN() << ":" << check.TN() << " target: " << target.FN() << ":" << target.TN()
|
<< " check: " << check.FN() << ":" << check.TN() << " target: " << target.FN() << ":" << target.TN()
|
||||||
<< " ts now: " << now_ts << " target ts:" << send_ts << std::endl;
|
<< " ts now: " << now_ts << " target ts:" << send_ts << std::endl;
|
||||||
#endif
|
#endif
|
||||||
#if 0
|
#if 1
|
||||||
auto check = now_time + tosend;
|
|
||||||
unsigned int pad = 4 * 4;
|
unsigned int pad = 4 * 4;
|
||||||
blade_sample_type buf2[len + pad];
|
blade_sample_type buf2[len + pad];
|
||||||
std::fill(buf2, buf2 + pad, 0);
|
memset(buf2, 0, pad * sizeof(blade_sample_type));
|
||||||
memcpy(&buf2[pad], buffer, len * sizeof(blade_sample_type));
|
memcpy(&buf2[pad], buffer, len * sizeof(blade_sample_type));
|
||||||
|
|
||||||
assert(target.FN() == check.FN());
|
assert(target.FN() == check.FN());
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
@@ -34,92 +35,37 @@
|
|||||||
#elif defined(BUILDUHD)
|
#elif defined(BUILDUHD)
|
||||||
#include "uhd_specific.h"
|
#include "uhd_specific.h"
|
||||||
#define BASET uhd_hw<ms_trx>
|
#define BASET uhd_hw<ms_trx>
|
||||||
|
#elif defined(BUILDIPC)
|
||||||
|
#include "ipc_specific.h"
|
||||||
|
#define BASET ipc_hw<ms_trx>
|
||||||
#else
|
#else
|
||||||
#error wat? no device..
|
#error wat? no device..
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "Complex.h"
|
|
||||||
#include "GSMCommon.h"
|
#include "GSMCommon.h"
|
||||||
#include "itrq.h"
|
#include "itrq.h"
|
||||||
#include "threadpool.h"
|
|
||||||
|
|
||||||
const unsigned int ONE_TS_BURST_LEN = (3 + 58 + 26 + 58 + 3 + 8.25) * 4 /*sps*/;
|
const unsigned int ONE_TS_BURST_LEN = (3 + 58 + 26 + 58 + 3 + 8.25) * 4 /*sps*/;
|
||||||
const unsigned int NUM_RXQ_FRAMES = 1; // rx thread <-> upper rx queue
|
const unsigned int NUM_RXQ_FRAMES = 1; // rx thread <-> upper rx queue
|
||||||
const unsigned int SCH_LEN_SPS = (ONE_TS_BURST_LEN * 8 /*ts*/ * 12 /*frames*/);
|
const unsigned int SCH_LEN_SPS = (ONE_TS_BURST_LEN * 8 /*ts*/ * 12 /*frames*/);
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> void clamp_array(T *start2, unsigned int len, T max)
|
||||||
void clamp_array(T *start2, unsigned int len, T max)
|
|
||||||
{
|
{
|
||||||
for (unsigned int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
const T t1 = start2[i] < -max ? -max : start2[i];
|
const T t1 = start2[i] < -max ? -max : start2[i];
|
||||||
const T t2 = t1 > max ? max : t1;
|
const T t2 = t1 > max ? max : t1;
|
||||||
start2[i] = t2;
|
start2[i] = t2;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace cvt_internal
|
|
||||||
{
|
|
||||||
|
|
||||||
template <typename SRC_T, typename ST>
|
|
||||||
void convert_and_scale_i(float *dst, const SRC_T *src, unsigned int src_len, ST scale)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < src_len; i++)
|
|
||||||
dst[i] = static_cast<float>(src[i]) * scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename DST_T, typename ST>
|
|
||||||
void convert_and_scale_i(DST_T *dst, const float *src, unsigned int src_len, ST scale)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < src_len; i++)
|
|
||||||
dst[i] = static_cast<DST_T>(src[i] * scale);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename ST>
|
|
||||||
void convert_and_scale_i(float *dst, const float *src, unsigned int src_len, ST scale)
|
|
||||||
{
|
|
||||||
for (unsigned int i = 0; i < src_len; i++)
|
|
||||||
dst[i] = src[i] * scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_complex : std::false_type {
|
|
||||||
using baset = T;
|
|
||||||
static const unsigned int len_mul = 1;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_complex<std::complex<T>> : std::true_type {
|
|
||||||
using baset = typename std::complex<T>::value_type;
|
|
||||||
static const unsigned int len_mul = 2;
|
|
||||||
};
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
struct is_complex<Complex<T>> : std::true_type {
|
|
||||||
using baset = typename Complex<T>::value_type;
|
|
||||||
static const unsigned int len_mul = 2;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace cvt_internal
|
|
||||||
|
|
||||||
template <typename DST_T, typename SRC_T, typename ST>
|
template <typename DST_T, typename SRC_T, typename ST>
|
||||||
void convert_and_scale(DST_T *dst, const SRC_T *src, unsigned int src_len, ST scale)
|
void convert_and_scale(void *dst, void *src, unsigned int src_len, ST scale)
|
||||||
{
|
{
|
||||||
using vd = typename cvt_internal::is_complex<DST_T>::baset;
|
for (unsigned int i = 0; i < src_len; i++)
|
||||||
using vs = typename cvt_internal::is_complex<SRC_T>::baset;
|
reinterpret_cast<DST_T *>(dst)[i] = static_cast<DST_T>((reinterpret_cast<SRC_T *>(src)[i])) * scale;
|
||||||
return cvt_internal::convert_and_scale_i((vd *)dst, (vs *)src, src_len, scale);
|
|
||||||
}
|
}
|
||||||
|
template <typename DST_T, typename SRC_T> void convert_and_scale_default(void *dst, void *src, unsigned int src_len)
|
||||||
template <typename array_t>
|
|
||||||
float normed_abs_sum(array_t *src, int len)
|
|
||||||
{
|
{
|
||||||
using vd = typename cvt_internal::is_complex<array_t>::baset;
|
return convert_and_scale<DST_T, SRC_T>(dst, src, src_len, SAMPLE_SCALE_FACTOR);
|
||||||
auto len_mul = cvt_internal::is_complex<array_t>::len_mul;
|
|
||||||
auto ptr = reinterpret_cast<const vd *>(src);
|
|
||||||
float sum = 0;
|
|
||||||
for (unsigned int i = 0; i < len * len_mul; i++)
|
|
||||||
sum += std::abs(ptr[i]);
|
|
||||||
sum /= len * len_mul;
|
|
||||||
return sum;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct one_burst {
|
struct one_burst {
|
||||||
@@ -133,7 +79,7 @@ struct one_burst {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
using rx_queue_t = spsc_cond_timeout<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 };
|
enum class SCH_STATE { SEARCHING, FOUND };
|
||||||
|
|
||||||
@@ -217,40 +163,6 @@ class time_keeper {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
static struct sched_params {
|
|
||||||
enum thread_names { U_CTL = 0, U_RX, U_TX, SCH_SEARCH, MAIN, LEAKCHECK, RXRUN, TXRUN, _THRD_NAME_COUNT };
|
|
||||||
enum target { ODROID = 0, PI4 };
|
|
||||||
const char *name;
|
|
||||||
int core;
|
|
||||||
int schedtype;
|
|
||||||
int prio;
|
|
||||||
} schdp[][sched_params::_THRD_NAME_COUNT]{
|
|
||||||
{
|
|
||||||
{ "upper_ctrl", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) },
|
|
||||||
{ "upper_rx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
|
|
||||||
{ "upper_tx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 1 },
|
|
||||||
|
|
||||||
{ "sch_search", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
|
|
||||||
{ "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
|
|
||||||
{ "leakcheck", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
|
|
||||||
|
|
||||||
{ "rxrun", 4, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
|
|
||||||
{ "txrun", 5, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
{ "upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) },
|
|
||||||
{ "upper_rx", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
|
|
||||||
{ "upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
|
|
||||||
|
|
||||||
{ "sch_search", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
|
|
||||||
{ "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
|
|
||||||
{ "leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
|
|
||||||
|
|
||||||
{ "rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
|
|
||||||
{ "txrun", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>;
|
using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>;
|
||||||
|
|
||||||
struct ms_trx : public BASET {
|
struct ms_trx : public BASET {
|
||||||
@@ -261,12 +173,15 @@ struct ms_trx : public BASET {
|
|||||||
int timing_advance;
|
int timing_advance;
|
||||||
bool do_auto_gain;
|
bool do_auto_gain;
|
||||||
|
|
||||||
std::thread lower_rx_task;
|
std::thread rx_task;
|
||||||
std::thread lower_tx_task;
|
std::thread tx_task;
|
||||||
|
std::thread *calcrval_task;
|
||||||
|
|
||||||
// provides bursts to upper rx thread
|
// provides bursts to upper rx thread
|
||||||
rx_queue_t rxqueue;
|
rx_queue_t rxqueue;
|
||||||
|
#ifdef SYNCTHINGONLY
|
||||||
|
ts_hitter_q_t ts_hitter_q;
|
||||||
|
#endif
|
||||||
blade_sample_type *first_sch_buf;
|
blade_sample_type *first_sch_buf;
|
||||||
blade_sample_type *burst_copy_buffer;
|
blade_sample_type *burst_copy_buffer;
|
||||||
|
|
||||||
@@ -278,20 +193,18 @@ struct ms_trx : public BASET {
|
|||||||
int64_t first_sch_ts_start = -1;
|
int64_t first_sch_ts_start = -1;
|
||||||
|
|
||||||
time_keeper timekeeper;
|
time_keeper timekeeper;
|
||||||
int hw_cpus;
|
|
||||||
sched_params::target hw_target;
|
|
||||||
single_thread_pool worker_thread;
|
|
||||||
|
|
||||||
void start_lower_ms();
|
void start();
|
||||||
std::atomic<bool> upper_is_ready;
|
std::atomic<bool> upper_is_ready;
|
||||||
void set_upper_ready(bool is_ready);
|
void set_upper_ready(bool is_ready);
|
||||||
|
|
||||||
bool handle_sch_or_nb();
|
bool handle_sch_or_nb();
|
||||||
bool handle_sch(bool first = false);
|
bool handle_sch(bool first = false);
|
||||||
bool decode_sch(char *bits, bool update_global_clock);
|
bool decode_sch(float *bits, bool update_global_clock);
|
||||||
SCH_STATE search_for_sch(dev_buf_t *rcd);
|
SCH_STATE search_for_sch(dev_buf_t *rcd);
|
||||||
void grab_bursts(dev_buf_t *rcd);
|
void grab_bursts(dev_buf_t *rcd) __attribute__((optnone));
|
||||||
|
|
||||||
|
int init_device();
|
||||||
int init_dev_and_streams();
|
int init_dev_and_streams();
|
||||||
void stop_threads();
|
void stop_threads();
|
||||||
void *rx_cb(ms_trx *t);
|
void *rx_cb(ms_trx *t);
|
||||||
@@ -299,15 +212,9 @@ struct ms_trx : public BASET {
|
|||||||
void maybe_update_gain(one_burst &brst);
|
void maybe_update_gain(one_burst &brst);
|
||||||
|
|
||||||
ms_trx()
|
ms_trx()
|
||||||
: mTSC(0), mBSIC(0), timing_advance(0), do_auto_gain(false), rxqueue(),
|
: timing_advance(0), do_auto_gain(false), rxqueue(), first_sch_buf(new blade_sample_type[SCH_LEN_SPS]),
|
||||||
first_sch_buf(new blade_sample_type[SCH_LEN_SPS]),
|
burst_copy_buffer(new blade_sample_type[ONE_TS_BURST_LEN]), rcv_done{ false }, sch_thread_done{ false }
|
||||||
burst_copy_buffer(new blade_sample_type[ONE_TS_BURST_LEN]), first_sch_buf_rcv_ts(0),
|
|
||||||
rcv_done{ false }, sch_thread_done{ false }, hw_cpus(std::thread::hardware_concurrency()),
|
|
||||||
hw_target(hw_cpus > 4 ? sched_params::target::ODROID : sched_params::target::PI4),
|
|
||||||
upper_is_ready(false)
|
|
||||||
{
|
{
|
||||||
std::cerr << "scheduling for: " << (hw_cpus > 4 ? "odroid" : "pi4") << std::endl;
|
|
||||||
set_name_aff_sched(worker_thread.get_handle(), sched_params::thread_names::SCH_SEARCH);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual ~ms_trx()
|
virtual ~ms_trx()
|
||||||
@@ -325,28 +232,11 @@ struct ms_trx : public BASET {
|
|||||||
timing_advance = val * 4;
|
timing_advance = val * 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_name_aff_sched(sched_params::thread_names name)
|
void set_name_aff_sched(const char *name, int cpunum, int schedtype, int prio)
|
||||||
{
|
{
|
||||||
set_name_aff_sched(pthread_self(), name);
|
set_name_aff_sched(pthread_self(), name, cpunum, schedtype, prio);
|
||||||
}
|
}
|
||||||
|
|
||||||
void set_name_aff_sched(std::thread::native_handle_type h, sched_params::thread_names name)
|
|
||||||
{
|
|
||||||
auto tgt = schdp[hw_target][name];
|
|
||||||
// std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << std::endl;
|
|
||||||
set_name_aff_sched(h, tgt.name, tgt.core, tgt.schedtype, tgt.prio);
|
|
||||||
}
|
|
||||||
|
|
||||||
using pt_sig = void *(*)(void *);
|
|
||||||
|
|
||||||
pthread_t spawn_worker_thread(sched_params::thread_names name, pt_sig fun, void *arg)
|
|
||||||
{
|
|
||||||
auto tgt = schdp[hw_target][name];
|
|
||||||
// std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << " prio:" << tgt.prio << std::endl;
|
|
||||||
return do_spawn_thr(tgt.name, tgt.core, tgt.schedtype, tgt.prio, fun, arg);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void set_name_aff_sched(std::thread::native_handle_type h, const char *name, int cpunum, int schedtype,
|
void set_name_aff_sched(std::thread::native_handle_type h, const char *name, int cpunum, int schedtype,
|
||||||
int prio)
|
int prio)
|
||||||
{
|
{
|
||||||
@@ -357,40 +247,18 @@ struct ms_trx : public BASET {
|
|||||||
CPU_ZERO(&cpuset);
|
CPU_ZERO(&cpuset);
|
||||||
CPU_SET(cpunum, &cpuset);
|
CPU_SET(cpunum, &cpuset);
|
||||||
|
|
||||||
if (pthread_setaffinity_np(h, sizeof(cpuset), &cpuset) < 0) {
|
auto rv = pthread_setaffinity_np(h, sizeof(cpuset), &cpuset);
|
||||||
|
if (rv < 0) {
|
||||||
std::cerr << name << " affinity: errreur! " << std::strerror(errno);
|
std::cerr << name << " affinity: errreur! " << std::strerror(errno);
|
||||||
return exit(0);
|
return exit(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
sched_param sch_params;
|
sched_param sch_params;
|
||||||
sch_params.sched_priority = prio;
|
sch_params.sched_priority = prio;
|
||||||
if (pthread_setschedparam(h, schedtype, &sch_params) < 0) {
|
rv = pthread_setschedparam(h, schedtype, &sch_params);
|
||||||
|
if (rv < 0) {
|
||||||
std::cerr << name << " sched: errreur! " << std::strerror(errno);
|
std::cerr << name << " sched: errreur! " << std::strerror(errno);
|
||||||
return exit(0);
|
return exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pthread_t do_spawn_thr(const char *name, int cpunum, int schedtype, int prio, pt_sig fun, void *arg)
|
|
||||||
{
|
|
||||||
pthread_t thread;
|
|
||||||
|
|
||||||
pthread_attr_t attr;
|
|
||||||
pthread_attr_init(&attr);
|
|
||||||
|
|
||||||
sched_param sch_params;
|
|
||||||
sch_params.sched_priority = prio;
|
|
||||||
cpu_set_t cpuset;
|
|
||||||
CPU_ZERO(&cpuset);
|
|
||||||
CPU_SET(cpunum, &cpuset);
|
|
||||||
auto a = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
|
|
||||||
a |= pthread_attr_setschedpolicy(&attr, schedtype);
|
|
||||||
a |= pthread_attr_setschedparam(&attr, &sch_params);
|
|
||||||
a |= pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
|
|
||||||
if(a)
|
|
||||||
std::cerr << "thread arg rc:" << a << std::endl;
|
|
||||||
pthread_create(&thread, &attr, fun, arg);
|
|
||||||
pthread_setname_np(thread, name);
|
|
||||||
pthread_attr_destroy(&attr);
|
|
||||||
return thread;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
/*
|
/*
|
||||||
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*
|
*
|
||||||
* Author: Eric Wild <ewild@sysmocom.de>
|
* Author: Eric Wild <ewild@sysmocom.de>
|
||||||
@@ -21,22 +21,5 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "ms.h"
|
#include "ms.h"
|
||||||
extern "C" {
|
|
||||||
#include <osmocom/bb/trxcon/phyif.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
extern struct trxcon_inst *g_trxcon;
|
void rcv_bursts_test(rx_queue_t *q, unsigned int *tsc, int scale);
|
||||||
struct internal_q_tx_buf {
|
|
||||||
trxcon_phyif_burst_req r;
|
|
||||||
uint8_t buf[148];
|
|
||||||
internal_q_tx_buf() = default;
|
|
||||||
internal_q_tx_buf(const internal_q_tx_buf &) = delete;
|
|
||||||
internal_q_tx_buf &operator=(const internal_q_tx_buf &) = default;
|
|
||||||
internal_q_tx_buf(const struct trxcon_phyif_burst_req *br) : r(*br)
|
|
||||||
{
|
|
||||||
memcpy(buf, (void *)br->burst, br->burst_len);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
using tx_queue_t = spsc_cond<8 * 1, internal_q_tx_buf, true, false>;
|
|
||||||
using cmd_queue_t = spsc_cond_timeout<8 * 1, trxcon_phyif_cmd, true, false>;
|
|
||||||
using cmdr_queue_t = spsc_cond<8 * 1, trxcon_phyif_rsp, false, false>;
|
|
||||||
207
Transceiver52M/ms/ms_rx_burst_test.cpp
Normal file
207
Transceiver52M/ms/ms_rx_burst_test.cpp
Normal file
@@ -0,0 +1,207 @@
|
|||||||
|
/*
|
||||||
|
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
|
* All Rights Reserved
|
||||||
|
*
|
||||||
|
* Author: Eric Wild <ewild@sysmocom.de>
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation; either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU Affero General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#include "ms.h"
|
||||||
|
#include "sigProcLib.h"
|
||||||
|
#include "signalVector.h"
|
||||||
|
#include "grgsm_vitac/grgsm_vitac.h"
|
||||||
|
extern "C" {
|
||||||
|
#include "sch.h"
|
||||||
|
}
|
||||||
|
|
||||||
|
#if !defined(SYNCTHINGONLY) || !defined(NODAMNLOG)
|
||||||
|
#define DBGLG(...) ms_trx::dummy_log()
|
||||||
|
#else
|
||||||
|
#define DBGLG(...) std::cerr
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if !defined(SYNCTHINGONLY)
|
||||||
|
#define DBGLG2(...) ms_trx::dummy_log()
|
||||||
|
#else
|
||||||
|
#define DBGLG2(...) std::cerr
|
||||||
|
#endif
|
||||||
|
|
||||||
|
static bool decode_sch(float *bits, bool update_global_clock)
|
||||||
|
{
|
||||||
|
struct sch_info sch;
|
||||||
|
ubit_t info[GSM_SCH_INFO_LEN];
|
||||||
|
sbit_t data[GSM_SCH_CODED_LEN];
|
||||||
|
|
||||||
|
float_to_sbit(&bits[3], &data[0], 62, 39);
|
||||||
|
float_to_sbit(&bits[106], &data[39], 62, 39);
|
||||||
|
|
||||||
|
if (!gsm_sch_decode(info, data)) {
|
||||||
|
gsm_sch_parse(info, &sch);
|
||||||
|
|
||||||
|
DBGLG() << "SCH : Decoded values" << std::endl;
|
||||||
|
DBGLG() << " BSIC: " << sch.bsic << std::endl;
|
||||||
|
DBGLG() << " TSC: " << (sch.bsic & 0x7) << std::endl;
|
||||||
|
DBGLG() << " T1 : " << sch.t1 << std::endl;
|
||||||
|
DBGLG() << " T2 : " << sch.t2 << std::endl;
|
||||||
|
DBGLG() << " T3p : " << sch.t3p << std::endl;
|
||||||
|
DBGLG() << " FN : " << gsm_sch_to_fn(&sch) << std::endl;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void check_rcv_fn(GSM::Time t, bool first, unsigned int &lastfn, unsigned int &fnbm)
|
||||||
|
{
|
||||||
|
if (first && t.TN() == 0) {
|
||||||
|
lastfn = t.FN();
|
||||||
|
fnbm = 1 << 0;
|
||||||
|
first = false;
|
||||||
|
}
|
||||||
|
if (!first && t.FN() != lastfn) {
|
||||||
|
if (fnbm != 255)
|
||||||
|
std::cerr << "rx " << lastfn << ":" << fnbm << " " << __builtin_popcount(fnbm) << std::endl;
|
||||||
|
lastfn = t.FN();
|
||||||
|
fnbm = 1 << t.TN();
|
||||||
|
}
|
||||||
|
|
||||||
|
fnbm |= 1 << t.TN();
|
||||||
|
}
|
||||||
|
|
||||||
|
static void handle_it(one_burst &e, signalVector &burst, unsigned int tsc, int scale)
|
||||||
|
{
|
||||||
|
memset(burst.begin(), 0, burst.size() * sizeof(std::complex<float>));
|
||||||
|
const auto is_sch = gsm_sch_check_ts(e.gsmts.TN(), e.gsmts.FN());
|
||||||
|
const auto is_fcch = gsm_fcch_check_ts(e.gsmts.TN(), e.gsmts.FN());
|
||||||
|
|
||||||
|
if (is_fcch)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (is_sch) {
|
||||||
|
char outbin[148];
|
||||||
|
convert_and_scale_default<float, int16_t>(burst.begin(), e.burst, ONE_TS_BURST_LEN * 2);
|
||||||
|
std::stringstream dbgout;
|
||||||
|
#if 0
|
||||||
|
{
|
||||||
|
struct estim_burst_params ebp;
|
||||||
|
auto rv2 = detectSCHBurst(burst, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp);
|
||||||
|
auto bits = demodAnyBurst(burst, SCH, 4, &ebp);
|
||||||
|
// clamp_array(bits->begin(), 148, 1.5f);
|
||||||
|
for (auto &i : *bits)
|
||||||
|
i = (i > 0 ? 1 : -1);
|
||||||
|
|
||||||
|
auto rv = decode_sch(bits->begin(), false);
|
||||||
|
dbgout << "U DET@" << (rv2 ? "yes " : " ") << "Timing offset " << ebp.toa
|
||||||
|
<< " symbols, DECODE: " << (rv ? "yes" : "---") << " ";
|
||||||
|
|
||||||
|
delete bits;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
{
|
||||||
|
convert_and_scale<float, float>(burst.begin(), burst.begin(), ONE_TS_BURST_LEN * 2,
|
||||||
|
1.f / float(scale));
|
||||||
|
|
||||||
|
std::complex<float> channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
|
||||||
|
auto ss = reinterpret_cast<std::complex<float> *>(burst.begin());
|
||||||
|
int d_c0_burst_start = get_sch_chan_imp_resp(ss, &channel_imp_resp[0]);
|
||||||
|
detect_burst(ss, &channel_imp_resp[0], d_c0_burst_start, outbin);
|
||||||
|
|
||||||
|
SoftVector bits;
|
||||||
|
bits.resize(148);
|
||||||
|
for (int i = 0; i < 148; i++) {
|
||||||
|
bits[i] = (!outbin[i]); // < 1 ? -1 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto rv = decode_sch(bits.begin(), false);
|
||||||
|
dbgout << "U SCH@"
|
||||||
|
<< " " << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << d_c0_burst_start
|
||||||
|
<< " DECODE:" << (rv ? "yes" : "---") << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBGLG() << dbgout.str();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#if 1
|
||||||
|
convert_and_scale<float, int16_t>(burst.begin(), e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(scale));
|
||||||
|
// std::cerr << "@" << tsc << " " << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << ebp.toa << " "
|
||||||
|
// << std::endl;
|
||||||
|
|
||||||
|
char outbin[148];
|
||||||
|
auto ss = reinterpret_cast<std::complex<float> *>(burst.begin());
|
||||||
|
float ncmax, dcmax;
|
||||||
|
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR], chan_imp_resp2[CHAN_IMP_RESP_LENGTH * d_OSR];
|
||||||
|
auto normal_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp[0], &ncmax, tsc);
|
||||||
|
auto dummy_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp2[0], &dcmax, TS_DUMMY);
|
||||||
|
auto is_nb = ncmax > dcmax;
|
||||||
|
|
||||||
|
DBGLG() << " U " << (is_nb ? "NB" : "DB") << "@ o nb: " << normal_burst_start << " o db: " << dummy_burst_start
|
||||||
|
<< std::endl;
|
||||||
|
|
||||||
|
if (is_nb)
|
||||||
|
detect_burst(ss, &chan_imp_resp[0], normal_burst_start, outbin);
|
||||||
|
else
|
||||||
|
detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
|
||||||
|
;
|
||||||
|
#ifdef DBGXX
|
||||||
|
// auto bits = SoftVector(148);
|
||||||
|
// for (int i = 0; i < 148; i++)
|
||||||
|
// (bits)[i] = outbin[i] < 1 ? -1 : 1;
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void rcv_bursts_test(rx_queue_t *q, unsigned int *tsc, int scale)
|
||||||
|
{
|
||||||
|
static bool first = true;
|
||||||
|
unsigned int lastfn = 0;
|
||||||
|
unsigned int fnbm = 0;
|
||||||
|
signalVector burst(ONE_TS_BURST_LEN, 100, 100);
|
||||||
|
|
||||||
|
cpu_set_t cpuset;
|
||||||
|
|
||||||
|
CPU_ZERO(&cpuset);
|
||||||
|
CPU_SET(1, &cpuset);
|
||||||
|
|
||||||
|
auto rv = pthread_setaffinity_np(pthread_self(), sizeof(cpuset), &cpuset);
|
||||||
|
if (rv < 0) {
|
||||||
|
std::cerr << "affinity: errreur! " << std::strerror(errno);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
int prio = sched_get_priority_max(SCHED_RR);
|
||||||
|
struct sched_param param;
|
||||||
|
param.sched_priority = prio;
|
||||||
|
rv = sched_setscheduler(0, SCHED_RR, ¶m);
|
||||||
|
if (rv < 0) {
|
||||||
|
std::cerr << "scheduler: errreur! " << std::strerror(errno);
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
while (1) {
|
||||||
|
one_burst e;
|
||||||
|
while (!q->spsc_pop(&e)) {
|
||||||
|
q->spsc_prep_pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
check_rcv_fn(e.gsmts, first, lastfn, fnbm);
|
||||||
|
|
||||||
|
handle_it(e, burst, *tsc, scale);
|
||||||
|
#ifdef DBGXX
|
||||||
|
rv = detectSCHBurst(*burst, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp);
|
||||||
|
if (rv > 0)
|
||||||
|
std::cerr << "#" << e.gsmts.FN() << ":" << e.gsmts.TN() << " " << ebp.toa << std::endl;
|
||||||
|
sched_yield();
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,6 +19,8 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include "sigProcLib.h"
|
||||||
|
#include "signalVector.h"
|
||||||
#include <atomic>
|
#include <atomic>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <complex>
|
#include <complex>
|
||||||
@@ -28,8 +30,6 @@
|
|||||||
#include "ms.h"
|
#include "ms.h"
|
||||||
#include "grgsm_vitac/grgsm_vitac.h"
|
#include "grgsm_vitac/grgsm_vitac.h"
|
||||||
|
|
||||||
#include "threadpool.h"
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "sch.h"
|
#include "sch.h"
|
||||||
}
|
}
|
||||||
@@ -38,29 +38,28 @@ extern "C" {
|
|||||||
#undef LOG
|
#undef LOG
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(NODAMNLOG)
|
#if !defined(SYNCTHINGONLY) //|| !defined(NODAMNLOG)
|
||||||
#define DBGLG(...) ms_trx::dummy_log()
|
#define DBGLG(...) ms_trx::dummy_log()
|
||||||
#else
|
#else
|
||||||
#define DBGLG(...) std::cerr
|
#define DBGLG(...) std::cerr
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if !defined(NODAMNLOG)
|
#if !defined(SYNCTHINGONLY) || !defined(NODAMNLOG)
|
||||||
#define DBGLG2(...) ms_trx::dummy_log()
|
#define DBGLG2(...) ms_trx::dummy_log()
|
||||||
#else
|
#else
|
||||||
#define DBGLG2(...) std::cerr
|
#define DBGLG2(...) std::cerr
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define PRINT_Q_OVERFLOW
|
#define PRINT_Q_OVERFLOW
|
||||||
|
bool ms_trx::decode_sch(float *bits, bool update_global_clock)
|
||||||
bool ms_trx::decode_sch(char *bits, bool update_global_clock)
|
|
||||||
{
|
{
|
||||||
int fn;
|
int fn;
|
||||||
struct sch_info sch;
|
struct sch_info sch;
|
||||||
ubit_t info[GSM_SCH_INFO_LEN];
|
ubit_t info[GSM_SCH_INFO_LEN];
|
||||||
sbit_t data[GSM_SCH_CODED_LEN];
|
sbit_t data[GSM_SCH_CODED_LEN];
|
||||||
|
|
||||||
memcpy(&data[0], &bits[3], 39);
|
float_to_sbit(&bits[3], &data[0], 1, 39);
|
||||||
memcpy(&data[39], &bits[106], 39);
|
float_to_sbit(&bits[106], &data[39], 1, 39);
|
||||||
|
|
||||||
if (!gsm_sch_decode(info, data)) {
|
if (!gsm_sch_decode(info, data)) {
|
||||||
gsm_sch_parse(info, &sch);
|
gsm_sch_parse(info, &sch);
|
||||||
@@ -88,6 +87,18 @@ bool ms_trx::decode_sch(char *bits, bool update_global_clock)
|
|||||||
// global_time_keeper.FN(fn);
|
// global_time_keeper.FN(fn);
|
||||||
// global_time_keeper.TN(0);
|
// global_time_keeper.TN(0);
|
||||||
}
|
}
|
||||||
|
#ifdef SYNCTHINGONLY
|
||||||
|
else {
|
||||||
|
int t3 = sch.t3p * 10 + 1;
|
||||||
|
if (t3 == 11) {
|
||||||
|
// timeslot hitter attempt @ fn 21 in mf
|
||||||
|
DBGLG2() << "sch @ " << t3 << std::endl;
|
||||||
|
auto e = GSM::Time(fn, 0);
|
||||||
|
e += 10;
|
||||||
|
ts_hitter_q.spsc_push(&e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -102,7 +113,11 @@ void ms_trx::maybe_update_gain(one_burst &brst)
|
|||||||
const unsigned int rx_max_cutoff = (rxFullScale * 2) / 3;
|
const unsigned int rx_max_cutoff = (rxFullScale * 2) / 3;
|
||||||
static int gain_check = 0;
|
static int gain_check = 0;
|
||||||
static float runmean = 0;
|
static float runmean = 0;
|
||||||
float sum = normed_abs_sum(&brst.burst[0], ONE_TS_BURST_LEN);
|
float sum = 0;
|
||||||
|
for (auto i : brst.burst)
|
||||||
|
sum += abs(i.real()) + abs(i.imag());
|
||||||
|
sum /= ONE_TS_BURST_LEN * 2;
|
||||||
|
|
||||||
runmean = gain_check ? (runmean * (gain_check + 2) - 1 + sum) / (gain_check + 2) : sum;
|
runmean = gain_check ? (runmean * (gain_check + 2) - 1 + sum) / (gain_check + 2) : sum;
|
||||||
|
|
||||||
if (gain_check == avgburst_num - 1) {
|
if (gain_check == avgburst_num - 1) {
|
||||||
@@ -111,11 +126,8 @@ void ms_trx::maybe_update_gain(one_burst &brst)
|
|||||||
gainoffset = runmean < (rxFullScale / 2 ? 2 : 1);
|
gainoffset = runmean < (rxFullScale / 2 ? 2 : 1);
|
||||||
float newgain = runmean < rx_max_cutoff ? rxgain + gainoffset : rxgain - gainoffset;
|
float newgain = runmean < rx_max_cutoff ? rxgain + gainoffset : rxgain - gainoffset;
|
||||||
// FIXME: gian cutoff
|
// FIXME: gian cutoff
|
||||||
if (newgain != rxgain && newgain <= 60) {
|
if (newgain != rxgain && newgain <= 60)
|
||||||
auto gain_fun = [this, newgain] { setRxGain(newgain); };
|
std::thread([this, newgain] { setRxGain(newgain); }).detach();
|
||||||
worker_thread.add_task(gain_fun);
|
|
||||||
}
|
|
||||||
|
|
||||||
runmean = 0;
|
runmean = 0;
|
||||||
}
|
}
|
||||||
gain_check = (gain_check + 1) % avgburst_num;
|
gain_check = (gain_check + 1) % avgburst_num;
|
||||||
@@ -128,6 +140,8 @@ bool ms_trx::handle_sch_or_nb()
|
|||||||
one_burst brst;
|
one_burst brst;
|
||||||
const auto current_gsm_time = timekeeper.gsmtime();
|
const auto current_gsm_time = timekeeper.gsmtime();
|
||||||
const auto is_sch = gsm_sch_check_ts(current_gsm_time.TN(), current_gsm_time.FN());
|
const auto is_sch = gsm_sch_check_ts(current_gsm_time.TN(), current_gsm_time.FN());
|
||||||
|
const auto is_fcch = gsm_fcch_check_ts(current_gsm_time.TN(), current_gsm_time.FN());
|
||||||
|
#pragma unused(is_fcch)
|
||||||
|
|
||||||
//either pass burst to upper layer for demod, OR pass demodded SCH to upper layer so we don't waste time processing it twice
|
//either pass burst to upper layer for demod, OR pass demodded SCH to upper layer so we don't waste time processing it twice
|
||||||
brst.gsmts = current_gsm_time;
|
brst.gsmts = current_gsm_time;
|
||||||
@@ -138,9 +152,14 @@ bool ms_trx::handle_sch_or_nb()
|
|||||||
handle_sch(false);
|
handle_sch(false);
|
||||||
memcpy(brst.sch_bits, sch_demod_bits, sizeof(sch_demod_bits));
|
memcpy(brst.sch_bits, sch_demod_bits, sizeof(sch_demod_bits));
|
||||||
}
|
}
|
||||||
|
#ifndef SYNCTHINGONLY
|
||||||
while (upper_is_ready && !rxqueue.spsc_push(&brst))
|
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
|
||||||
|
|
||||||
if (do_auto_gain)
|
if (do_auto_gain)
|
||||||
maybe_update_gain(brst);
|
maybe_update_gain(brst);
|
||||||
@@ -161,18 +180,27 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
|
|||||||
|
|
||||||
int start;
|
int start;
|
||||||
memset((void *)&sch_acq_buffer[0], 0, sizeof(sch_acq_buffer));
|
memset((void *)&sch_acq_buffer[0], 0, sizeof(sch_acq_buffer));
|
||||||
convert_and_scale(which_out_buffer, which_in_buffer, buf_len * 2, 1.f / float(rxFullScale));
|
|
||||||
if (is_first_sch_acq) {
|
if (is_first_sch_acq) {
|
||||||
float max_corr = 0;
|
float max_corr = 0;
|
||||||
|
convert_and_scale<float, int16_t>(which_out_buffer, which_in_buffer, buf_len * 2,
|
||||||
|
1.f / float(rxFullScale));
|
||||||
start = get_sch_buffer_chan_imp_resp(ss, &channel_imp_resp[0], buf_len, &max_corr);
|
start = get_sch_buffer_chan_imp_resp(ss, &channel_imp_resp[0], buf_len, &max_corr);
|
||||||
|
detect_burst(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
|
||||||
} else {
|
} else {
|
||||||
|
convert_and_scale<float, int16_t>(which_out_buffer, which_in_buffer, buf_len * 2,
|
||||||
|
1.f / float(rxFullScale));
|
||||||
start = get_sch_chan_imp_resp(ss, &channel_imp_resp[0]);
|
start = get_sch_chan_imp_resp(ss, &channel_imp_resp[0]);
|
||||||
start = start < 39 ? start : 39;
|
start = start < 39 ? start : 39;
|
||||||
start = start > -39 ? start : -39;
|
start = start > -39 ? start : -39;
|
||||||
|
detect_burst(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
|
||||||
}
|
}
|
||||||
detect_burst_nb(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
|
|
||||||
|
|
||||||
auto sch_decode_success = decode_sch(sch_demod_bits, is_first_sch_acq);
|
SoftVector bitss(148);
|
||||||
|
for (int i = 0; i < 148; i++) {
|
||||||
|
bitss[i] = (sch_demod_bits[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto sch_decode_success = decode_sch(bitss.begin(), is_first_sch_acq);
|
||||||
|
|
||||||
if (sch_decode_success) {
|
if (sch_decode_success) {
|
||||||
const auto ts_offset_symb = 0;
|
const auto ts_offset_symb = 0;
|
||||||
@@ -193,38 +221,32 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
__attribute__((xray_never_instrument)) SCH_STATE ms_trx::search_for_sch(dev_buf_t *rcd)
|
||||||
accumulates a full big buffer consisting of 8*12 timeslots, then:
|
|
||||||
either
|
|
||||||
1) adjusts gain if necessary and starts over
|
|
||||||
2) searches and finds SCH and is done
|
|
||||||
*/
|
|
||||||
SCH_STATE ms_trx::search_for_sch(dev_buf_t *rcd)
|
|
||||||
{
|
{
|
||||||
static unsigned int sch_pos = 0;
|
static unsigned int sch_pos = 0;
|
||||||
auto to_copy = SCH_LEN_SPS - sch_pos;
|
|
||||||
|
|
||||||
if (sch_thread_done)
|
if (sch_thread_done)
|
||||||
return SCH_STATE::FOUND;
|
return SCH_STATE::FOUND;
|
||||||
|
|
||||||
if (rcv_done)
|
if (rcv_done)
|
||||||
return SCH_STATE::SEARCHING;
|
return SCH_STATE::SEARCHING;
|
||||||
|
|
||||||
if (sch_pos == 0) // keep first ts for time delta calc
|
auto to_copy = SCH_LEN_SPS - sch_pos;
|
||||||
|
|
||||||
|
if (SCH_LEN_SPS == to_copy) // first time
|
||||||
first_sch_buf_rcv_ts = rcd->get_first_ts();
|
first_sch_buf_rcv_ts = rcd->get_first_ts();
|
||||||
|
|
||||||
if (to_copy) {
|
if (!to_copy) {
|
||||||
auto spsmax = rcd->actual_samples_per_buffer();
|
|
||||||
if (to_copy > (unsigned int)spsmax)
|
|
||||||
sch_pos += rcd->readall(first_sch_buf + sch_pos);
|
|
||||||
else
|
|
||||||
sch_pos += rcd->read_n(first_sch_buf + sch_pos, 0, to_copy);
|
|
||||||
} else { // (!to_copy)
|
|
||||||
sch_pos = 0;
|
sch_pos = 0;
|
||||||
rcv_done = true;
|
rcv_done = true;
|
||||||
auto sch_search_fun = [this] {
|
std::thread([this] {
|
||||||
|
set_name_aff_sched("sch_search", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5);
|
||||||
|
|
||||||
|
auto ptr = reinterpret_cast<const int16_t *>(first_sch_buf);
|
||||||
const auto target_val = rxFullScale / 8;
|
const auto target_val = rxFullScale / 8;
|
||||||
float sum = normed_abs_sum(first_sch_buf, SCH_LEN_SPS);
|
float sum = 0;
|
||||||
|
for (int i = 0; i < SCH_LEN_SPS * 2; i++)
|
||||||
|
sum += std::abs(ptr[i]);
|
||||||
|
sum /= SCH_LEN_SPS * 2;
|
||||||
|
|
||||||
//FIXME: arbitrary value, gain cutoff
|
//FIXME: arbitrary value, gain cutoff
|
||||||
if (sum > target_val || rxgain >= 60) // enough ?
|
if (sum > target_val || rxgain >= 60) // enough ?
|
||||||
@@ -237,9 +259,16 @@ SCH_STATE ms_trx::search_for_sch(dev_buf_t *rcd)
|
|||||||
|
|
||||||
if (!sch_thread_done)
|
if (!sch_thread_done)
|
||||||
rcv_done = false; // retry!
|
rcv_done = false; // retry!
|
||||||
};
|
return (bool)sch_thread_done;
|
||||||
worker_thread.add_task(sch_search_fun);
|
}).detach();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto spsmax = rcd->actual_samples_per_buffer();
|
||||||
|
if (to_copy > spsmax)
|
||||||
|
sch_pos += rcd->readall(first_sch_buf + sch_pos);
|
||||||
|
else
|
||||||
|
sch_pos += rcd->read_n(first_sch_buf + sch_pos, 0, to_copy);
|
||||||
|
|
||||||
return SCH_STATE::SEARCHING;
|
return SCH_STATE::SEARCHING;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -257,7 +286,7 @@ void ms_trx::grab_bursts(dev_buf_t *rcd)
|
|||||||
const auto fracts = next_burst_start % ONE_TS_BURST_LEN;
|
const auto fracts = next_burst_start % ONE_TS_BURST_LEN;
|
||||||
to_skip = ONE_TS_BURST_LEN - fracts;
|
to_skip = ONE_TS_BURST_LEN - fracts;
|
||||||
|
|
||||||
for (unsigned int i = 0; i < fullts; i++)
|
for (int i = 0; i < fullts; i++)
|
||||||
timekeeper.inc_and_update(first_sch_ts_start + i * ONE_TS_BURST_LEN);
|
timekeeper.inc_and_update(first_sch_ts_start + i * ONE_TS_BURST_LEN);
|
||||||
|
|
||||||
if (fracts)
|
if (fracts)
|
||||||
@@ -277,7 +306,7 @@ void ms_trx::grab_bursts(dev_buf_t *rcd)
|
|||||||
if (partial_rdofs) {
|
if (partial_rdofs) {
|
||||||
auto first_remaining = ONE_TS_BURST_LEN - partial_rdofs;
|
auto first_remaining = ONE_TS_BURST_LEN - partial_rdofs;
|
||||||
auto rd = rcd->read_n(burst_copy_buffer + partial_rdofs, 0, first_remaining);
|
auto rd = rcd->read_n(burst_copy_buffer + partial_rdofs, 0, first_remaining);
|
||||||
if (rd != (int)first_remaining) {
|
if (rd != first_remaining) {
|
||||||
partial_rdofs += rd;
|
partial_rdofs += rd;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,78 +0,0 @@
|
|||||||
/*
|
|
||||||
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* Author: Eric Wild <ewild@sysmocom.de>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <atomic>
|
|
||||||
#include "ms_trxcon_if.h"
|
|
||||||
extern "C" {
|
|
||||||
#include <osmocom/bb/trxcon/trxcon.h>
|
|
||||||
#include <osmocom/bb/trxcon/l1ctl_server.h>
|
|
||||||
#include <osmocom/core/panic.h>
|
|
||||||
}
|
|
||||||
|
|
||||||
extern tx_queue_t txq;
|
|
||||||
extern cmd_queue_t cmdq_to_phy;
|
|
||||||
extern cmdr_queue_t cmdq_from_phy;
|
|
||||||
extern std::atomic<bool> g_exit_flag;
|
|
||||||
// trxcon C call(back) if
|
|
||||||
extern "C" {
|
|
||||||
int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon_phyif_burst_req *br)
|
|
||||||
{
|
|
||||||
if (br->burst_len == 0) // dummy/nope
|
|
||||||
return 0;
|
|
||||||
OSMO_ASSERT(br->burst != 0);
|
|
||||||
|
|
||||||
internal_q_tx_buf b(br);
|
|
||||||
if (!g_exit_flag)
|
|
||||||
txq.spsc_push(&b);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon_phyif_cmd *cmd)
|
|
||||||
{
|
|
||||||
#ifdef TXDEBUG
|
|
||||||
DBGLG() << "TOP C: " << cmd2str(cmd->type) << std::endl;
|
|
||||||
#endif
|
|
||||||
if (!g_exit_flag)
|
|
||||||
cmdq_to_phy.spsc_push(cmd);
|
|
||||||
// q for resp polling happens in main loop
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void trxcon_phyif_close(void *phyif)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
void trxcon_l1ctl_close(struct trxcon_inst *trxcon)
|
|
||||||
{
|
|
||||||
/* Avoid use-after-free: both *fi and *trxcon are children of
|
|
||||||
* the L2IF (L1CTL connection), so we need to re-parent *fi
|
|
||||||
* to NULL before calling l1ctl_client_conn_close(). */
|
|
||||||
talloc_steal(NULL, trxcon->fi);
|
|
||||||
l1ctl_client_conn_close((struct l1ctl_client *)trxcon->l2if);
|
|
||||||
}
|
|
||||||
|
|
||||||
int trxcon_l1ctl_send(struct trxcon_inst *trxcon, struct msgb *msg)
|
|
||||||
{
|
|
||||||
struct l1ctl_client *l1c = (struct l1ctl_client *)trxcon->l2if;
|
|
||||||
|
|
||||||
return l1ctl_client_send(l1c, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,43 +19,78 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <csignal>
|
|
||||||
#include "sigProcLib.h"
|
#include "sigProcLib.h"
|
||||||
#include "ms.h"
|
#include "ms.h"
|
||||||
#include <signalVector.h>
|
#include <signalVector.h>
|
||||||
#include <radioVector.h>
|
#include <radioVector.h>
|
||||||
#include <radioInterface.h>
|
#include <radioInterface.h>
|
||||||
#include <grgsm_vitac/grgsm_vitac.h>
|
#include "grgsm_vitac/grgsm_vitac.h"
|
||||||
|
|
||||||
// #define TXDEBUG
|
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#include <osmocom/core/select.h>
|
||||||
#include "sch.h"
|
#include "sch.h"
|
||||||
#include "convolve.h"
|
#include "convolve.h"
|
||||||
#include "convert.h"
|
#include "convert.h"
|
||||||
|
#include "proto_trxd.h"
|
||||||
|
|
||||||
#include <osmocom/core/application.h>
|
#include <stdio.h>
|
||||||
#include <osmocom/gsm/gsm_utils.h>
|
#include <stdlib.h>
|
||||||
|
#include <stdint.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <getopt.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <errno.h>
|
||||||
|
#include <time.h>
|
||||||
|
|
||||||
#include <osmocom/bb/trxcon/trxcon.h>
|
|
||||||
#include <osmocom/bb/trxcon/trxcon_fsm.h>
|
|
||||||
#include <osmocom/bb/trxcon/l1ctl_server.h>
|
|
||||||
|
|
||||||
extern void trxc_log_init(void *tallctx);
|
|
||||||
#ifdef LSANDEBUG
|
#ifdef LSANDEBUG
|
||||||
void __lsan_do_recoverable_leak_check();
|
void __lsan_do_recoverable_leak_check();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "ms_trxcon_if.h"
|
|
||||||
#include "ms_upper.h"
|
#include "ms_upper.h"
|
||||||
|
|
||||||
extern bool trxc_l1ctl_init(void *tallctx);
|
namespace trxcon
|
||||||
|
{
|
||||||
|
extern "C" {
|
||||||
|
#include <osmocom/core/fsm.h>
|
||||||
|
#include <osmocom/core/msgb.h>
|
||||||
|
#include <osmocom/core/talloc.h>
|
||||||
|
#include <osmocom/core/signal.h>
|
||||||
|
#include <osmocom/core/select.h>
|
||||||
|
#include <osmocom/core/gsmtap_util.h>
|
||||||
|
#include <osmocom/core/gsmtap.h>
|
||||||
|
|
||||||
|
// #include <osmocom/core/application.h>
|
||||||
|
#include <osmocom/core/logging.h>
|
||||||
|
#include <osmocom/bb/trxcon/logging.h>
|
||||||
|
|
||||||
|
#include <osmocom/bb/trxcon/trxcon.h>
|
||||||
|
#include <osmocom/bb/trxcon/trxcon_fsm.h>
|
||||||
|
#include <osmocom/bb/trxcon/phyif.h>
|
||||||
|
#include <osmocom/bb/trxcon/trx_if.h>
|
||||||
|
#include <osmocom/bb/trxcon/l1ctl_server.h>
|
||||||
|
|
||||||
|
#include <osmocom/bb/l1sched/l1sched.h>
|
||||||
|
// #include <osmocom/bb/l1sched/logging.h>
|
||||||
|
}
|
||||||
struct trxcon_inst *g_trxcon;
|
struct trxcon_inst *g_trxcon;
|
||||||
tx_queue_t txq;
|
// trx_instance *trxcon_instance; // local handle
|
||||||
cmd_queue_t cmdq_to_phy;
|
struct internal_q_tx_buf {
|
||||||
cmdr_queue_t cmdq_from_phy;
|
trxcon_phyif_burst_req r;
|
||||||
|
uint8_t buf[148];
|
||||||
|
};
|
||||||
|
using tx_queue_t = spsc_cond<8 * 1, internal_q_tx_buf, true, false>;
|
||||||
|
using cmd_queue_t = spsc_cond<8 * 1, trxcon_phyif_cmd, true, false>;
|
||||||
|
using cmdr_queue_t = spsc_cond<8 * 1, trxcon_phyif_rsp, false, false>;
|
||||||
|
static tx_queue_t txq;
|
||||||
|
static cmd_queue_t cmdq_to_phy;
|
||||||
|
static cmdr_queue_t cmdq_from_phy;
|
||||||
|
|
||||||
|
extern void trxc_log_init(void *tallctx);
|
||||||
|
extern void trxc_l1ctl_init(void *tallctx);
|
||||||
|
|
||||||
|
} // namespace trxcon
|
||||||
|
|
||||||
#ifdef LOG
|
#ifdef LOG
|
||||||
#undef LOG
|
#undef LOG
|
||||||
@@ -64,64 +99,41 @@ cmdr_queue_t cmdq_from_phy;
|
|||||||
|
|
||||||
#define DBGLG(...) upper_trx::dummy_log()
|
#define DBGLG(...) upper_trx::dummy_log()
|
||||||
|
|
||||||
std::atomic<bool> g_exit_flag;
|
|
||||||
|
|
||||||
void upper_trx::stop_upper_threads()
|
|
||||||
{
|
|
||||||
g_exit_flag = true;
|
|
||||||
|
|
||||||
pthread_join(thr_control, NULL);
|
|
||||||
pthread_join(thr_tx, NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
void upper_trx::start_threads()
|
void upper_trx::start_threads()
|
||||||
{
|
{
|
||||||
DBGLG(...) << "spawning threads.." << std::endl;
|
thr_control = std::thread([this] {
|
||||||
|
set_name_aff_sched("upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR));
|
||||||
thr_control = spawn_worker_thread(
|
while (1) {
|
||||||
sched_params::thread_names::U_CTL,
|
driveControl();
|
||||||
[](void *args) -> void * {
|
|
||||||
upper_trx *t = reinterpret_cast<upper_trx *>(args);
|
|
||||||
#ifdef TXDEBUG
|
|
||||||
struct sched_param param;
|
|
||||||
int policy;
|
|
||||||
pthread_getschedparam(pthread_self(), &policy, ¶m);
|
|
||||||
printf("ID: %lu, CPU: %d policy = %d priority = %d\n", pthread_self(), sched_getcpu(), policy,
|
|
||||||
param.sched_priority);
|
|
||||||
#endif
|
|
||||||
std::cerr << "started U control!" << std::endl;
|
|
||||||
while (!g_exit_flag) {
|
|
||||||
t->driveControl();
|
|
||||||
}
|
}
|
||||||
std::cerr << "exit U control!" << std::endl;
|
});
|
||||||
|
msleep(1);
|
||||||
return 0;
|
thr_tx = std::thread([this] {
|
||||||
},
|
set_name_aff_sched("upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1);
|
||||||
this);
|
while (1) {
|
||||||
thr_tx = spawn_worker_thread(
|
driveTx();
|
||||||
sched_params::thread_names::U_TX,
|
|
||||||
[](void *args) -> void * {
|
|
||||||
upper_trx *t = reinterpret_cast<upper_trx *>(args);
|
|
||||||
#ifdef TXDEBUG
|
|
||||||
struct sched_param param;
|
|
||||||
int policy;
|
|
||||||
pthread_getschedparam(pthread_self(), &policy, ¶m);
|
|
||||||
printf("ID: %lu, CPU: %d policy = %d priority = %d\n", pthread_self(), sched_getcpu(), policy,
|
|
||||||
param.sched_priority);
|
|
||||||
#endif
|
|
||||||
std::cerr << "started U tx!" << std::endl;
|
|
||||||
while (!g_exit_flag) {
|
|
||||||
t->driveTx();
|
|
||||||
}
|
}
|
||||||
std::cerr << "exit U tx!" << std::endl;
|
});
|
||||||
|
|
||||||
return 0;
|
// atomic ensures data is not written to q until loop reads
|
||||||
},
|
start_lower_ms();
|
||||||
this);
|
|
||||||
|
set_name_aff_sched("upper_rx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_RR) - 5);
|
||||||
|
while (1) {
|
||||||
|
// set_upper_ready(true);
|
||||||
|
driveReceiveFIFO();
|
||||||
|
osmo_select_main(1);
|
||||||
|
|
||||||
|
trxcon::trxcon_phyif_rsp r;
|
||||||
|
if (trxcon::cmdq_from_phy.spsc_pop(&r)) {
|
||||||
|
DBGLG() << "HAVE RESP:" << r.type << std::endl;
|
||||||
|
trxcon_phyif_handle_rsp(trxcon::g_trxcon, &r);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef LSANDEBUG
|
#ifdef LSANDEBUG
|
||||||
std::thread([this] {
|
std::thread([this] {
|
||||||
set_name_aff_sched(sched_params::thread_names::LEAKCHECK);
|
set_name_aff_sched("leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
std::this_thread::sleep_for(std::chrono::seconds{ 5 });
|
std::this_thread::sleep_for(std::chrono::seconds{ 5 });
|
||||||
@@ -131,43 +143,19 @@ void upper_trx::start_threads()
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void upper_trx::main_loop()
|
void upper_trx::start_lower_ms()
|
||||||
{
|
{
|
||||||
set_name_aff_sched(sched_params::thread_names::U_RX);
|
ms_trx::start();
|
||||||
set_upper_ready(true);
|
|
||||||
while (!g_exit_flag) {
|
|
||||||
driveReceiveFIFO();
|
|
||||||
osmo_select_main(1);
|
|
||||||
|
|
||||||
trxcon_phyif_rsp r;
|
|
||||||
if (cmdq_from_phy.spsc_pop(&r)) {
|
|
||||||
DBGLG() << "HAVE RESP:" << r.type << std::endl;
|
|
||||||
trxcon_phyif_handle_rsp(g_trxcon, &r);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
set_upper_ready(false);
|
|
||||||
std::cerr << "exit U rx!" << std::endl;
|
|
||||||
mOn = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// signalvector is owning despite claiming not to, but we can pretend, too..
|
|
||||||
static void static_free(void *wData){};
|
|
||||||
static void *static_alloc(size_t newSize)
|
|
||||||
{
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
|
bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
|
||||||
{
|
{
|
||||||
float pow, avg = 1.0;
|
float pow, avg = 1.0;
|
||||||
const auto zero_pad_len = 40; // give the VA some runway for misaligned bursts
|
static complex workbuf[40 + 625 + 40];
|
||||||
const auto workbuf_size = zero_pad_len + ONE_TS_BURST_LEN + zero_pad_len;
|
static signalVector sv(workbuf, 40, 625);
|
||||||
static complex workbuf[workbuf_size];
|
|
||||||
|
|
||||||
static signalVector sv(workbuf, zero_pad_len, ONE_TS_BURST_LEN, static_alloc, static_free);
|
|
||||||
one_burst e;
|
one_burst e;
|
||||||
auto ss = reinterpret_cast<std::complex<float> *>(&workbuf[zero_pad_len]);
|
auto ss = reinterpret_cast<std::complex<float> *>(&workbuf[40]);
|
||||||
std::fill(workbuf, workbuf + workbuf_size, 0);
|
memset((void *)&workbuf[0], 0, sizeof(workbuf));
|
||||||
// assert(sv.begin() == &workbuf[40]);
|
// assert(sv.begin() == &workbuf[40]);
|
||||||
|
|
||||||
while (!rxqueue.spsc_pop(&e)) {
|
while (!rxqueue.spsc_pop(&e)) {
|
||||||
@@ -179,9 +167,9 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
|
|||||||
const auto is_sch = gsm_sch_check_ts(wTime.TN(), wTime.FN());
|
const auto is_sch = gsm_sch_check_ts(wTime.TN(), wTime.FN());
|
||||||
const auto is_fcch = gsm_fcch_check_ts(wTime.TN(), wTime.FN());
|
const auto is_fcch = gsm_fcch_check_ts(wTime.TN(), wTime.FN());
|
||||||
|
|
||||||
trxcon_phyif_rtr_ind i = { static_cast<uint32_t>(wTime.FN()), static_cast<uint8_t>(wTime.TN()) };
|
trxcon::trxcon_phyif_rtr_ind i = { static_cast<uint32_t>(wTime.FN()), static_cast<uint8_t>(wTime.TN()) };
|
||||||
trxcon_phyif_rtr_rsp r = {};
|
trxcon::trxcon_phyif_rtr_rsp r = {};
|
||||||
trxcon_phyif_handle_rtr_ind(g_trxcon, &i, &r);
|
trxcon_phyif_handle_rtr_ind(trxcon::g_trxcon, &i, &r);
|
||||||
if (!(r.flags & TRXCON_PHYIF_RTR_F_ACTIVE))
|
if (!(r.flags & TRXCON_PHYIF_RTR_F_ACTIVE))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -198,7 +186,7 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
convert_and_scale(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
|
convert_and_scale<float, int16_t>(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
|
||||||
|
|
||||||
pow = energyDetect(sv, 20 * 4 /*sps*/);
|
pow = energyDetect(sv, 20 * 4 /*sps*/);
|
||||||
if (pow < -1) {
|
if (pow < -1) {
|
||||||
@@ -225,14 +213,13 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
|
|||||||
// fprintf(stderr, "%s %d\n", (is_nb ? "N":"D"), burst_time.FN());
|
// fprintf(stderr, "%s %d\n", (is_nb ? "N":"D"), burst_time.FN());
|
||||||
// if (is_nb)
|
// if (is_nb)
|
||||||
#endif
|
#endif
|
||||||
detect_burst_nb(ss, &chan_imp_resp[0], normal_burst_start, demodded_softbits);
|
detect_burst(ss, &chan_imp_resp[0], normal_burst_start, demodded_softbits);
|
||||||
#ifdef DBGXX
|
#ifdef DBGXX
|
||||||
// else
|
// else
|
||||||
// detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
|
// detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
RSSI = (int)floor(20.0 * log10(rxFullScale / avg));
|
RSSI = (int)floor(20.0 * log10(rxFullScale / avg));
|
||||||
// FIXME: properly handle offset, sch/nb alignment diff? handled by lower anyway...
|
|
||||||
timingOffset = (int)round(0);
|
timingOffset = (int)round(0);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -248,39 +235,34 @@ void upper_trx::driveReceiveFIFO()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (pullRadioVector(burstTime, RSSI, TOA)) {
|
if (pullRadioVector(burstTime, RSSI, TOA)) {
|
||||||
trxcon_phyif_burst_ind bi;
|
// trxcon::trx_data_rx_handler(trxcon::trxcon_instance, (uint8_t *)&response);
|
||||||
|
trxcon::trxcon_phyif_burst_ind bi;
|
||||||
bi.fn = burstTime.FN();
|
bi.fn = burstTime.FN();
|
||||||
bi.tn = burstTime.TN();
|
bi.tn = burstTime.TN();
|
||||||
bi.rssi = RSSI;
|
bi.rssi = RSSI;
|
||||||
bi.toa256 = TOA;
|
bi.toa256 = TOA;
|
||||||
bi.burst = (sbit_t *)demodded_softbits;
|
bi.burst = (sbit_t *)demodded_softbits;
|
||||||
bi.burst_len = sizeof(demodded_softbits);
|
bi.burst_len = sizeof(demodded_softbits);
|
||||||
trxcon_phyif_handle_burst_ind(g_trxcon, &bi);
|
// trxcon_phyif_handle_clock_ind(trxcon::g_trxcon, bi.fn);
|
||||||
|
trxcon_phyif_handle_burst_ind(trxcon::g_trxcon, &bi);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct trxcon_phyif_rts_ind rts {
|
struct trxcon::trxcon_phyif_rts_ind rts {
|
||||||
static_cast<uint32_t>(burstTime.FN()), static_cast<uint8_t>(burstTime.TN())
|
static_cast<uint32_t>(burstTime.FN()), static_cast<uint8_t>(burstTime.TN())
|
||||||
};
|
};
|
||||||
trxcon_phyif_handle_rts_ind(g_trxcon, &rts);
|
trxcon_phyif_handle_rts_ind(trxcon::g_trxcon, &rts);
|
||||||
}
|
}
|
||||||
|
|
||||||
void upper_trx::driveTx()
|
void upper_trx::driveTx()
|
||||||
{
|
{
|
||||||
internal_q_tx_buf e;
|
trxcon::internal_q_tx_buf e;
|
||||||
static BitVector newBurst(sizeof(e.buf));
|
while (!trxcon::txq.spsc_pop(&e)) {
|
||||||
while (!txq.spsc_pop(&e)) {
|
trxcon::txq.spsc_prep_pop();
|
||||||
txq.spsc_prep_pop();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure our tx cb is tickled and can exit
|
trxcon::internal_q_tx_buf *burst = &e;
|
||||||
if (g_exit_flag) {
|
|
||||||
submit_burst_ts(0, 1337, 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal_q_tx_buf *burst = &e;
|
#ifdef TXDEBUG
|
||||||
|
|
||||||
#ifdef TXDEBUG2
|
|
||||||
DBGLG() << "got burst!" << burst->r.fn << ":" << burst->ts << " current: " << timekeeper.gsmtime().FN()
|
DBGLG() << "got burst!" << burst->r.fn << ":" << burst->ts << " current: " << timekeeper.gsmtime().FN()
|
||||||
<< " dff: " << (int64_t)((int64_t)timekeeper.gsmtime().FN() - (int64_t)burst->r.fn) << std::endl;
|
<< " dff: " << (int64_t)((int64_t)timekeeper.gsmtime().FN() - (int64_t)burst->r.fn) << std::endl;
|
||||||
#endif
|
#endif
|
||||||
@@ -288,6 +270,7 @@ void upper_trx::driveTx()
|
|||||||
auto currTime = GSM::Time(burst->r.fn, burst->r.tn);
|
auto currTime = GSM::Time(burst->r.fn, burst->r.tn);
|
||||||
int RSSI = (int)burst->r.pwr;
|
int RSSI = (int)burst->r.pwr;
|
||||||
|
|
||||||
|
static BitVector newBurst(gSlotLen);
|
||||||
BitVector::iterator itr = newBurst.begin();
|
BitVector::iterator itr = newBurst.begin();
|
||||||
auto *bufferItr = burst->buf;
|
auto *bufferItr = burst->buf;
|
||||||
while (itr < newBurst.end())
|
while (itr < newBurst.end())
|
||||||
@@ -298,10 +281,10 @@ void upper_trx::driveTx()
|
|||||||
|
|
||||||
// float -> int16
|
// float -> int16
|
||||||
blade_sample_type burst_buf[txburst->size()];
|
blade_sample_type burst_buf[txburst->size()];
|
||||||
convert_and_scale(burst_buf, txburst->begin(), txburst->size() * 2, 1);
|
convert_and_scale<int16_t, float>(burst_buf, txburst->begin(), txburst->size() * 2, 1);
|
||||||
#ifdef TXDEBUG2
|
#ifdef TXDEBUG
|
||||||
auto check = signalVector(txburst->size(), 40);
|
auto check = signalVector(txburst->size(), 40);
|
||||||
convert_and_scale(check.begin(), burst_buf, txburst->size() * 2, 1);
|
convert_and_scale<float, int16_t, 1>(check.begin(), burst_buf, txburst->size() * 2);
|
||||||
estim_burst_params ebp;
|
estim_burst_params ebp;
|
||||||
auto d = detectAnyBurst(check, 2, 4, 4, CorrType::RACH, 40, &ebp);
|
auto d = detectAnyBurst(check, 2, 4, 4, CorrType::RACH, 40, &ebp);
|
||||||
if (d)
|
if (d)
|
||||||
@@ -315,133 +298,139 @@ void upper_trx::driveTx()
|
|||||||
delete txburst;
|
delete txburst;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef TXDEBUG
|
static const char *cmd2str(trxcon::trxcon_phyif_cmd_type c)
|
||||||
static const char *cmd2str(trxcon_phyif_cmd_type c)
|
|
||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case TRXCON_PHYIF_CMDT_RESET:
|
case trxcon::TRXCON_PHYIF_CMDT_RESET:
|
||||||
return "TRXCON_PHYIF_CMDT_RESET";
|
return "TRXCON_PHYIF_CMDT_RESET";
|
||||||
case TRXCON_PHYIF_CMDT_POWERON:
|
case trxcon::TRXCON_PHYIF_CMDT_POWERON:
|
||||||
return "TRXCON_PHYIF_CMDT_POWERON";
|
return "TRXCON_PHYIF_CMDT_POWERON";
|
||||||
case TRXCON_PHYIF_CMDT_POWEROFF:
|
case trxcon::TRXCON_PHYIF_CMDT_POWEROFF:
|
||||||
return "TRXCON_PHYIF_CMDT_POWEROFF";
|
return "TRXCON_PHYIF_CMDT_POWEROFF";
|
||||||
case TRXCON_PHYIF_CMDT_MEASURE:
|
case trxcon::TRXCON_PHYIF_CMDT_MEASURE:
|
||||||
return "TRXCON_PHYIF_CMDT_MEASURE";
|
return "TRXCON_PHYIF_CMDT_MEASURE";
|
||||||
case TRXCON_PHYIF_CMDT_SETFREQ_H0:
|
case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H0:
|
||||||
return "TRXCON_PHYIF_CMDT_SETFREQ_H0";
|
return "TRXCON_PHYIF_CMDT_SETFREQ_H0";
|
||||||
case TRXCON_PHYIF_CMDT_SETFREQ_H1:
|
case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H1:
|
||||||
return "TRXCON_PHYIF_CMDT_SETFREQ_H1";
|
return "TRXCON_PHYIF_CMDT_SETFREQ_H1";
|
||||||
case TRXCON_PHYIF_CMDT_SETSLOT:
|
case trxcon::TRXCON_PHYIF_CMDT_SETSLOT:
|
||||||
return "TRXCON_PHYIF_CMDT_SETSLOT";
|
return "TRXCON_PHYIF_CMDT_SETSLOT";
|
||||||
case TRXCON_PHYIF_CMDT_SETTA:
|
case trxcon::TRXCON_PHYIF_CMDT_SETTA:
|
||||||
return "TRXCON_PHYIF_CMDT_SETTA";
|
return "TRXCON_PHYIF_CMDT_SETTA";
|
||||||
default:
|
default:
|
||||||
return "UNKNOWN COMMAND!";
|
return "UNKNOWN COMMAND!";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print_cmd(trxcon_phyif_cmd_type c)
|
static void print_cmd(trxcon::trxcon_phyif_cmd_type c)
|
||||||
{
|
{
|
||||||
DBGLG() << "handling " << cmd2str(c) << std::endl;
|
DBGLG() << cmd2str(c) << std::endl;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
bool upper_trx::driveControl()
|
bool upper_trx::driveControl()
|
||||||
{
|
{
|
||||||
trxcon_phyif_rsp r;
|
trxcon::trxcon_phyif_rsp r;
|
||||||
trxcon_phyif_cmd cmd;
|
trxcon::trxcon_phyif_cmd cmd;
|
||||||
while (!cmdq_to_phy.spsc_pop(&cmd)) {
|
while (!trxcon::cmdq_to_phy.spsc_pop(&cmd)) {
|
||||||
cmdq_to_phy.spsc_prep_pop();
|
trxcon::cmdq_to_phy.spsc_prep_pop();
|
||||||
if (g_exit_flag)
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (g_exit_flag)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
#ifdef TXDEBUG
|
|
||||||
print_cmd(cmd.type);
|
print_cmd(cmd.type);
|
||||||
#endif
|
|
||||||
|
|
||||||
switch (cmd.type) {
|
switch (cmd.type) {
|
||||||
case TRXCON_PHYIF_CMDT_RESET:
|
case trxcon::TRXCON_PHYIF_CMDT_RESET:
|
||||||
set_ta(0);
|
|
||||||
break;
|
break;
|
||||||
case TRXCON_PHYIF_CMDT_POWERON:
|
case trxcon::TRXCON_PHYIF_CMDT_POWERON:
|
||||||
|
|
||||||
if (!mOn) {
|
if (!mOn) {
|
||||||
|
// start_ms();
|
||||||
|
set_upper_ready(true);
|
||||||
mOn = true;
|
mOn = true;
|
||||||
start_lower_ms();
|
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case TRXCON_PHYIF_CMDT_POWEROFF:
|
case trxcon::TRXCON_PHYIF_CMDT_POWEROFF:
|
||||||
|
// set_upper_ready(false);
|
||||||
|
set_ta(0);
|
||||||
break;
|
break;
|
||||||
case TRXCON_PHYIF_CMDT_MEASURE:
|
case trxcon::TRXCON_PHYIF_CMDT_MEASURE:
|
||||||
r.type = trxcon_phyif_cmd_type::TRXCON_PHYIF_CMDT_MEASURE;
|
r.type = trxcon::trxcon_phyif_cmd_type::TRXCON_PHYIF_CMDT_MEASURE;
|
||||||
r.param.measure.band_arfcn = cmd.param.measure.band_arfcn;
|
r.param.measure.band_arfcn = cmd.param.measure.band_arfcn;
|
||||||
// FIXME: do we want to measure anything, considering the transceiver just syncs by.. syncing?
|
|
||||||
r.param.measure.dbm = -80;
|
r.param.measure.dbm = -80;
|
||||||
tuneRx(gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 0) * 1000 * 100);
|
tuneRx(trxcon::gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 0) * 1000 * 100);
|
||||||
tuneTx(gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 1) * 1000 * 100);
|
tuneTx(trxcon::gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 1) * 1000 * 100);
|
||||||
cmdq_from_phy.spsc_push(&r);
|
trxcon::cmdq_from_phy.spsc_push(&r);
|
||||||
break;
|
break;
|
||||||
case TRXCON_PHYIF_CMDT_SETFREQ_H0:
|
case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H0:
|
||||||
tuneRx(gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 0) * 1000 * 100);
|
// gsm_arfcn2band_rc(uint16_t arfcn, enum gsm_band *band)
|
||||||
tuneTx(gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 1) * 1000 * 100);
|
tuneRx(trxcon::gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 0) * 1000 * 100);
|
||||||
|
tuneTx(trxcon::gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 1) * 1000 * 100);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case TRXCON_PHYIF_CMDT_SETFREQ_H1:
|
case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H1:
|
||||||
break;
|
break;
|
||||||
case TRXCON_PHYIF_CMDT_SETSLOT:
|
case trxcon::TRXCON_PHYIF_CMDT_SETSLOT:
|
||||||
break;
|
break;
|
||||||
case TRXCON_PHYIF_CMDT_SETTA:
|
case trxcon::TRXCON_PHYIF_CMDT_SETTA:
|
||||||
set_ta(cmd.param.setta.ta);
|
set_ta(cmd.param.setta.ta);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void sighandler(int sigset)
|
// trxcon C call(back) if
|
||||||
|
extern "C" {
|
||||||
|
int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon::trxcon_phyif_burst_req *br)
|
||||||
{
|
{
|
||||||
// we might get a sigpipe in case the l1ctl ud socket disconnects because mobile quits
|
if (br->burst_len == 0) // dummy/nope
|
||||||
if (sigset == SIGPIPE || sigset == SIGINT) {
|
return 0;
|
||||||
g_exit_flag = true;
|
assert(br->burst != 0);
|
||||||
|
|
||||||
// we know the flag is atomic and it prevents the trxcon cb handlers from writing
|
trxcon::internal_q_tx_buf b;
|
||||||
// to the queues, so submit some trash to unblock the threads & exit
|
b.r = *br;
|
||||||
trxcon_phyif_cmd cmd = {};
|
memcpy(b.buf, (void *)br->burst, br->burst_len);
|
||||||
internal_q_tx_buf b = {};
|
trxcon::txq.spsc_push(&b);
|
||||||
txq.spsc_push(&b);
|
return 0;
|
||||||
cmdq_to_phy.spsc_push(&cmd);
|
}
|
||||||
msleep(200);
|
|
||||||
|
|
||||||
return;
|
int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon::trxcon_phyif_cmd *cmd)
|
||||||
}
|
{
|
||||||
|
DBGLG() << "TOP C: " << cmd2str(cmd->type) << std::endl;
|
||||||
|
trxcon::cmdq_to_phy.spsc_push(cmd);
|
||||||
|
// q for resp polling happens in main loop
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void trxcon_phyif_close(void *phyif)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void trxcon_l1ctl_close(struct trxcon::trxcon_inst *trxcon)
|
||||||
|
{
|
||||||
|
/* Avoid use-after-free: both *fi and *trxcon are children of
|
||||||
|
* the L2IF (L1CTL connection), so we need to re-parent *fi
|
||||||
|
* to NULL before calling l1ctl_client_conn_close(). */
|
||||||
|
talloc_steal(NULL, trxcon->fi);
|
||||||
|
trxcon::l1ctl_client_conn_close((struct trxcon::l1ctl_client *)trxcon->l2if);
|
||||||
|
}
|
||||||
|
|
||||||
|
int trxcon_l1ctl_send(struct trxcon::trxcon_inst *trxcon, struct trxcon::msgb *msg)
|
||||||
|
{
|
||||||
|
struct trxcon::l1ctl_client *l1c = (struct trxcon::l1ctl_client *)trxcon->l2if;
|
||||||
|
|
||||||
|
return trxcon::l1ctl_client_send(l1c, msg);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
auto tall_trxcon_ctx = talloc_init("trxcon context");
|
auto tall_trxcon_ctx = talloc_init("trxcon context");
|
||||||
signal(SIGPIPE, sighandler);
|
trxcon::msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
|
||||||
signal(SIGINT, sighandler);
|
trxcon::trxc_log_init(tall_trxcon_ctx);
|
||||||
|
|
||||||
msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
|
trxcon::g_trxcon = trxcon::trxcon_inst_alloc(tall_trxcon_ctx, 0, 3);
|
||||||
trxc_log_init(tall_trxcon_ctx);
|
trxcon::g_trxcon->gsmtap = 0;
|
||||||
|
trxcon::g_trxcon->phyif = (void *)0x1234;
|
||||||
/* Configure pretty logging */
|
|
||||||
log_set_print_extended_timestamp(osmo_stderr_target, 1);
|
|
||||||
log_set_print_category_hex(osmo_stderr_target, 0);
|
|
||||||
log_set_print_category(osmo_stderr_target, 1);
|
|
||||||
log_set_print_level(osmo_stderr_target, 1);
|
|
||||||
|
|
||||||
log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
|
|
||||||
log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
|
|
||||||
|
|
||||||
osmo_fsm_log_timeouts(true);
|
|
||||||
|
|
||||||
g_trxcon = trxcon_inst_alloc(tall_trxcon_ctx, 0);
|
|
||||||
g_trxcon->gsmtap = nullptr;
|
|
||||||
g_trxcon->phyif = nullptr;
|
|
||||||
g_trxcon->phy_quirks.fbsb_extend_fns = 866; // 4 seconds, known to work.
|
|
||||||
|
|
||||||
|
pthread_setname_np(pthread_self(), "main_trxc");
|
||||||
convolve_init();
|
convolve_init();
|
||||||
convert_init();
|
convert_init();
|
||||||
sigProcLibSetup();
|
sigProcLibSetup();
|
||||||
@@ -452,22 +441,11 @@ int main(int argc, char *argv[])
|
|||||||
trx->do_auto_gain = true;
|
trx->do_auto_gain = true;
|
||||||
|
|
||||||
status = trx->init_dev_and_streams();
|
status = trx->init_dev_and_streams();
|
||||||
if (status < 0) {
|
trx->set_name_aff_sched("main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5);
|
||||||
std::cerr << "Error initializing hardware, quitting.." << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
trx->set_name_aff_sched(sched_params::thread_names::MAIN);
|
|
||||||
|
|
||||||
if (!trxc_l1ctl_init(tall_trxcon_ctx)) {
|
trxcon::trxc_l1ctl_init(tall_trxcon_ctx);
|
||||||
std::cerr << "Error initializing l1ctl, quitting.." << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// blocking, will return when global exit is requested
|
|
||||||
trx->start_threads();
|
trx->start_threads();
|
||||||
trx->main_loop();
|
|
||||||
trx->stop_threads();
|
|
||||||
trx->stop_upper_threads();
|
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,6 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
@@ -24,25 +26,32 @@
|
|||||||
#include <arpa/inet.h>
|
#include <arpa/inet.h>
|
||||||
|
|
||||||
#include "GSMCommon.h"
|
#include "GSMCommon.h"
|
||||||
|
#include "radioClock.h"
|
||||||
#include "ms.h"
|
#include "ms.h"
|
||||||
|
|
||||||
|
namespace trxcon
|
||||||
|
{
|
||||||
|
extern "C" {
|
||||||
|
#include <osmocom/bb/trxcon/phyif.h>
|
||||||
|
#include <osmocom/bb/trxcon/trx_if.h>
|
||||||
|
}
|
||||||
|
} // namespace trxcon
|
||||||
class upper_trx : public ms_trx {
|
class upper_trx : public ms_trx {
|
||||||
volatile bool mOn;
|
bool mOn;
|
||||||
char demodded_softbits[444];
|
char demodded_softbits[444];
|
||||||
|
|
||||||
// void driveControl();
|
// void driveControl();
|
||||||
bool pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
|
|
||||||
|
|
||||||
pthread_t thr_control, thr_tx;
|
|
||||||
|
|
||||||
public:
|
|
||||||
void start_threads();
|
|
||||||
void main_loop();
|
|
||||||
void stop_upper_threads();
|
|
||||||
|
|
||||||
bool driveControl();
|
bool driveControl();
|
||||||
void driveReceiveFIFO();
|
void driveReceiveFIFO();
|
||||||
void driveTx();
|
void driveTx();
|
||||||
|
|
||||||
upper_trx() : mOn(false){};
|
bool pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset) __attribute__((optnone));
|
||||||
|
|
||||||
|
std::thread thr_control, thr_rx, thr_tx;
|
||||||
|
|
||||||
|
public:
|
||||||
|
void start_threads();
|
||||||
|
void start_lower_ms();
|
||||||
|
|
||||||
|
upper_trx(){};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -34,9 +34,6 @@
|
|||||||
|
|
||||||
#include "sch.h"
|
#include "sch.h"
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
|
||||||
#pragma GCC diagnostic ignored "-Wunused-variable"
|
|
||||||
|
|
||||||
/* GSM 04.08, 9.1.30 Synchronization channel information */
|
/* GSM 04.08, 9.1.30 Synchronization channel information */
|
||||||
struct sch_packed_info {
|
struct sch_packed_info {
|
||||||
ubit_t t1_hi[2];
|
ubit_t t1_hi[2];
|
||||||
@@ -325,5 +322,3 @@ static __attribute__((constructor)) void init()
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#pragma GCC diagnostic pop
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@
|
|||||||
* (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
|
* (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
|
||||||
* (C) 2016 by Tom Tsou <tom.tsou@ettus.com>
|
* (C) 2016 by Tom Tsou <tom.tsou@ettus.com>
|
||||||
* (C) 2017 by Harald Welte <laforge@gnumonks.org>
|
* (C) 2017 by Harald Welte <laforge@gnumonks.org>
|
||||||
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> / Eric Wild <ewild@sysmocom.de>
|
* (C) 2022 by 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> / Eric Wild <ewild@sysmocom.de>
|
||||||
*
|
*
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
*
|
*
|
||||||
|
|||||||
@@ -1,92 +0,0 @@
|
|||||||
#pragma once
|
|
||||||
/*
|
|
||||||
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
|
||||||
* All Rights Reserved
|
|
||||||
*
|
|
||||||
* Author: Eric Wild <ewild@sysmocom.de>
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation; either version 3 of the License, or
|
|
||||||
* (at your option) any later version.
|
|
||||||
*
|
|
||||||
* This program is distributed in the hope that it will be useful,
|
|
||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
* GNU Affero General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include <functional>
|
|
||||||
#include <thread>
|
|
||||||
#include <atomic>
|
|
||||||
#include <vector>
|
|
||||||
#include <future>
|
|
||||||
#include <mutex>
|
|
||||||
#include <queue>
|
|
||||||
|
|
||||||
struct single_thread_pool {
|
|
||||||
std::mutex m;
|
|
||||||
std::condition_variable cv;
|
|
||||||
std::atomic<bool> stop_flag;
|
|
||||||
std::atomic<bool> is_ready;
|
|
||||||
std::deque<std::function<void()>> wq;
|
|
||||||
std::thread worker_thread;
|
|
||||||
|
|
||||||
template <class F>
|
|
||||||
void add_task(F &&f)
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m);
|
|
||||||
wq.emplace_back(std::forward<F>(f));
|
|
||||||
cv.notify_one();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
single_thread_pool() : stop_flag(false), is_ready(false), worker_thread(std::thread([this] { thread_loop(); }))
|
|
||||||
{
|
|
||||||
}
|
|
||||||
~single_thread_pool()
|
|
||||||
{
|
|
||||||
stop();
|
|
||||||
}
|
|
||||||
|
|
||||||
std::thread::native_handle_type get_handle()
|
|
||||||
{
|
|
||||||
return worker_thread.native_handle();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
void stop()
|
|
||||||
{
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m);
|
|
||||||
wq.clear();
|
|
||||||
stop_flag = true;
|
|
||||||
cv.notify_one();
|
|
||||||
}
|
|
||||||
worker_thread.join();
|
|
||||||
}
|
|
||||||
|
|
||||||
void thread_loop()
|
|
||||||
{
|
|
||||||
while (true) {
|
|
||||||
is_ready = true;
|
|
||||||
std::function<void()> f;
|
|
||||||
{
|
|
||||||
std::unique_lock<std::mutex> l(m);
|
|
||||||
if (wq.empty()) {
|
|
||||||
cv.wait(l, [&] { return !wq.empty() || stop_flag; });
|
|
||||||
}
|
|
||||||
if (stop_flag)
|
|
||||||
return;
|
|
||||||
is_ready = false;
|
|
||||||
f = std::move(wq.front());
|
|
||||||
wq.pop_front();
|
|
||||||
}
|
|
||||||
f();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
* All Rights Reserved
|
* All Rights Reserved
|
||||||
@@ -65,8 +66,7 @@ struct uhd_buf_wrap {
|
|||||||
using dev_buf_t = uhd_buf_wrap;
|
using dev_buf_t = uhd_buf_wrap;
|
||||||
using bh_fn_t = std::function<int(dev_buf_t *)>;
|
using bh_fn_t = std::function<int(dev_buf_t *)>;
|
||||||
|
|
||||||
template <typename T>
|
template <typename T> struct uhd_hw {
|
||||||
struct uhd_hw {
|
|
||||||
uhd::usrp::multi_usrp::sptr dev;
|
uhd::usrp::multi_usrp::sptr dev;
|
||||||
uhd::rx_streamer::sptr rx_stream;
|
uhd::rx_streamer::sptr rx_stream;
|
||||||
uhd::tx_streamer::sptr tx_stream;
|
uhd::tx_streamer::sptr tx_stream;
|
||||||
@@ -77,38 +77,32 @@ struct uhd_hw {
|
|||||||
const unsigned int rxFullScale, txFullScale;
|
const unsigned int rxFullScale, txFullScale;
|
||||||
const int rxtxdelay;
|
const int rxtxdelay;
|
||||||
float rxgain, txgain;
|
float rxgain, txgain;
|
||||||
static std::atomic<bool> stop_lower_threads_flag;
|
volatile bool stop_me_flag;
|
||||||
double rxfreq_cache, txfreq_cache;
|
|
||||||
|
|
||||||
virtual ~uhd_hw()
|
virtual ~uhd_hw()
|
||||||
{
|
{
|
||||||
delete[] one_pkt_buf;
|
delete[] one_pkt_buf;
|
||||||
}
|
}
|
||||||
uhd_hw() : rxFullScale(32767), txFullScale(32767 * 0.3), rxtxdelay(-67), rxfreq_cache(0), txfreq_cache(0)
|
uhd_hw() : rxFullScale(32767), txFullScale(32767), rxtxdelay(-67), stop_me_flag(false)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
void close_device()
|
void close_device()
|
||||||
{
|
{
|
||||||
|
stop_me_flag = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tuneTx(double freq, size_t chan = 0)
|
bool tuneTx(double freq, size_t chan = 0)
|
||||||
{
|
{
|
||||||
if (txfreq_cache == freq)
|
|
||||||
return true;
|
|
||||||
msleep(25);
|
msleep(25);
|
||||||
dev->set_tx_freq(freq, chan);
|
dev->set_tx_freq(freq, chan);
|
||||||
txfreq_cache = freq;
|
|
||||||
msleep(25);
|
msleep(25);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
bool tuneRx(double freq, size_t chan = 0)
|
bool tuneRx(double freq, size_t chan = 0)
|
||||||
{
|
{
|
||||||
if (rxfreq_cache == freq)
|
|
||||||
return true;
|
|
||||||
msleep(25);
|
msleep(25);
|
||||||
dev->set_rx_freq(freq, chan);
|
dev->set_rx_freq(freq, chan);
|
||||||
rxfreq_cache = freq;
|
|
||||||
msleep(25);
|
msleep(25);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
@@ -141,7 +135,6 @@ struct uhd_hw {
|
|||||||
int init_device(bh_fn_t rxh, bh_fn_t txh)
|
int init_device(bh_fn_t rxh, bh_fn_t txh)
|
||||||
{
|
{
|
||||||
auto const lock_delay_ms = 500;
|
auto const lock_delay_ms = 500;
|
||||||
auto clock_lock_attempts = 15; // x lock_delay_ms
|
|
||||||
auto const mcr = 26e6;
|
auto const mcr = 26e6;
|
||||||
auto const rate = (1625e3 / 6) * 4;
|
auto const rate = (1625e3 / 6) * 4;
|
||||||
auto const ref = "external";
|
auto const ref = "external";
|
||||||
@@ -149,8 +142,7 @@ struct uhd_hw {
|
|||||||
auto const freq = 931.4e6; // 936.8e6
|
auto const freq = 931.4e6; // 936.8e6
|
||||||
auto bw = 0.5e6;
|
auto bw = 0.5e6;
|
||||||
auto const channel = 0;
|
auto const channel = 0;
|
||||||
// aligned to blade: 1020 samples per transfer
|
std::string args = {};
|
||||||
std::string args = { "recv_frame_size=4092,send_frame_size=4092" };
|
|
||||||
|
|
||||||
dev = uhd::usrp::multi_usrp::make(args);
|
dev = uhd::usrp::multi_usrp::make(args);
|
||||||
std::cout << "Using Device: " << dev->get_pp_string() << std::endl;
|
std::cout << "Using Device: " << dev->get_pp_string() << std::endl;
|
||||||
@@ -166,18 +158,8 @@ struct uhd_hw {
|
|||||||
dev->set_tx_bandwidth(bw, channel);
|
dev->set_tx_bandwidth(bw, channel);
|
||||||
|
|
||||||
while (!(dev->get_rx_sensor("lo_locked", channel).to_bool() &&
|
while (!(dev->get_rx_sensor("lo_locked", channel).to_bool() &&
|
||||||
dev->get_mboard_sensor("ref_locked").to_bool()) &&
|
dev->get_mboard_sensor("ref_locked").to_bool()))
|
||||||
clock_lock_attempts > 0) {
|
|
||||||
std::cerr << "clock source lock attempts remaining: " << clock_lock_attempts << ".."
|
|
||||||
<< std::endl;
|
|
||||||
std::this_thread::sleep_for(std::chrono::milliseconds(lock_delay_ms));
|
std::this_thread::sleep_for(std::chrono::milliseconds(lock_delay_ms));
|
||||||
clock_lock_attempts--;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (clock_lock_attempts <= 0) {
|
|
||||||
std::cerr << "Error locking clock, gpsdo missing? quitting.." << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
uhd::stream_args_t stream_args("sc16", "sc16");
|
uhd::stream_args_t stream_args("sc16", "sc16");
|
||||||
rx_stream = dev->get_rx_stream(stream_args);
|
rx_stream = dev->get_rx_stream(stream_args);
|
||||||
@@ -192,14 +174,9 @@ struct uhd_hw {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void actually_enable_streams()
|
|
||||||
{
|
|
||||||
// nop: stream cmd in handler
|
|
||||||
}
|
|
||||||
|
|
||||||
void *rx_cb(bh_fn_t burst_handler)
|
void *rx_cb(bh_fn_t burst_handler)
|
||||||
{
|
{
|
||||||
void *ret = nullptr;
|
void *ret;
|
||||||
static int to_skip = 0;
|
static int to_skip = 0;
|
||||||
|
|
||||||
uhd::rx_metadata_t md;
|
uhd::rx_metadata_t md;
|
||||||
@@ -239,7 +216,7 @@ struct uhd_hw {
|
|||||||
stream_cmd.time_spec = uhd::time_spec_t();
|
stream_cmd.time_spec = uhd::time_spec_t();
|
||||||
rx_stream->issue_stream_cmd(stream_cmd);
|
rx_stream->issue_stream_cmd(stream_cmd);
|
||||||
|
|
||||||
while (!stop_lower_threads_flag) {
|
while (!stop_me_flag) {
|
||||||
rx_cb(burst_handler);
|
rx_cb(burst_handler);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -77,24 +77,6 @@ static struct ctrl_handle *g_ctrlh;
|
|||||||
static RadioDevice *usrp;
|
static RadioDevice *usrp;
|
||||||
static RadioInterface *radio;
|
static RadioInterface *radio;
|
||||||
|
|
||||||
/* adjusts read timestamp offset to make the viterbi equalizer happy by including the start tail bits */
|
|
||||||
template <typename B>
|
|
||||||
class rif_va_wrapper : public B {
|
|
||||||
bool use_va;
|
|
||||||
|
|
||||||
public:
|
|
||||||
template <typename... Args>
|
|
||||||
rif_va_wrapper(bool use_va, Args &&...args) : B(std::forward<Args>(args)...), use_va(use_va)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
bool start() override
|
|
||||||
{
|
|
||||||
auto rv = B::start();
|
|
||||||
B::readTimestamp -= use_va ? 20 : 0;
|
|
||||||
return rv;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
/* Create radio interface
|
/* Create radio interface
|
||||||
* The interface consists of sample rate changes, frequency shifts,
|
* The interface consists of sample rate changes, frequency shifts,
|
||||||
* channel multiplexing, and other conversions. The transceiver core
|
* channel multiplexing, and other conversions. The transceiver core
|
||||||
@@ -109,17 +91,17 @@ RadioInterface *makeRadioInterface(struct trx_ctx *trx,
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case RadioDevice::NORMAL:
|
case RadioDevice::NORMAL:
|
||||||
radio = new rif_va_wrapper<RadioInterface>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps,
|
radio = new RadioInterface(usrp, trx->cfg.tx_sps,
|
||||||
trx->cfg.num_chans);
|
trx->cfg.rx_sps, trx->cfg.num_chans);
|
||||||
break;
|
break;
|
||||||
case RadioDevice::RESAMP_64M:
|
case RadioDevice::RESAMP_64M:
|
||||||
case RadioDevice::RESAMP_100M:
|
case RadioDevice::RESAMP_100M:
|
||||||
radio = new rif_va_wrapper<RadioInterfaceResamp>(trx->cfg.use_va, usrp, trx->cfg.tx_sps,
|
radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
|
||||||
trx->cfg.rx_sps);
|
trx->cfg.rx_sps);
|
||||||
break;
|
break;
|
||||||
case RadioDevice::MULTI_ARFCN:
|
case RadioDevice::MULTI_ARFCN:
|
||||||
radio = new rif_va_wrapper<RadioInterfaceMulti>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps,
|
radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
|
||||||
trx->cfg.num_chans);
|
trx->cfg.rx_sps, trx->cfg.num_chans);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
LOG(ALERT) << "Unsupported radio interface configuration";
|
LOG(ALERT) << "Unsupported radio interface configuration";
|
||||||
@@ -483,12 +465,6 @@ int trx_validate_config(struct trx_ctx *trx)
|
|||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (trx->cfg.use_va &&
|
|
||||||
(trx->cfg.egprs || trx->cfg.multi_arfcn || trx->cfg.tx_sps != 4 || trx->cfg.rx_sps != 4)) {
|
|
||||||
LOG(ERROR) << "Viterbi equalizer only works for gmsk with 4 tx/rx samples per symbol!";
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -595,14 +571,24 @@ static void trx_stop()
|
|||||||
static int trx_start(struct trx_ctx *trx)
|
static int trx_start(struct trx_ctx *trx)
|
||||||
{
|
{
|
||||||
int type, chans;
|
int type, chans;
|
||||||
|
unsigned int i;
|
||||||
|
std::vector<std::string> rx_paths, tx_paths;
|
||||||
RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
|
RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
|
||||||
|
|
||||||
/* Create the low level device object */
|
/* Create the low level device object */
|
||||||
if (trx->cfg.multi_arfcn)
|
if (trx->cfg.multi_arfcn)
|
||||||
iface = RadioDevice::MULTI_ARFCN;
|
iface = RadioDevice::MULTI_ARFCN;
|
||||||
|
|
||||||
usrp = RadioDevice::make(iface, &trx->cfg);
|
/* Generate vector of rx/tx_path: */
|
||||||
type = usrp->open();
|
for (i = 0; i < trx->cfg.num_chans; i++) {
|
||||||
|
rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path));
|
||||||
|
tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path));
|
||||||
|
}
|
||||||
|
|
||||||
|
usrp = RadioDevice::make(trx->cfg.tx_sps, trx->cfg.rx_sps, iface,
|
||||||
|
trx->cfg.num_chans, trx->cfg.offset,
|
||||||
|
tx_paths, rx_paths);
|
||||||
|
type = usrp->open(charp2str(trx->cfg.dev_args), trx->cfg.clock_ref, trx->cfg.swap_channels);
|
||||||
if (type < 0) {
|
if (type < 0) {
|
||||||
LOG(ALERT) << "Failed to create radio device" << std::endl;
|
LOG(ALERT) << "Failed to create radio device" << std::endl;
|
||||||
goto shutdown;
|
goto shutdown;
|
||||||
@@ -665,11 +651,11 @@ int main(int argc, char *argv[])
|
|||||||
exit(2);
|
exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = telnet_init_default(tall_trx_ctx, NULL, OSMO_VTY_PORT_TRX);
|
rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX);
|
||||||
if (rc < 0)
|
if (rc < 0)
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|
||||||
g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL);
|
g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_TRX, NULL);
|
||||||
if (!g_ctrlh) {
|
if (!g_ctrlh) {
|
||||||
LOG(ERROR) << "Failed to create CTRL interface.\n";
|
LOG(ERROR) << "Failed to create CTRL interface.\n";
|
||||||
exit(1);
|
exit(1);
|
||||||
|
|||||||
@@ -48,8 +48,9 @@ struct trxd_hdr_common {
|
|||||||
reserved:1,
|
reserved:1,
|
||||||
version:4;
|
version:4;
|
||||||
#elif OSMO_IS_BIG_ENDIAN
|
#elif OSMO_IS_BIG_ENDIAN
|
||||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
uint8_t version:4,
|
||||||
uint8_t version:4, reserved:1, tn:3;
|
reserved:1,
|
||||||
|
tn:3;
|
||||||
#endif
|
#endif
|
||||||
uint32_t fn; /* big endian */
|
uint32_t fn; /* big endian */
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
@@ -85,8 +86,9 @@ struct trxd_hdr_v1_specific {
|
|||||||
modulation:4,
|
modulation:4,
|
||||||
idle:1;
|
idle:1;
|
||||||
#elif OSMO_IS_BIG_ENDIAN
|
#elif OSMO_IS_BIG_ENDIAN
|
||||||
/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
|
uint8_t idle:1,
|
||||||
uint8_t idle:1, modulation:4, tsc:3;
|
modulation:4,
|
||||||
|
tsc:3;
|
||||||
#endif
|
#endif
|
||||||
int16_t ci; /* big endian, in centiBels */
|
int16_t ci; /* big endian, in centiBels */
|
||||||
} __attribute__ ((packed));
|
} __attribute__ ((packed));
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen,
|
RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen,
|
||||||
size_t hLen, bool outDirection)
|
size_t hLen, bool outDirection)
|
||||||
: writeIndex(0), readIndex(0), availSamples(0), segments(numSegments)
|
: writeIndex(0), readIndex(0), availSamples(0)
|
||||||
{
|
{
|
||||||
if (!outDirection)
|
if (!outDirection)
|
||||||
hLen = 0;
|
hLen = 0;
|
||||||
@@ -36,6 +36,7 @@ RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen,
|
|||||||
buffer = new float[2 * (hLen + numSegments * segmentLen)];
|
buffer = new float[2 * (hLen + numSegments * segmentLen)];
|
||||||
bufferLen = numSegments * segmentLen;
|
bufferLen = numSegments * segmentLen;
|
||||||
|
|
||||||
|
segments.resize(numSegments);
|
||||||
|
|
||||||
for (size_t i = 0; i < numSegments; i++)
|
for (size_t i = 0; i < numSegments; i++)
|
||||||
segments[i] = &buffer[2 * (hLen + i * segmentLen)];
|
segments[i] = &buffer[2 * (hLen + i * segmentLen)];
|
||||||
|
|||||||
@@ -39,10 +39,9 @@ extern "C" {
|
|||||||
RadioInterface::RadioInterface(RadioDevice *wDevice, size_t tx_sps,
|
RadioInterface::RadioInterface(RadioDevice *wDevice, size_t tx_sps,
|
||||||
size_t rx_sps, size_t chans,
|
size_t rx_sps, size_t chans,
|
||||||
int wReceiveOffset, GSM::Time wStartTime)
|
int wReceiveOffset, GSM::Time wStartTime)
|
||||||
: mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mReceiveFIFO(mChans), mDevice(wDevice),
|
: mDevice(wDevice), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
|
||||||
sendBuffer(mChans), recvBuffer(mChans), convertRecvBuffer(mChans),
|
underrun(false), overrun(false), writeTimestamp(0), readTimestamp(0),
|
||||||
convertSendBuffer(mChans), powerScaling(mChans), underrun(false), overrun(false),
|
receiveOffset(wReceiveOffset), mOn(false)
|
||||||
writeTimestamp(0), readTimestamp(0), receiveOffset(wReceiveOffset), mOn(false)
|
|
||||||
{
|
{
|
||||||
mClock.set(wStartTime);
|
mClock.set(wStartTime);
|
||||||
}
|
}
|
||||||
@@ -59,6 +58,15 @@ bool RadioInterface::init(int type)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close();
|
||||||
|
|
||||||
|
sendBuffer.resize(mChans);
|
||||||
|
recvBuffer.resize(mChans);
|
||||||
|
convertSendBuffer.resize(mChans);
|
||||||
|
convertRecvBuffer.resize(mChans);
|
||||||
|
mReceiveFIFO.resize(mChans);
|
||||||
|
powerScaling.resize(mChans);
|
||||||
|
|
||||||
for (size_t i = 0; i < mChans; i++) {
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
|
sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
|
||||||
recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
|
recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
|
||||||
|
|||||||
@@ -31,9 +31,6 @@ static const unsigned gSlotLen = 148; ///< number of symbols per slot, not
|
|||||||
class RadioInterface {
|
class RadioInterface {
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
size_t mSPSTx;
|
|
||||||
size_t mSPSRx;
|
|
||||||
size_t mChans;
|
|
||||||
|
|
||||||
Thread mAlignRadioServiceLoopThread; ///< thread that synchronizes transmit and receive sections
|
Thread mAlignRadioServiceLoopThread; ///< thread that synchronizes transmit and receive sections
|
||||||
|
|
||||||
@@ -41,6 +38,10 @@ protected:
|
|||||||
|
|
||||||
RadioDevice *mDevice; ///< the USRP object
|
RadioDevice *mDevice; ///< the USRP object
|
||||||
|
|
||||||
|
size_t mSPSTx;
|
||||||
|
size_t mSPSRx;
|
||||||
|
size_t mChans;
|
||||||
|
|
||||||
std::vector<RadioBuffer *> sendBuffer;
|
std::vector<RadioBuffer *> sendBuffer;
|
||||||
std::vector<RadioBuffer *> recvBuffer;
|
std::vector<RadioBuffer *> recvBuffer;
|
||||||
|
|
||||||
@@ -75,7 +76,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
/** start the interface */
|
/** start the interface */
|
||||||
virtual bool start();
|
bool start();
|
||||||
bool stop();
|
bool stop();
|
||||||
|
|
||||||
/** initialization */
|
/** initialization */
|
||||||
@@ -151,7 +152,7 @@ private:
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
RadioInterfaceResamp(RadioDevice* wDevice, size_t tx_sps, size_t rx_sps);
|
RadioInterfaceResamp(RadioDevice* wDevice, size_t tx_sps, size_t rx_sps);
|
||||||
virtual ~RadioInterfaceResamp();
|
~RadioInterfaceResamp();
|
||||||
|
|
||||||
bool init(int type);
|
bool init(int type);
|
||||||
void close();
|
void close();
|
||||||
@@ -184,7 +185,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
RadioInterfaceMulti(RadioDevice* radio, size_t tx_sps,
|
RadioInterfaceMulti(RadioDevice* radio, size_t tx_sps,
|
||||||
size_t rx_sps, size_t chans = 1);
|
size_t rx_sps, size_t chans = 1);
|
||||||
virtual ~RadioInterfaceMulti();
|
~RadioInterfaceMulti();
|
||||||
|
|
||||||
bool init(int type);
|
bool init(int type);
|
||||||
void close();
|
void close();
|
||||||
|
|||||||
@@ -44,9 +44,8 @@ extern "C" {
|
|||||||
RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps,
|
RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps,
|
||||||
size_t rx_sps, size_t chans)
|
size_t rx_sps, size_t chans)
|
||||||
: RadioInterface(radio, tx_sps, rx_sps, chans),
|
: RadioInterface(radio, tx_sps, rx_sps, chans),
|
||||||
outerSendBuffer(NULL), outerRecvBuffer(NULL), history(mChans), active(MCHANS, false),
|
outerSendBuffer(NULL), outerRecvBuffer(NULL),
|
||||||
rx_freq_state(mChans), tx_freq_state(mChans), dnsampler(NULL), upsampler(NULL), channelizer(NULL),
|
dnsampler(NULL), upsampler(NULL), channelizer(NULL), synthesis(NULL)
|
||||||
synthesis(NULL)
|
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,12 +74,12 @@ void RadioInterfaceMulti::close()
|
|||||||
for (std::vector<signalVector*>::iterator it = history.begin(); it != history.end(); ++it)
|
for (std::vector<signalVector*>::iterator it = history.begin(); it != history.end(); ++it)
|
||||||
delete *it;
|
delete *it;
|
||||||
|
|
||||||
mReceiveFIFO.clear();
|
mReceiveFIFO.resize(0);
|
||||||
powerScaling.clear();
|
powerScaling.resize(0);
|
||||||
history.clear();
|
history.resize(0);
|
||||||
active.clear();
|
active.resize(0);
|
||||||
rx_freq_state.clear();
|
rx_freq_state.resize(0);
|
||||||
tx_freq_state.clear();
|
tx_freq_state.resize(0);
|
||||||
|
|
||||||
RadioInterface::close();
|
RadioInterface::close();
|
||||||
}
|
}
|
||||||
@@ -153,9 +152,20 @@ bool RadioInterfaceMulti::init(int type)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
close();
|
||||||
|
|
||||||
|
sendBuffer.resize(mChans);
|
||||||
|
recvBuffer.resize(mChans);
|
||||||
convertSendBuffer.resize(1);
|
convertSendBuffer.resize(1);
|
||||||
convertRecvBuffer.resize(1);
|
convertRecvBuffer.resize(1);
|
||||||
|
|
||||||
|
mReceiveFIFO.resize(mChans);
|
||||||
|
powerScaling.resize(mChans);
|
||||||
|
history.resize(mChans);
|
||||||
|
rx_freq_state.resize(mChans);
|
||||||
|
tx_freq_state.resize(mChans);
|
||||||
|
active.resize(MCHANS, false);
|
||||||
|
|
||||||
/* 4 == sps */
|
/* 4 == sps */
|
||||||
inchunk = RESAMP_INRATE * 4;
|
inchunk = RESAMP_INRATE * 4;
|
||||||
outchunk = RESAMP_OUTRATE * 4;
|
outchunk = RESAMP_OUTRATE * 4;
|
||||||
|
|||||||
@@ -98,6 +98,15 @@ bool RadioInterfaceResamp::init(int type)
|
|||||||
{
|
{
|
||||||
float cutoff = 1.0f;
|
float cutoff = 1.0f;
|
||||||
|
|
||||||
|
close();
|
||||||
|
|
||||||
|
sendBuffer.resize(1);
|
||||||
|
recvBuffer.resize(1);
|
||||||
|
convertSendBuffer.resize(1);
|
||||||
|
convertRecvBuffer.resize(1);
|
||||||
|
mReceiveFIFO.resize(1);
|
||||||
|
powerScaling.resize(1);
|
||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case RadioDevice::RESAMP_64M:
|
case RadioDevice::RESAMP_64M:
|
||||||
resamp_inrate = RESAMP_64M_INRATE;
|
resamp_inrate = RESAMP_64M_INRATE;
|
||||||
|
|||||||
41
configure.ac
41
configure.ac
@@ -82,10 +82,10 @@ AC_TYPE_SIZE_T
|
|||||||
AC_HEADER_TIME
|
AC_HEADER_TIME
|
||||||
AC_C_BIGENDIAN
|
AC_C_BIGENDIAN
|
||||||
|
|
||||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
|
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
|
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
|
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.9.0)
|
PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.6.0)
|
||||||
|
|
||||||
AC_ARG_ENABLE(sanitize,
|
AC_ARG_ENABLE(sanitize,
|
||||||
[AS_HELP_STRING(
|
[AS_HELP_STRING(
|
||||||
@@ -143,11 +143,6 @@ AC_ARG_WITH(bladerf, [
|
|||||||
[enable bladeRF])
|
[enable bladeRF])
|
||||||
])
|
])
|
||||||
|
|
||||||
AC_ARG_WITH(mstrx, [
|
|
||||||
AS_HELP_STRING([--with-mstrx],
|
|
||||||
[enable MS TRX])
|
|
||||||
])
|
|
||||||
|
|
||||||
AC_ARG_WITH(singledb, [
|
AC_ARG_WITH(singledb, [
|
||||||
AS_HELP_STRING([--with-singledb],
|
AS_HELP_STRING([--with-singledb],
|
||||||
[enable single daughterboard use on USRP1])
|
[enable single daughterboard use on USRP1])
|
||||||
@@ -209,25 +204,6 @@ AS_IF([test "x$with_bladerf" = "xyes"], [
|
|||||||
PKG_CHECK_MODULES(BLADE, libbladeRF >= 2.0)
|
PKG_CHECK_MODULES(BLADE, libbladeRF >= 2.0)
|
||||||
])
|
])
|
||||||
|
|
||||||
AC_MSG_CHECKING([whether to enable building MS TRX])
|
|
||||||
AS_IF([test "x$with_mstrx" = "xyes"], [
|
|
||||||
AC_CONFIG_SUBDIRS([osmocom-bb/src/host/trxcon])
|
|
||||||
LIBTRXCON_DIR="osmocom-bb/src/host/trxcon"
|
|
||||||
if ! test -d "$srcdir/$LIBTRXCON_DIR"; then
|
|
||||||
AC_MSG_RESULT([no])
|
|
||||||
AC_MSG_ERROR([$LIBTRXCON_DIR does not exist])
|
|
||||||
fi
|
|
||||||
AC_SUBST(LIBTRXCON_DIR)
|
|
||||||
AC_MSG_RESULT([yes])
|
|
||||||
], [
|
|
||||||
# Despite LIBTRXCON_DIR is added to SUBDIRS conditionally,
|
|
||||||
# autoconf/automake still requires the directory to be present
|
|
||||||
# and thus the submodule to be fetched (even if MS TRX is not needed).
|
|
||||||
# Work this around by pointing it to an empty dir.
|
|
||||||
AC_SUBST(LIBTRXCON_DIR, "osmocom-bb")
|
|
||||||
AC_MSG_RESULT([no])
|
|
||||||
])
|
|
||||||
|
|
||||||
AS_IF([test "x$with_singledb" = "xyes"], [
|
AS_IF([test "x$with_singledb" = "xyes"], [
|
||||||
AC_DEFINE(SINGLEDB, 1, Define to 1 for single daughterboard)
|
AC_DEFINE(SINGLEDB, 1, Define to 1 for single daughterboard)
|
||||||
])
|
])
|
||||||
@@ -284,7 +260,6 @@ AM_CONDITIONAL(DEVICE_IPC, [test "x$with_ipc" = "xyes"])
|
|||||||
AM_CONDITIONAL(DEVICE_BLADE, [test "x$with_bladerf" = "xyes"])
|
AM_CONDITIONAL(DEVICE_BLADE, [test "x$with_bladerf" = "xyes"])
|
||||||
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
|
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
|
||||||
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
|
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
|
||||||
AM_CONDITIONAL(ENABLE_MS_TRX, [test "x$with_mstrx" = "xyes"])
|
|
||||||
|
|
||||||
PKG_CHECK_MODULES(LIBUSB, libusb-1.0)
|
PKG_CHECK_MODULES(LIBUSB, libusb-1.0)
|
||||||
PKG_CHECK_MODULES(FFTWF, fftw3f)
|
PKG_CHECK_MODULES(FFTWF, fftw3f)
|
||||||
@@ -352,6 +327,13 @@ AC_MSG_RESULT([CFLAGS="$CFLAGS"])
|
|||||||
AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"])
|
AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"])
|
||||||
AC_MSG_RESULT([LDFLAGS="$LDFLAGS"])
|
AC_MSG_RESULT([LDFLAGS="$LDFLAGS"])
|
||||||
|
|
||||||
|
AM_CONDITIONAL(ENABLE_MS_TRX, [test -d osmocom-bb])
|
||||||
|
|
||||||
|
if ENABLE_MS_TRX; then
|
||||||
|
AC_MSG_NOTICE(["Enabling ms-trx..."])
|
||||||
|
AC_CONFIG_SUBDIRS([osmocom-bb/src/host/trxcon])
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
dnl Output files
|
dnl Output files
|
||||||
AC_CONFIG_FILES([\
|
AC_CONFIG_FILES([\
|
||||||
@@ -369,6 +351,7 @@ AC_CONFIG_FILES([\
|
|||||||
Transceiver52M/device/usrp1/Makefile \
|
Transceiver52M/device/usrp1/Makefile \
|
||||||
Transceiver52M/device/lms/Makefile \
|
Transceiver52M/device/lms/Makefile \
|
||||||
Transceiver52M/device/ipc/Makefile \
|
Transceiver52M/device/ipc/Makefile \
|
||||||
|
Transceiver52M/device/ipc2/Makefile \
|
||||||
Transceiver52M/device/bladerf/Makefile \
|
Transceiver52M/device/bladerf/Makefile \
|
||||||
tests/Makefile \
|
tests/Makefile \
|
||||||
tests/CommonLibs/Makefile \
|
tests/CommonLibs/Makefile \
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
|
|||||||
export LD_LIBRARY_PATH="$inst/lib"
|
export LD_LIBRARY_PATH="$inst/lib"
|
||||||
export PATH="$inst/bin:$PATH"
|
export PATH="$inst/bin:$PATH"
|
||||||
|
|
||||||
CONFIG="--enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms --with-ipc --with-mstrx $INSTR"
|
CONFIG="--enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms --with-ipc $INSTR"
|
||||||
|
|
||||||
# Additional configure options and depends
|
# Additional configure options and depends
|
||||||
if [ "$WITH_MANUALS" = "1" ]; then
|
if [ "$WITH_MANUALS" = "1" ]; then
|
||||||
@@ -101,7 +101,6 @@ echo
|
|||||||
set -x
|
set -x
|
||||||
|
|
||||||
cd "$base"
|
cd "$base"
|
||||||
git submodule status
|
|
||||||
autoreconf --install --force
|
autoreconf --install --force
|
||||||
./configure $CONFIG
|
./configure $CONFIG
|
||||||
$MAKE $PARALLEL_MAKE
|
$MAKE $PARALLEL_MAKE
|
||||||
|
|||||||
@@ -34,10 +34,10 @@ BuildRequires: pkgconfig(LimeSuite)
|
|||||||
BuildRequires: pkgconfig(usrp) >= 3.3
|
BuildRequires: pkgconfig(usrp) >= 3.3
|
||||||
%endif
|
%endif
|
||||||
BuildRequires: pkgconfig(fftw3f)
|
BuildRequires: pkgconfig(fftw3f)
|
||||||
BuildRequires: pkgconfig(libosmocoding) >= 1.9.0
|
BuildRequires: pkgconfig(libosmocoding) >= 1.6.0
|
||||||
BuildRequires: pkgconfig(libosmocore) >= 1.9.0
|
BuildRequires: pkgconfig(libosmocore) >= 1.6.0
|
||||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.9.0
|
BuildRequires: pkgconfig(libosmoctrl) >= 1.6.0
|
||||||
BuildRequires: pkgconfig(libosmovty) >= 1.9.0
|
BuildRequires: pkgconfig(libosmovty) >= 1.6.0
|
||||||
BuildRequires: pkgconfig(libusb-1.0)
|
BuildRequires: pkgconfig(libusb-1.0)
|
||||||
BuildRequires: pkgconfig(uhd)
|
BuildRequires: pkgconfig(uhd)
|
||||||
%{?systemd_requires}
|
%{?systemd_requires}
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Osmocom SDR BTS L1 Transceiver (IPC Backend)
|
Description=Osmocom SDR BTS L1 Transceiver (IPC Backend)
|
||||||
After=network-online.target
|
|
||||||
Wants=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Osmocom SDR BTS L1 Transceiver (LimeSuite backend)
|
Description=Osmocom SDR BTS L1 Transceiver (LimeSuite backend)
|
||||||
After=network-online.target
|
|
||||||
Wants=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Osmocom SDR BTS L1 Transceiver (UHD Backend)
|
Description=Osmocom SDR BTS L1 Transceiver (UHD Backend)
|
||||||
After=network-online.target
|
|
||||||
Wants=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
[Unit]
|
[Unit]
|
||||||
Description=Osmocom SDR BTS L1 Transceiver (libusrp backend)
|
Description=Osmocom SDR BTS L1 Transceiver (libusrp backend)
|
||||||
After=network-online.target
|
|
||||||
Wants=network-online.target
|
|
||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
|
|||||||
116
debian/changelog
vendored
116
debian/changelog
vendored
@@ -1,119 +1,3 @@
|
|||||||
osmo-trx (1.6.1) unstable; urgency=medium
|
|
||||||
|
|
||||||
[ Eric Wild ]
|
|
||||||
* devices: fix wrong gain to power mapping
|
|
||||||
|
|
||||||
-- Eric Wild <ewild@sysmocom.de> Thu, 09 Nov 2023 14:16:21 +0200
|
|
||||||
|
|
||||||
osmo-trx (1.6.0) unstable; urgency=medium
|
|
||||||
|
|
||||||
[ Vadim Yanitskiy ]
|
|
||||||
* configure.ac: check if LIBTRXCON_DIR (submodule) exists
|
|
||||||
* tests: Makefile.am: move -I flags from AM_CFLAGS to AM_CPPFLAGS
|
|
||||||
* tests: there shall be no libraries in LDFLAGS
|
|
||||||
* tests: use -no-install libtool flag to avoid ./lt-* scripts
|
|
||||||
* tests: LMSDeviceTest: fix CPPFLAGS vs CXXFLAGS
|
|
||||||
* ipc-driver-test: clean up variables in Makefile.am
|
|
||||||
* CommonLibs: remove unused *trx in cfg[_no]_ctr_error_threshold_cmd
|
|
||||||
* CommonLibs: clean up and fix Makefile.am
|
|
||||||
* ms: logging: print category, level, and extended timestamp
|
|
||||||
|
|
||||||
[ Oliver Smith ]
|
|
||||||
* Run struct_endianness.py
|
|
||||||
* debian: set compat level to 10
|
|
||||||
* systemd: depend on networking-online.target
|
|
||||||
* USRPDevice:updateAlignment: remove byteswap code
|
|
||||||
|
|
||||||
[ Eric ]
|
|
||||||
* .clang-format: adjust template formatting
|
|
||||||
* ms: update submodule to currently known working version
|
|
||||||
* ms: adjust tx scaling for tx samples
|
|
||||||
* ms : fix the template formatting
|
|
||||||
* ms: fix the gain init for blade
|
|
||||||
* ms: prettify scheduling + add odroid
|
|
||||||
* ms: fix startup & shutdown of blade
|
|
||||||
* ms: block burst q to upper layer
|
|
||||||
* ms: use single thread pool
|
|
||||||
* ms : rename var
|
|
||||||
* ms : rename var
|
|
||||||
* ms: cache frequency
|
|
||||||
* ms: pretty tx buf class
|
|
||||||
* ms: rearrange internal trxcon<->phy if
|
|
||||||
* ms: remove syncthing tool
|
|
||||||
* ms: prune common sch acq code
|
|
||||||
* ms: rearrange code to allow clean exits
|
|
||||||
* ms: flexible template for value_type buffer sum
|
|
||||||
* ms: make init call less confusing
|
|
||||||
* transceiver: pass cfg struct instead of args
|
|
||||||
* devices: unify band handling
|
|
||||||
* ms: fix blocking logging
|
|
||||||
* ms: drop the tx burst padding
|
|
||||||
* trx: fix dev-args issue
|
|
||||||
* ms: update osmocom-bb
|
|
||||||
* ms: restructure the va code to add rach support
|
|
||||||
* transceiver: add experimental viterbi equalizer support
|
|
||||||
* ms/va: make ancient gcc < 8 happy
|
|
||||||
* ms: fix thread prio startup issue
|
|
||||||
* ms: fix a few coverity complaints related to initialization
|
|
||||||
* ms: bump osmocom-bb submodule to current head
|
|
||||||
|
|
||||||
[ Eric Wild ]
|
|
||||||
* ms: adjust float<->integral type conversion
|
|
||||||
* ms: sch: drop intermediate softvector
|
|
||||||
* devices: add freq/gain override for uhd
|
|
||||||
|
|
||||||
[ arehbein ]
|
|
||||||
* Transition to use of 'telnet_init_default'
|
|
||||||
|
|
||||||
[ Pau Espin Pedrol ]
|
|
||||||
* Call osmo_fd_unregister() before closing and changing bfd->fd
|
|
||||||
* ms: update osmocom-bb submodule
|
|
||||||
|
|
||||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 15:56:57 +0200
|
|
||||||
|
|
||||||
osmo-trx (1.5.0) unstable; urgency=medium
|
|
||||||
|
|
||||||
[ Oliver Smith ]
|
|
||||||
* configure.ac: add -lboost_thread for uhd < 4.2.0
|
|
||||||
* gitignore: add uhddev_ipc.cpp
|
|
||||||
* contrib/jenkins: don't run "make distcheck" on arm
|
|
||||||
|
|
||||||
[ Vadim Yanitskiy ]
|
|
||||||
* threshold_timer_update_intv(): call osmo_timer_del() unconditionally
|
|
||||||
* Transceiver::expectedCorrType(): RACH is always 8-bit on PTCCH/U
|
|
||||||
* contrib/jenkins.sh: dump submodule status before building
|
|
||||||
* configure.ac: fix: properly check whether to enable ms-trx
|
|
||||||
* configure.ac: allow building without cloning submodules
|
|
||||||
* configure.ac: cosmetic: rearrange MS TRX related logic
|
|
||||||
* configure.ac: make use of AC_MSG_CHECKING and AC_MSG_RESULT
|
|
||||||
|
|
||||||
[ Max ]
|
|
||||||
* Set working directory in systemd service file
|
|
||||||
* Add realtime scheduling and set priority in service file
|
|
||||||
* ctrl: take both address and port from vty config
|
|
||||||
|
|
||||||
[ Eric ]
|
|
||||||
* ignore vscode dirs
|
|
||||||
* rename noisevector class -> avgvector
|
|
||||||
* osmocom-bb for ms-trx side trxcon integration
|
|
||||||
* add checkpatch config
|
|
||||||
* bladerf xa4 support
|
|
||||||
* update osmocom-bb submodule to fix make distcheck
|
|
||||||
* vita demod by piotr krysik, modified
|
|
||||||
* properly update osmocom-bb submodule, for real this time..
|
|
||||||
* ms-trx support
|
|
||||||
* clean up mutex, scopedlock, and signal classes
|
|
||||||
* ipc: add missing override
|
|
||||||
* clang-format: proper c++ standard
|
|
||||||
* ipc: remove old autotools workaround
|
|
||||||
* ms: init trash used to escape the usb callbacks
|
|
||||||
* radio interface: fix init
|
|
||||||
|
|
||||||
[ Eric Wild ]
|
|
||||||
* mstrx: do not wait forever if clock locking fails
|
|
||||||
|
|
||||||
-- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 17:08:17 +0100
|
|
||||||
|
|
||||||
osmo-trx (1.4.1) unstable; urgency=medium
|
osmo-trx (1.4.1) unstable; urgency=medium
|
||||||
|
|
||||||
[ Oliver Smith ]
|
[ Oliver Smith ]
|
||||||
|
|||||||
2
debian/compat
vendored
2
debian/compat
vendored
@@ -1 +1 @@
|
|||||||
10
|
9
|
||||||
|
|||||||
6
debian/control
vendored
6
debian/control
vendored
@@ -2,7 +2,7 @@ Source: osmo-trx
|
|||||||
Section: net
|
Section: net
|
||||||
Priority: optional
|
Priority: optional
|
||||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
|
||||||
Build-Depends: debhelper (>= 10),
|
Build-Depends: debhelper (>= 9),
|
||||||
autotools-dev,
|
autotools-dev,
|
||||||
autoconf-archive,
|
autoconf-archive,
|
||||||
pkg-config,
|
pkg-config,
|
||||||
@@ -14,8 +14,8 @@ Build-Depends: debhelper (>= 10),
|
|||||||
libtalloc-dev,
|
libtalloc-dev,
|
||||||
libusrp-dev,
|
libusrp-dev,
|
||||||
liblimesuite-dev,
|
liblimesuite-dev,
|
||||||
libosmocore-dev (>= 1.9.0),
|
libosmocore-dev (>= 1.6.0),
|
||||||
osmo-gsm-manuals-dev (>= 1.5.0)
|
osmo-gsm-manuals-dev
|
||||||
Standards-Version: 3.9.6
|
Standards-Version: 3.9.6
|
||||||
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
|
Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
|
||||||
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
|
Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
|
||||||
|
|||||||
Submodule osmocom-bb updated: 05ddc05233...a4aac5c355
@@ -29,9 +29,9 @@
|
|||||||
#include "Threads.h"
|
#include "Threads.h"
|
||||||
#include "Interthread.h"
|
#include "Interthread.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <mutex>
|
|
||||||
|
|
||||||
std::mutex dbg_cout;
|
using namespace std;
|
||||||
|
|
||||||
|
|
||||||
InterthreadQueue<int> gQ;
|
InterthreadQueue<int> gQ;
|
||||||
InterthreadMap<int,int> gMap;
|
InterthreadMap<int,int> gMap;
|
||||||
@@ -41,8 +41,6 @@ int q_last_write_val;
|
|||||||
int m_last_read_val;
|
int m_last_read_val;
|
||||||
int m_last_write_val;
|
int m_last_write_val;
|
||||||
|
|
||||||
#define CERR(text) { dbg_cout.lock() ; std::cerr << text; dbg_cout.unlock(); }
|
|
||||||
|
|
||||||
void* qWriter(void*)
|
void* qWriter(void*)
|
||||||
{
|
{
|
||||||
int *p;
|
int *p;
|
||||||
|
|||||||
@@ -1,25 +1,7 @@
|
|||||||
include $(top_srcdir)/Makefile.common
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
AM_CPPFLAGS = \
|
AM_CPPFLAGS = -Wall -I$(top_srcdir)/CommonLibs $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) -g
|
||||||
-I$(top_srcdir)/CommonLibs \
|
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
|
||||||
$(STD_DEFINES_AND_INCLUDES) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
AM_CXXFLAGS = \
|
|
||||||
-Wall -g \
|
|
||||||
$(LIBOSMOCORE_CFLAGS) \
|
|
||||||
$(LIBOSMOCTRL_CFLAGS) \
|
|
||||||
$(LIBOSMOVTY_CFLAGS) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
AM_LDFLAGS = -no-install
|
|
||||||
|
|
||||||
LDADD = \
|
|
||||||
$(COMMON_LA) \
|
|
||||||
$(LIBOSMOCORE_LIBS) \
|
|
||||||
$(LIBOSMOCTRL_LIBS) \
|
|
||||||
$(LIBOSMOVTY_LIBS) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
EXTRA_DIST = BitVectorTest.ok \
|
EXTRA_DIST = BitVectorTest.ok \
|
||||||
PRBSTest.ok \
|
PRBSTest.ok \
|
||||||
@@ -38,16 +20,21 @@ check_PROGRAMS = \
|
|||||||
LogTest
|
LogTest
|
||||||
|
|
||||||
BitVectorTest_SOURCES = BitVectorTest.cpp
|
BitVectorTest_SOURCES = BitVectorTest.cpp
|
||||||
|
BitVectorTest_LDADD = $(COMMON_LA)
|
||||||
|
|
||||||
PRBSTest_SOURCES = PRBSTest.cpp
|
PRBSTest_SOURCES = PRBSTest.cpp
|
||||||
|
|
||||||
InterthreadTest_SOURCES = InterthreadTest.cpp
|
InterthreadTest_SOURCES = InterthreadTest.cpp
|
||||||
InterthreadTest_LDADD = $(LDADD) -lpthread
|
InterthreadTest_LDADD = $(COMMON_LA)
|
||||||
|
InterthreadTest_LDFLAGS = -lpthread $(AM_LDFLAGS)
|
||||||
|
|
||||||
TimevalTest_SOURCES = TimevalTest.cpp
|
TimevalTest_SOURCES = TimevalTest.cpp
|
||||||
|
TimevalTest_LDADD = $(COMMON_LA)
|
||||||
|
|
||||||
VectorTest_SOURCES = VectorTest.cpp
|
VectorTest_SOURCES = VectorTest.cpp
|
||||||
|
VectorTest_LDADD = $(COMMON_LA)
|
||||||
|
|
||||||
LogTest_SOURCES = LogTest.cpp
|
LogTest_SOURCES = LogTest.cpp
|
||||||
|
LogTest_LDADD = $(COMMON_LA)
|
||||||
|
|
||||||
MOSTLYCLEANFILES += testSource testDestination
|
MOSTLYCLEANFILES += testSource testDestination
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
include $(top_srcdir)/Makefile.common
|
include $(top_srcdir)/Makefile.common
|
||||||
|
|
||||||
AM_CPPFLAGS = \
|
AM_CFLAGS = -Wall -I$(top_srcdir)/Transceiver52M -I$(top_srcdir)/Transceiver52M/arch/common $(STD_DEFINES_AND_INCLUDES) -g
|
||||||
-I$(top_srcdir)/Transceiver52M \
|
|
||||||
-I$(top_srcdir)/Transceiver52M/arch/common \
|
|
||||||
$(STD_DEFINES_AND_INCLUDES) \
|
|
||||||
$(NULL)
|
|
||||||
|
|
||||||
AM_CFLAGS = -Wall -g
|
|
||||||
AM_LDFLAGS = -no-install
|
|
||||||
|
|
||||||
EXTRA_DIST = convolve_test.ok convolve_test_golden.h
|
EXTRA_DIST = convolve_test.ok convolve_test_golden.h
|
||||||
|
|
||||||
@@ -27,11 +20,10 @@ endif
|
|||||||
if DEVICE_LMS
|
if DEVICE_LMS
|
||||||
check_PROGRAMS += LMSDeviceTest
|
check_PROGRAMS += LMSDeviceTest
|
||||||
LMSDeviceTest_SOURCES = LMSDeviceTest.cpp
|
LMSDeviceTest_SOURCES = LMSDeviceTest.cpp
|
||||||
|
LMSDeviceTest_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LMS_LIBS)
|
||||||
LMSDeviceTest_LDADD = \
|
LMSDeviceTest_LDADD = \
|
||||||
$(top_builddir)/Transceiver52M/device/lms/libdevice.la \
|
$(top_builddir)/Transceiver52M/device/lms/libdevice.la \
|
||||||
$(LIBOSMOCORE_LIBS) \
|
|
||||||
$(COMMON_LA) \
|
$(COMMON_LA) \
|
||||||
$(LMS_LIBS) \
|
$(LMS_LIBS)
|
||||||
$(NULL)
|
LMSDeviceTest_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
|
||||||
LMSDeviceTest_CXXFLAGS = $(AM_CFLAGS) $(LMS_CFLAGS)
|
|
||||||
endif
|
endif
|
||||||
|
|||||||
Reference in New Issue
Block a user