Compare commits

..

4 Commits

Author SHA1 Message Date
Eric
a0e1ed3215 ms-trx support
This is basically a trxcon that includes a transceiver, and can just
be used with existing and future apps supporting the trxcon interface,
i.e. mobile or ccch_scan.

Supports bladerf and uhd, binaries are automatically built if those libs
are detected + the osmocom-bb submodule exists.
Currently using hardcoded sched/prios aimed at a setup with working,
reliable usb and reserved cores, for example a raspi 4 (ONLY 4, not 3,
not 2, not any other version)

Additionally builds test tools used for development: osmo-trx-syncthing*

Change-Id: I36c65a8c725c4da76dc70006cd96b0a2b6878e84
2022-12-01 12:00:04 +01:00
Eric
a5439daaf7 vita demod by piotr krysik, modified
Had a few rounds of extensive cleanup. Uses gcc multiversioning for x86
targets.

Change-Id: I5466c522cf4de984a4810ec46df43a10b52ed78f
2022-12-01 12:00:04 +01:00
Eric
42cc715fac ill-fated ipcv2 for mstrx
The problem here is that the ipc if requires reducing the clock ind
inteval to a few fn/ts on the trx side to work at all, but scheduling
still does not work out, unless you start driving the IF using the tx
side, at which point the approach is useless, because it does not really
do more than a burst loopback that is easier to do with the higher
layers/interfaces.

The code is still useful should there be a reason to continue working on
this.

Change-Id: I8f582c7c06fed8d1dcc5ea52472a97dc313fdde5
2022-12-01 12:00:04 +01:00
Eric
008418327b bladerf xa4 support
This is not really finished, there are multiple reasons to not use this:
1) main clock is not a gsm multiple, so it will continously drift
2) small buffer sizes lead to tx gaps that are hard to detect and break
everything.

Change-Id: I455c34bb9520d5f09eeb1ac76fceb4bdea94d1ac
2022-12-01 12:00:04 +01:00
53 changed files with 2890 additions and 1307 deletions

View File

@@ -1,4 +0,0 @@
--exclude osmocom-bb/.*
--exclude .*h
--exclude Transceiver52M/grgsm_vitac/.*
--ignore FUNCTION_WITHOUT_ARGS

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View 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_

View 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

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

View 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

View File

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

View File

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

View File

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

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

View File

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