mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-04 22:23:16 +00:00
Compare commits
4 Commits
1.5.0
...
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
|
|
||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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 \
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
@@ -40,7 +40,9 @@ COMMON_SOURCES = \
|
|||||||
ChannelizerBase.cpp \
|
ChannelizerBase.cpp \
|
||||||
Channelizer.cpp \
|
Channelizer.cpp \
|
||||||
Synthesis.cpp \
|
Synthesis.cpp \
|
||||||
proto_trxd.c
|
proto_trxd.c \
|
||||||
|
grgsm_vitac/grgsm_vitac.cpp \
|
||||||
|
grgsm_vitac/viterbi_detector.cc
|
||||||
|
|
||||||
libtransceiver_common_la_SOURCES = \
|
libtransceiver_common_la_SOURCES = \
|
||||||
$(COMMON_SOURCES) \
|
$(COMMON_SOURCES) \
|
||||||
@@ -61,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 \
|
||||||
@@ -70,38 +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 \
|
||||||
$(LIBOSMOCODING_LIBS)
|
$(top_builddir)/osmocom-bb/src/host/trxcon/src/.libs/libtrxcon.a
|
||||||
|
|
||||||
MS_SOURCES = \
|
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
|
|
||||||
|
|
||||||
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_rx_burst.h \
|
|
||||||
ms/ms_upper.h \
|
ms/ms_upper.h \
|
||||||
ms/itrq.h \
|
ms/itrq.h
|
||||||
ms/sch.h \
|
|
||||||
grgsm_vitac/viterbi_detector.h \
|
|
||||||
grgsm_vitac/constants.h \
|
|
||||||
grgsm_vitac/grgsm_vitac.h
|
|
||||||
|
|
||||||
endif
|
|
||||||
|
|
||||||
bin_PROGRAMS =
|
bin_PROGRAMS =
|
||||||
|
|
||||||
@@ -116,7 +107,7 @@ 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_SOURCES) ms/ms_upper.cpp ms/l1ctl_server.c ms/logging.c ms/l1ctl_server_cb.cpp
|
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) \
|
||||||
@@ -166,7 +157,7 @@ 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_SOURCES) ms/ms_upper.cpp ms/l1ctl_server.c ms/logging.c ms/l1ctl_server_cb.cpp
|
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) \
|
||||||
@@ -192,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
|
||||||
|
|||||||
@@ -135,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,7 +208,14 @@ bool Transceiver::init()
|
|||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
|
|||||||
@@ -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() {};
|
||||||
@@ -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,14 +39,29 @@ 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)); \
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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 */
|
/* Device Type, Tx-SPS, Rx-SPS */
|
||||||
typedef std::tuple<blade_dev_type, int, int> dev_key;
|
typedef std::tuple<blade_dev_type, int, int> dev_key;
|
||||||
|
|
||||||
@@ -166,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);
|
||||||
@@ -175,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()
|
||||||
@@ -192,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);
|
||||||
@@ -207,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()
|
||||||
@@ -220,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)
|
||||||
@@ -280,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);
|
||||||
|
|
||||||
@@ -338,6 +356,7 @@ int blade_device::open(const std::string &args, int ref, bool swap_channels)
|
|||||||
|
|
||||||
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);
|
||||||
@@ -385,7 +404,7 @@ int blade_device::open(const std::string &args, int ref, bool swap_channels)
|
|||||||
|
|
||||||
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;
|
||||||
@@ -400,14 +419,17 @@ int blade_device::open(const std::string &args, int ref, bool swap_channels)
|
|||||||
/* 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;
|
||||||
@@ -441,6 +463,7 @@ bool blade_device::start()
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start streaming
|
||||||
if (!restart())
|
if (!restart())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
@@ -492,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;
|
||||||
;
|
;
|
||||||
@@ -522,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)) {
|
||||||
@@ -547,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;
|
||||||
}
|
}
|
||||||
@@ -664,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);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -687,8 +724,10 @@ double blade_device::fullScaleOutputValue()
|
|||||||
return (double)2047;
|
return (double)2047;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef IPCMAGIC
|
||||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
|
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)
|
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
|
||||||
{
|
{
|
||||||
return new blade_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
|
return new blade_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|||||||
@@ -169,13 +169,14 @@ class RadioDevice {
|
|||||||
const std::vector<std::string>& tx_paths,
|
const std::vector<std::string>& tx_paths,
|
||||||
const std::vector<std::string>& rx_paths):
|
const std::vector<std::string>& rx_paths):
|
||||||
tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chan_num), lo_offset(offset),
|
tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chan_num), lo_offset(offset),
|
||||||
tx_paths(tx_paths), rx_paths(rx_paths), m_ctr(chans)
|
tx_paths(tx_paths), rx_paths(rx_paths)
|
||||||
{
|
{
|
||||||
if (iface == MULTI_ARFCN) {
|
if (iface == MULTI_ARFCN) {
|
||||||
LOGC(DDEV, INFO) << "Multi-ARFCN: "<< chan_num << " 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;
|
||||||
|
|||||||
@@ -58,12 +58,18 @@ static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags);
|
|||||||
|
|
||||||
IPCDevice::IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
|
IPCDevice::IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
|
||||||
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
|
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
|
||||||
: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths), sk_chan_state(chans, ipc_per_trx_sock_state()),
|
: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths), tx_attenuation(),
|
||||||
tx_attenuation(), tmp_state(IPC_IF_MSG_GREETING_REQ), shm(NULL), shm_dec(0),
|
tmp_state(IPC_IF_MSG_GREETING_REQ), shm(NULL), shm_dec(0), started(false)
|
||||||
rx_buffers(chans), started(false), tx_gains(chans), rx_gains(chans)
|
|
||||||
{
|
{
|
||||||
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));
|
||||||
|
|||||||
@@ -199,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;
|
||||||
|
|||||||
@@ -17,11 +17,19 @@ libdevice_la_SOURCES = IPCDevice.cpp shm.c ipc_shm.c ipc_chan.c ipc_sock.c
|
|||||||
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
|
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
|
||||||
libdevice_la_CXXFLAGS = $(AM_CXXFLAGS) -DIPCMAGIC
|
libdevice_la_CXXFLAGS = $(AM_CXXFLAGS) -DIPCMAGIC
|
||||||
|
|
||||||
|
#work around distclean issue on older autotools vers:
|
||||||
|
#a direct build of ../uhd/UHDDevice.cpp tries to clean
|
||||||
|
#../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 ipc_shm.c ipc_chan.c ipc_sock.c ../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 = \
|
||||||
shm.lo \
|
shm.lo \
|
||||||
$(top_builddir)/Transceiver52M/device/common/libdevice_common.la \
|
$(top_builddir)/Transceiver52M/device/common/libdevice_common.la \
|
||||||
|
|||||||
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
|
||||||
@@ -74,7 +74,7 @@ void initvita() {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
MULTI_VER_TARGET_ATTR NO_UBSAN
|
MULTI_VER_TARGET_ATTR
|
||||||
void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
|
void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
|
||||||
{
|
{
|
||||||
std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
|
std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
|
||||||
|
|||||||
@@ -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,21 +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
|
|
||||||
|
|
||||||
#if defined(__has_attribute)
|
|
||||||
#if __has_attribute(no_sanitize)
|
|
||||||
#define NO_UBSAN __attribute__((no_sanitize("undefined")))
|
|
||||||
#endif
|
|
||||||
#else
|
|
||||||
#define NO_UBSAN
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#define SYNC_SEARCH_RANGE 30
|
#define SYNC_SEARCH_RANGE 30
|
||||||
const int d_OSR(4);
|
const int d_OSR(4);
|
||||||
|
|
||||||
@@ -58,7 +40,7 @@ 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);
|
||||||
|
|
||||||
MULTI_VER_TARGET_ATTR_CLANGONLY
|
MULTI_VER_TARGET_ATTR
|
||||||
void detect_burst(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary);
|
void detect_burst(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);
|
||||||
|
|||||||
@@ -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,7 +39,6 @@ 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);
|
||||||
@@ -48,6 +48,7 @@ template <typename Arg, typename... Args> void expand_args(std::ostream &out, Ar
|
|||||||
|
|
||||||
template <class R, class... Args> using RvalFunc = R (*)(Args...);
|
template <class R, class... 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)
|
||||||
@@ -108,7 +109,7 @@ template <unsigned int SZ, blade_speed_buffer_type T> 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();
|
||||||
}
|
}
|
||||||
@@ -156,7 +157,7 @@ template <unsigned int SZ, blade_speed_buffer_type T> 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) {
|
||||||
@@ -191,7 +192,6 @@ template <typename T> struct blade_hw {
|
|||||||
const int rxtxdelay;
|
const int rxtxdelay;
|
||||||
|
|
||||||
float rxgain, txgain;
|
float rxgain, txgain;
|
||||||
static std::atomic<bool> stop_me_flag;
|
|
||||||
|
|
||||||
struct ms_trx_config {
|
struct ms_trx_config {
|
||||||
int tx_freq;
|
int tx_freq;
|
||||||
@@ -223,14 +223,14 @@ template <typename T> struct blade_hw {
|
|||||||
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);
|
||||||
|
|
||||||
@@ -297,7 +297,7 @@ template <typename T> struct blade_hw {
|
|||||||
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]);
|
||||||
}
|
}
|
||||||
@@ -379,9 +379,6 @@ template <typename T> 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_me_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 {
|
||||||
@@ -404,9 +401,6 @@ template <typename T> 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_me_flag)
|
|
||||||
return BLADERF_STREAM_SHUTDOWN;
|
|
||||||
|
|
||||||
return BLADERF_STREAM_NO_DATA;
|
return BLADERF_STREAM_NO_DATA;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -419,7 +413,7 @@ template <typename T> struct blade_hw {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
@@ -431,7 +425,7 @@ template <typename T> struct blade_hw {
|
|||||||
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;
|
||||||
}
|
}
|
||||||
|
|||||||
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,6 +27,25 @@
|
|||||||
#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> class spsc_cond_detail {
|
template <bool block_read, bool block_write> class spsc_cond_detail {
|
||||||
|
|||||||
@@ -58,11 +58,10 @@ static void l1ctl_conn_close_cb(struct l1ctl_client *l1c)
|
|||||||
|
|
||||||
namespace trxcon
|
namespace trxcon
|
||||||
{
|
{
|
||||||
bool trxc_l1ctl_init(void *tallctx)
|
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_read_cb = &l1ctl_rx_cb,
|
||||||
@@ -72,8 +71,7 @@ bool trxc_l1ctl_init(void *tallctx)
|
|||||||
|
|
||||||
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
|
} // namespace trxcon
|
||||||
@@ -1,41 +1,51 @@
|
|||||||
/*
|
/*
|
||||||
* (C) 2016-2022 by Vadim Yanitskiy <axilirator@gmail.com>
|
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
|
||||||
*
|
|
||||||
* 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/logging.h>
|
#include <osmocom/bb/trxcon/logging.h>
|
||||||
#include <osmocom/bb/trxcon/trxcon.h>
|
#include <osmocom/bb/trxcon/trxcon.h>
|
||||||
|
#include <osmocom/bb/l1sched/l1sched.h>
|
||||||
|
|
||||||
|
}
|
||||||
|
static const int trxcon_log_cfg[] = {
|
||||||
|
[TRXCON_LOGC_FSM] = DAPP,
|
||||||
|
[TRXCON_LOGC_L1C] = DL1C,
|
||||||
|
[TRXCON_LOGC_L1D] = DL1D,
|
||||||
|
[TRXCON_LOGC_SCHC] = DSCH,
|
||||||
|
[TRXCON_LOGC_SCHD] = DSCHD,
|
||||||
|
};
|
||||||
|
|
||||||
static struct log_info_cat trxcon_log_info_cat[] = {
|
static struct log_info_cat trxcon_log_info_cat[] = {
|
||||||
[DAPP] = {
|
[DAPP] = {
|
||||||
.name = "DAPP",
|
.name = "DAPP",
|
||||||
.color = "\033[1;35m",
|
.color = "\033[1;35m",
|
||||||
.description = "Application",
|
.description = "Application",
|
||||||
.loglevel = LOGL_NOTICE,
|
.loglevel = LOGL_NOTICE, .enabled = 1,
|
||||||
.enabled = 1,
|
|
||||||
},
|
},
|
||||||
[DL1C] = {
|
[DL1C] = {
|
||||||
.name = "DL1C",
|
.name = "DL1C",
|
||||||
.color = "\033[1;31m",
|
.color = "\033[1;31m",
|
||||||
.description = "Layer 1 control interface",
|
.description = "Layer 1 control interface",
|
||||||
.loglevel = LOGL_NOTICE,
|
.loglevel = LOGL_NOTICE, .enabled = 1,
|
||||||
.enabled = 1,
|
|
||||||
},
|
},
|
||||||
[DL1D] = {
|
[DL1D] = {
|
||||||
.name = "DL1D",
|
.name = "DL1D",
|
||||||
@@ -44,6 +54,20 @@ static struct log_info_cat trxcon_log_info_cat[] = {
|
|||||||
.loglevel = LOGL_NOTICE,
|
.loglevel = LOGL_NOTICE,
|
||||||
.enabled = 1,
|
.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] = {
|
[DSCH] = {
|
||||||
.name = "DSCH",
|
.name = "DSCH",
|
||||||
.color = "\033[1;36m",
|
.color = "\033[1;36m",
|
||||||
@@ -65,17 +89,15 @@ static struct log_info trxcon_log_info = {
|
|||||||
.num_cat = ARRAY_SIZE(trxcon_log_info_cat),
|
.num_cat = ARRAY_SIZE(trxcon_log_info_cat),
|
||||||
};
|
};
|
||||||
|
|
||||||
static const int trxcon_log_cfg[] = {
|
namespace trxcon
|
||||||
[TRXCON_LOGC_FSM] = DAPP,
|
{
|
||||||
[TRXCON_LOGC_L1C] = DL1C,
|
|
||||||
[TRXCON_LOGC_L1D] = DL1D,
|
|
||||||
[TRXCON_LOGC_SCHC] = DSCH,
|
|
||||||
[TRXCON_LOGC_SCHD] = DSCHD,
|
|
||||||
};
|
|
||||||
|
|
||||||
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_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
|
||||||
@@ -49,8 +49,6 @@ const int offset_start = -15;
|
|||||||
static int offset_ctr = 0;
|
static int offset_ctr = 0;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template <> std::atomic<bool> ms_trx::base::stop_me_flag(false);
|
|
||||||
|
|
||||||
void tx_test(ms_trx *t, ts_hitter_q_t *q, unsigned int *tsc)
|
void tx_test(ms_trx *t, ts_hitter_q_t *q, unsigned int *tsc)
|
||||||
{
|
{
|
||||||
sched_param sch_params;
|
sched_param sch_params;
|
||||||
@@ -109,8 +107,7 @@ void tx_test(ms_trx *t, ts_hitter_q_t *q, unsigned int *tsc)
|
|||||||
|
|
||||||
unsigned int pad = 4 * 25;
|
unsigned int pad = 4 * 25;
|
||||||
blade_sample_type buf2[burst->size() + pad];
|
blade_sample_type buf2[burst->size() + pad];
|
||||||
std::fill(buf2, buf2 + pad, 0);
|
memset(buf2, 0, pad * sizeof(blade_sample_type));
|
||||||
|
|
||||||
memcpy(&buf2[pad], burst_buf, burst->size() * sizeof(blade_sample_type));
|
memcpy(&buf2[pad], burst_buf, burst->size() * sizeof(blade_sample_type));
|
||||||
|
|
||||||
assert(target.FN() == check.FN());
|
assert(target.FN() == check.FN());
|
||||||
@@ -261,10 +258,9 @@ 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;
|
||||||
};
|
};
|
||||||
@@ -290,7 +286,6 @@ void ms_trx::set_upper_ready(bool is_ready)
|
|||||||
void ms_trx::stop_threads()
|
void ms_trx::stop_threads()
|
||||||
{
|
{
|
||||||
std::cerr << "killing threads...\r\n" << std::endl;
|
std::cerr << "killing threads...\r\n" << std::endl;
|
||||||
stop_me_flag = true;
|
|
||||||
close_device();
|
close_device();
|
||||||
rx_task.join();
|
rx_task.join();
|
||||||
tx_task.join();
|
tx_task.join();
|
||||||
@@ -314,7 +309,7 @@ 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;
|
||||||
@@ -332,7 +327,7 @@ void ms_trx::submit_burst(blade_sample_type *buffer, int len, GSM::Time target)
|
|||||||
#if 1
|
#if 1
|
||||||
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,6 +35,9 @@
|
|||||||
#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
|
||||||
@@ -198,7 +202,7 @@ struct ms_trx : public BASET {
|
|||||||
bool handle_sch(bool first = false);
|
bool handle_sch(bool first = false);
|
||||||
bool decode_sch(float *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_device();
|
||||||
int init_dev_and_streams();
|
int init_dev_and_streams();
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ static void check_rcv_fn(GSM::Time t, bool first, unsigned int &lastfn, unsigned
|
|||||||
fnbm = 1 << 0;
|
fnbm = 1 << 0;
|
||||||
first = false;
|
first = false;
|
||||||
}
|
}
|
||||||
if (!first && t.FN() != (int)lastfn) {
|
if (!first && t.FN() != lastfn) {
|
||||||
if (fnbm != 255)
|
if (fnbm != 255)
|
||||||
std::cerr << "rx " << lastfn << ":" << fnbm << " " << __builtin_popcount(fnbm) << std::endl;
|
std::cerr << "rx " << lastfn << ":" << fnbm << " " << __builtin_popcount(fnbm) << std::endl;
|
||||||
lastfn = t.FN();
|
lastfn = t.FN();
|
||||||
@@ -81,7 +81,7 @@ static void check_rcv_fn(GSM::Time t, bool first, unsigned int &lastfn, unsigned
|
|||||||
|
|
||||||
static void handle_it(one_burst &e, signalVector &burst, unsigned int tsc, int scale)
|
static void handle_it(one_burst &e, signalVector &burst, unsigned int tsc, int scale)
|
||||||
{
|
{
|
||||||
std::fill(burst.begin(), burst.begin() + burst.size(), 0.0);
|
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_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());
|
const auto is_fcch = gsm_fcch_check_ts(e.gsmts.TN(), e.gsmts.FN());
|
||||||
|
|
||||||
|
|||||||
@@ -140,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;
|
||||||
@@ -219,7 +221,7 @@ bool ms_trx::handle_sch(bool is_first_sch_acq)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
SCH_STATE ms_trx::search_for_sch(dev_buf_t *rcd)
|
__attribute__((xray_never_instrument)) SCH_STATE ms_trx::search_for_sch(dev_buf_t *rcd)
|
||||||
{
|
{
|
||||||
static unsigned int sch_pos = 0;
|
static unsigned int sch_pos = 0;
|
||||||
if (sch_thread_done)
|
if (sch_thread_done)
|
||||||
@@ -242,7 +244,7 @@ SCH_STATE ms_trx::search_for_sch(dev_buf_t *rcd)
|
|||||||
auto ptr = reinterpret_cast<const int16_t *>(first_sch_buf);
|
auto ptr = reinterpret_cast<const int16_t *>(first_sch_buf);
|
||||||
const auto target_val = rxFullScale / 8;
|
const auto target_val = rxFullScale / 8;
|
||||||
float sum = 0;
|
float sum = 0;
|
||||||
for (unsigned int i = 0; i < SCH_LEN_SPS * 2; i++)
|
for (int i = 0; i < SCH_LEN_SPS * 2; i++)
|
||||||
sum += std::abs(ptr[i]);
|
sum += std::abs(ptr[i]);
|
||||||
sum /= SCH_LEN_SPS * 2;
|
sum /= SCH_LEN_SPS * 2;
|
||||||
|
|
||||||
@@ -262,7 +264,7 @@ SCH_STATE ms_trx::search_for_sch(dev_buf_t *rcd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
auto spsmax = rcd->actual_samples_per_buffer();
|
auto spsmax = rcd->actual_samples_per_buffer();
|
||||||
if (to_copy > (unsigned int)spsmax)
|
if (to_copy > spsmax)
|
||||||
sch_pos += rcd->readall(first_sch_buf + sch_pos);
|
sch_pos += rcd->readall(first_sch_buf + sch_pos);
|
||||||
else
|
else
|
||||||
sch_pos += rcd->read_n(first_sch_buf + sch_pos, 0, to_copy);
|
sch_pos += rcd->read_n(first_sch_buf + sch_pos, 0, to_copy);
|
||||||
@@ -284,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)
|
||||||
@@ -304,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;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,9 +24,15 @@
|
|||||||
#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"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
|
#include <osmocom/core/select.h>
|
||||||
|
#include "sch.h"
|
||||||
|
#include "convolve.h"
|
||||||
|
#include "convert.h"
|
||||||
|
#include "proto_trxd.h"
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
@@ -36,11 +42,6 @@ extern "C" {
|
|||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
#include <fenv.h>
|
|
||||||
|
|
||||||
#include "sch.h"
|
|
||||||
#include "convolve.h"
|
|
||||||
#include "convert.h"
|
|
||||||
|
|
||||||
#ifdef LSANDEBUG
|
#ifdef LSANDEBUG
|
||||||
void __lsan_do_recoverable_leak_check();
|
void __lsan_do_recoverable_leak_check();
|
||||||
@@ -57,15 +58,21 @@ extern "C" {
|
|||||||
#include <osmocom/core/talloc.h>
|
#include <osmocom/core/talloc.h>
|
||||||
#include <osmocom/core/signal.h>
|
#include <osmocom/core/signal.h>
|
||||||
#include <osmocom/core/select.h>
|
#include <osmocom/core/select.h>
|
||||||
#include <osmocom/gsm/gsm_utils.h>
|
#include <osmocom/core/gsmtap_util.h>
|
||||||
|
#include <osmocom/core/gsmtap.h>
|
||||||
|
|
||||||
|
// #include <osmocom/core/application.h>
|
||||||
#include <osmocom/core/logging.h>
|
#include <osmocom/core/logging.h>
|
||||||
#include <osmocom/bb/trxcon/logging.h>
|
#include <osmocom/bb/trxcon/logging.h>
|
||||||
|
|
||||||
#include <osmocom/bb/trxcon/trxcon.h>
|
#include <osmocom/bb/trxcon/trxcon.h>
|
||||||
#include <osmocom/bb/trxcon/trxcon_fsm.h>
|
#include <osmocom/bb/trxcon/trxcon_fsm.h>
|
||||||
#include <osmocom/bb/trxcon/phyif.h>
|
#include <osmocom/bb/trxcon/phyif.h>
|
||||||
|
#include <osmocom/bb/trxcon/trx_if.h>
|
||||||
#include <osmocom/bb/trxcon/l1ctl_server.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;
|
||||||
// trx_instance *trxcon_instance; // local handle
|
// trx_instance *trxcon_instance; // local handle
|
||||||
@@ -80,10 +87,10 @@ static tx_queue_t txq;
|
|||||||
static cmd_queue_t cmdq_to_phy;
|
static cmd_queue_t cmdq_to_phy;
|
||||||
static cmdr_queue_t cmdq_from_phy;
|
static cmdr_queue_t cmdq_from_phy;
|
||||||
|
|
||||||
extern bool trxc_l1ctl_init(void *tallctx);
|
extern void trxc_log_init(void *tallctx);
|
||||||
|
extern void trxc_l1ctl_init(void *tallctx);
|
||||||
|
|
||||||
} // namespace trxcon
|
} // namespace trxcon
|
||||||
extern "C" void trxc_log_init(void *tallctx);
|
|
||||||
|
|
||||||
#ifdef LOG
|
#ifdef LOG
|
||||||
#undef LOG
|
#undef LOG
|
||||||
@@ -92,20 +99,18 @@ extern "C" void trxc_log_init(void *tallctx);
|
|||||||
|
|
||||||
#define DBGLG(...) upper_trx::dummy_log()
|
#define DBGLG(...) upper_trx::dummy_log()
|
||||||
|
|
||||||
std::atomic<bool> g_exit_flag;
|
|
||||||
|
|
||||||
void upper_trx::start_threads()
|
void upper_trx::start_threads()
|
||||||
{
|
{
|
||||||
thr_control = std::thread([this] {
|
thr_control = std::thread([this] {
|
||||||
set_name_aff_sched("upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR));
|
set_name_aff_sched("upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR));
|
||||||
while (!g_exit_flag) {
|
while (1) {
|
||||||
driveControl();
|
driveControl();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
msleep(1);
|
msleep(1);
|
||||||
thr_tx = std::thread([this] {
|
thr_tx = std::thread([this] {
|
||||||
set_name_aff_sched("upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1);
|
set_name_aff_sched("upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1);
|
||||||
while (!g_exit_flag) {
|
while (1) {
|
||||||
driveTx();
|
driveTx();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -114,10 +119,10 @@ void upper_trx::start_threads()
|
|||||||
start_lower_ms();
|
start_lower_ms();
|
||||||
|
|
||||||
set_name_aff_sched("upper_rx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_RR) - 5);
|
set_name_aff_sched("upper_rx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_RR) - 5);
|
||||||
while (!g_exit_flag) {
|
while (1) {
|
||||||
// set_upper_ready(true);
|
// set_upper_ready(true);
|
||||||
driveReceiveFIFO();
|
driveReceiveFIFO();
|
||||||
trxcon::osmo_select_main(1);
|
osmo_select_main(1);
|
||||||
|
|
||||||
trxcon::trxcon_phyif_rsp r;
|
trxcon::trxcon_phyif_rsp r;
|
||||||
if (trxcon::cmdq_from_phy.spsc_pop(&r)) {
|
if (trxcon::cmdq_from_phy.spsc_pop(&r)) {
|
||||||
@@ -143,24 +148,14 @@ void upper_trx::start_lower_ms()
|
|||||||
ms_trx::start();
|
ms_trx::start();
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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)) {
|
||||||
@@ -225,7 +220,6 @@ bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
|
|||||||
#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;
|
||||||
@@ -241,6 +235,7 @@ void upper_trx::driveReceiveFIFO()
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
if (pullRadioVector(burstTime, RSSI, TOA)) {
|
if (pullRadioVector(burstTime, RSSI, TOA)) {
|
||||||
|
// trxcon::trx_data_rx_handler(trxcon::trxcon_instance, (uint8_t *)&response);
|
||||||
trxcon::trxcon_phyif_burst_ind bi;
|
trxcon::trxcon_phyif_burst_ind bi;
|
||||||
bi.fn = burstTime.FN();
|
bi.fn = burstTime.FN();
|
||||||
bi.tn = burstTime.TN();
|
bi.tn = burstTime.TN();
|
||||||
@@ -248,6 +243,7 @@ void upper_trx::driveReceiveFIFO()
|
|||||||
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_clock_ind(trxcon::g_trxcon, bi.fn);
|
||||||
trxcon_phyif_handle_burst_ind(trxcon::g_trxcon, &bi);
|
trxcon_phyif_handle_burst_ind(trxcon::g_trxcon, &bi);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -260,18 +256,10 @@ void upper_trx::driveReceiveFIFO()
|
|||||||
void upper_trx::driveTx()
|
void upper_trx::driveTx()
|
||||||
{
|
{
|
||||||
trxcon::internal_q_tx_buf e;
|
trxcon::internal_q_tx_buf e;
|
||||||
static BitVector newBurst(sizeof(e.buf));
|
|
||||||
while (!trxcon::txq.spsc_pop(&e)) {
|
while (!trxcon::txq.spsc_pop(&e)) {
|
||||||
trxcon::txq.spsc_prep_pop();
|
trxcon::txq.spsc_prep_pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ensure our tx cb is tickled and can exit
|
|
||||||
if (g_exit_flag) {
|
|
||||||
blade_sample_type dummy[10] = {};
|
|
||||||
submit_burst_ts(dummy, 10, 1);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
trxcon::internal_q_tx_buf *burst = &e;
|
trxcon::internal_q_tx_buf *burst = &e;
|
||||||
|
|
||||||
#ifdef TXDEBUG
|
#ifdef TXDEBUG
|
||||||
@@ -282,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())
|
||||||
@@ -309,7 +298,6 @@ 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::trxcon_phyif_cmd_type c)
|
||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
@@ -338,7 +326,6 @@ static void print_cmd(trxcon::trxcon_phyif_cmd_type c)
|
|||||||
{
|
{
|
||||||
DBGLG() << cmd2str(c) << std::endl;
|
DBGLG() << cmd2str(c) << std::endl;
|
||||||
}
|
}
|
||||||
#endif
|
|
||||||
|
|
||||||
bool upper_trx::driveControl()
|
bool upper_trx::driveControl()
|
||||||
{
|
{
|
||||||
@@ -347,39 +334,36 @@ bool upper_trx::driveControl()
|
|||||||
while (!trxcon::cmdq_to_phy.spsc_pop(&cmd)) {
|
while (!trxcon::cmdq_to_phy.spsc_pop(&cmd)) {
|
||||||
trxcon::cmdq_to_phy.spsc_prep_pop();
|
trxcon::cmdq_to_phy.spsc_prep_pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
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::TRXCON_PHYIF_CMDT_RESET:
|
case trxcon::TRXCON_PHYIF_CMDT_RESET:
|
||||||
set_ta(0);
|
|
||||||
break;
|
break;
|
||||||
case trxcon::TRXCON_PHYIF_CMDT_POWERON:
|
case trxcon::TRXCON_PHYIF_CMDT_POWERON:
|
||||||
|
|
||||||
if (!mOn) {
|
if (!mOn) {
|
||||||
|
// start_ms();
|
||||||
set_upper_ready(true);
|
set_upper_ready(true);
|
||||||
mOn = true;
|
mOn = true;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case trxcon::TRXCON_PHYIF_CMDT_POWEROFF:
|
case trxcon::TRXCON_PHYIF_CMDT_POWEROFF:
|
||||||
|
// set_upper_ready(false);
|
||||||
|
set_ta(0);
|
||||||
break;
|
break;
|
||||||
case trxcon::TRXCON_PHYIF_CMDT_MEASURE:
|
case trxcon::TRXCON_PHYIF_CMDT_MEASURE:
|
||||||
r.type = trxcon::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(trxcon::gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 0) * 1000 * 100);
|
tuneRx(trxcon::gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 0) * 1000 * 100);
|
||||||
tuneTx(trxcon::gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 1) * 1000 * 100);
|
tuneTx(trxcon::gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 1) * 1000 * 100);
|
||||||
trxcon::cmdq_from_phy.spsc_push(&r);
|
trxcon::cmdq_from_phy.spsc_push(&r);
|
||||||
break;
|
break;
|
||||||
case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H0:
|
case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H0:
|
||||||
|
// gsm_arfcn2band_rc(uint16_t arfcn, enum gsm_band *band)
|
||||||
tuneRx(trxcon::gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 0) * 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);
|
tuneTx(trxcon::gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 1) * 1000 * 100);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H1:
|
case trxcon::TRXCON_PHYIF_CMDT_SETFREQ_H1:
|
||||||
break;
|
break;
|
||||||
@@ -398,23 +382,18 @@ int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon::trxcon_phyif
|
|||||||
{
|
{
|
||||||
if (br->burst_len == 0) // dummy/nope
|
if (br->burst_len == 0) // dummy/nope
|
||||||
return 0;
|
return 0;
|
||||||
OSMO_ASSERT(br->burst != 0);
|
assert(br->burst != 0);
|
||||||
|
|
||||||
trxcon::internal_q_tx_buf b;
|
trxcon::internal_q_tx_buf b;
|
||||||
b.r = *br;
|
b.r = *br;
|
||||||
memcpy(b.buf, (void *)br->burst, br->burst_len);
|
memcpy(b.buf, (void *)br->burst, br->burst_len);
|
||||||
|
|
||||||
if (!g_exit_flag)
|
|
||||||
trxcon::txq.spsc_push(&b);
|
trxcon::txq.spsc_push(&b);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon::trxcon_phyif_cmd *cmd)
|
int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon::trxcon_phyif_cmd *cmd)
|
||||||
{
|
{
|
||||||
#ifdef TXDEBUG
|
|
||||||
DBGLG() << "TOP C: " << cmd2str(cmd->type) << std::endl;
|
DBGLG() << "TOP C: " << cmd2str(cmd->type) << std::endl;
|
||||||
#endif
|
|
||||||
if (!g_exit_flag)
|
|
||||||
trxcon::cmdq_to_phy.spsc_push(cmd);
|
trxcon::cmdq_to_phy.spsc_push(cmd);
|
||||||
// q for resp polling happens in main loop
|
// q for resp polling happens in main loop
|
||||||
return 0;
|
return 0;
|
||||||
@@ -441,37 +420,17 @@ int trxcon_l1ctl_send(struct trxcon::trxcon_inst *trxcon, struct trxcon::msgb *m
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void sighandler(int sigset)
|
|
||||||
{
|
|
||||||
// we might get a sigpipe in case the l1ctl ud socket disconnects because mobile quits
|
|
||||||
if (sigset == SIGPIPE) {
|
|
||||||
g_exit_flag = true;
|
|
||||||
|
|
||||||
// we know the flag is atomic and it prevents the trxcon cb handlers from writing
|
|
||||||
// to the queues, so submit some trash to unblock the threads & exit
|
|
||||||
trxcon::trxcon_phyif_cmd cmd = {};
|
|
||||||
trxcon::internal_q_tx_buf b = {};
|
|
||||||
trxcon::txq.spsc_push(&b);
|
|
||||||
trxcon::cmdq_to_phy.spsc_push(&cmd);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
fesetround(FE_TOWARDZERO);
|
|
||||||
|
|
||||||
trxcon::msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
|
trxcon::msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
|
||||||
trxc_log_init(tall_trxcon_ctx);
|
trxcon::trxc_log_init(tall_trxcon_ctx);
|
||||||
|
|
||||||
trxcon::g_trxcon = trxcon::trxcon_inst_alloc(tall_trxcon_ctx, 0, 0);
|
trxcon::g_trxcon = trxcon::trxcon_inst_alloc(tall_trxcon_ctx, 0, 3);
|
||||||
trxcon::g_trxcon->gsmtap = nullptr;
|
trxcon::g_trxcon->gsmtap = 0;
|
||||||
trxcon::g_trxcon->phyif = nullptr;
|
trxcon::g_trxcon->phyif = (void *)0x1234;
|
||||||
trxcon::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();
|
||||||
@@ -482,19 +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) {
|
|
||||||
std::cerr << "Error initializing hardware, quitting.." << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
trx->set_name_aff_sched("main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5);
|
trx->set_name_aff_sched("main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5);
|
||||||
|
|
||||||
if (!trxcon::trxc_l1ctl_init(tall_trxcon_ctx)) {
|
trxcon::trxc_l1ctl_init(tall_trxcon_ctx);
|
||||||
std::cerr << "Error initializing l1ctl, quitting.." << std::endl;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
|
|
||||||
trx->start_threads();
|
trx->start_threads();
|
||||||
trx->stop_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
|
||||||
@@ -27,6 +29,13 @@
|
|||||||
#include "radioClock.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 {
|
||||||
bool mOn;
|
bool mOn;
|
||||||
char demodded_softbits[444];
|
char demodded_softbits[444];
|
||||||
@@ -36,7 +45,7 @@ class upper_trx : public ms_trx {
|
|||||||
void driveReceiveFIFO();
|
void driveReceiveFIFO();
|
||||||
void driveTx();
|
void driveTx();
|
||||||
|
|
||||||
bool pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
|
bool pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset) __attribute__((optnone));
|
||||||
|
|
||||||
std::thread thr_control, thr_rx, thr_tx;
|
std::thread thr_control, thr_rx, thr_tx;
|
||||||
|
|
||||||
|
|||||||
@@ -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,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
|
||||||
@@ -76,18 +77,19 @@ template <typename T> 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_me_flag;
|
volatile bool stop_me_flag;
|
||||||
|
|
||||||
virtual ~uhd_hw()
|
virtual ~uhd_hw()
|
||||||
{
|
{
|
||||||
delete[] one_pkt_buf;
|
delete[] one_pkt_buf;
|
||||||
}
|
}
|
||||||
uhd_hw() : rxFullScale(32767), txFullScale(32767), rxtxdelay(-67)
|
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)
|
||||||
@@ -133,7 +135,6 @@ template <typename T> 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";
|
||||||
@@ -141,8 +142,7 @@ template <typename T> 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;
|
||||||
@@ -158,18 +158,8 @@ template <typename T> 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);
|
||||||
@@ -186,7 +176,7 @@ template <typename T> struct uhd_hw {
|
|||||||
|
|
||||||
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;
|
||||||
|
|||||||
@@ -655,7 +655,7 @@ int main(int argc, char *argv[])
|
|||||||
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);
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
36
configure.ac
36
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.8.0)
|
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.8.0)
|
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.8.0)
|
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
|
||||||
PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.8.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,20 +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])
|
|
||||||
AC_SUBST(LIBTRXCON_DIR, "osmocom-bb/src/host/trxcon")
|
|
||||||
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)
|
||||||
])
|
])
|
||||||
@@ -279,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)
|
||||||
@@ -347,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([\
|
||||||
@@ -364,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.8.0
|
BuildRequires: pkgconfig(libosmocoding) >= 1.6.0
|
||||||
BuildRequires: pkgconfig(libosmocore) >= 1.8.0
|
BuildRequires: pkgconfig(libosmocore) >= 1.6.0
|
||||||
BuildRequires: pkgconfig(libosmoctrl) >= 1.8.0
|
BuildRequires: pkgconfig(libosmoctrl) >= 1.6.0
|
||||||
BuildRequires: pkgconfig(libosmovty) >= 1.8.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}
|
||||||
|
|||||||
43
debian/changelog
vendored
43
debian/changelog
vendored
@@ -1,46 +1,3 @@
|
|||||||
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/control
vendored
2
debian/control
vendored
@@ -14,7 +14,7 @@ Build-Depends: debhelper (>= 9),
|
|||||||
libtalloc-dev,
|
libtalloc-dev,
|
||||||
libusrp-dev,
|
libusrp-dev,
|
||||||
liblimesuite-dev,
|
liblimesuite-dev,
|
||||||
libosmocore-dev (>= 1.8.0),
|
libosmocore-dev (>= 1.6.0),
|
||||||
osmo-gsm-manuals-dev
|
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
|
||||||
|
|||||||
Submodule osmocom-bb updated: 040bf41028...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;
|
||||||
|
|||||||
Reference in New Issue
Block a user