aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.checkpatch.conf5
-rw-r--r--.clang-format4
-rw-r--r--.github/FUNDING.yml1
-rw-r--r--.gitignore18
-rw-r--r--.gitmodules3
-rw-r--r--CommonLibs/Interthread.h4
-rw-r--r--CommonLibs/Logger.h10
-rw-r--r--CommonLibs/Makefile.am11
-rw-r--r--CommonLibs/PRBS.h4
-rw-r--r--CommonLibs/Threads.cpp75
-rw-r--r--CommonLibs/Threads.h161
-rw-r--r--CommonLibs/Utils.cpp4
-rw-r--r--CommonLibs/Utils.h4
-rw-r--r--CommonLibs/config_defs.h13
-rw-r--r--CommonLibs/debug.c24
-rw-r--r--CommonLibs/debug.h9
-rw-r--r--CommonLibs/trx_rate_ctr.cpp40
-rw-r--r--CommonLibs/trx_vty.c189
-rw-r--r--GSM/GSMCommon.cpp3
-rw-r--r--GSM/GSMCommon.h5
-rw-r--r--Makefile.am21
-rw-r--r--README.md42
-rw-r--r--TODO-RELEASE2
-rw-r--r--Transceiver52M/ChannelizerBase.cpp1
-rw-r--r--Transceiver52M/Complex.h2
-rw-r--r--Transceiver52M/Makefile.am78
-rw-r--r--Transceiver52M/Resampler.cpp6
-rw-r--r--Transceiver52M/Resampler.h4
-rw-r--r--Transceiver52M/Transceiver.cpp104
-rw-r--r--Transceiver52M/Transceiver.h7
-rw-r--r--Transceiver52M/arch/arm/convert.c4
-rw-r--r--Transceiver52M/arch/arm/convert_neon.S4
-rw-r--r--Transceiver52M/arch/arm/convolve.c4
-rw-r--r--Transceiver52M/arch/arm/convolve_neon.S4
-rw-r--r--Transceiver52M/arch/arm/mult.c4
-rw-r--r--Transceiver52M/arch/arm/mult_neon.S4
-rw-r--r--Transceiver52M/arch/arm/scale.c4
-rw-r--r--Transceiver52M/arch/arm/scale_neon.S4
-rw-r--r--Transceiver52M/arch/common/convert_base.c4
-rw-r--r--Transceiver52M/arch/common/convolve_base.c4
-rw-r--r--Transceiver52M/arch/x86/convert.c4
-rw-r--r--Transceiver52M/arch/x86/convert_sse_3.c4
-rw-r--r--Transceiver52M/arch/x86/convert_sse_3.h4
-rw-r--r--Transceiver52M/arch/x86/convert_sse_4_1.c4
-rw-r--r--Transceiver52M/arch/x86/convert_sse_4_1.h4
-rw-r--r--Transceiver52M/arch/x86/convolve.c4
-rw-r--r--Transceiver52M/arch/x86/convolve_sse_3.c4
-rw-r--r--Transceiver52M/arch/x86/convolve_sse_3.h4
-rw-r--r--Transceiver52M/device/Makefile.am4
-rw-r--r--Transceiver52M/device/bladerf/Makefile.am11
-rw-r--r--Transceiver52M/device/bladerf/bladerf.cpp611
-rw-r--r--Transceiver52M/device/bladerf/bladerf.h195
-rw-r--r--Transceiver52M/device/common/Makefile.am2
-rw-r--r--Transceiver52M/device/common/bandmanager.h137
-rw-r--r--Transceiver52M/device/common/radioDevice.h49
-rw-r--r--Transceiver52M/device/ipc/IPCDevice.cpp60
-rw-r--r--Transceiver52M/device/ipc/IPCDevice.h7
-rw-r--r--Transceiver52M/device/ipc/Makefile.am33
-rw-r--r--Transceiver52M/device/ipc/ipc-driver-test.c31
-rw-r--r--Transceiver52M/device/ipc/ipc_chan.c8
-rw-r--r--Transceiver52M/device/ipc/ipc_sock.c12
-rw-r--r--Transceiver52M/device/ipc/shm.h2
-rw-r--r--Transceiver52M/device/ipc/uhdwrap.cpp34
-rw-r--r--Transceiver52M/device/ipc/uhdwrap.h2
-rw-r--r--Transceiver52M/device/lms/LMSDevice.cpp146
-rw-r--r--Transceiver52M/device/lms/LMSDevice.h61
-rw-r--r--Transceiver52M/device/uhd/UHDDevice.cpp177
-rw-r--r--Transceiver52M/device/uhd/UHDDevice.h44
-rw-r--r--Transceiver52M/device/usrp1/USRPDevice.cpp42
-rw-r--r--Transceiver52M/device/usrp1/USRPDevice.h21
-rw-r--r--Transceiver52M/grgsm_vitac/constants.h149
-rw-r--r--Transceiver52M/grgsm_vitac/grgsm_vitac.cpp305
-rw-r--r--Transceiver52M/grgsm_vitac/grgsm_vitac.h89
-rw-r--r--Transceiver52M/grgsm_vitac/viterbi_detector.cc392
-rw-r--r--Transceiver52M/grgsm_vitac/viterbi_detector.h64
-rw-r--r--Transceiver52M/ms/bladerf_specific.h479
-rw-r--r--Transceiver52M/ms/itrq.h249
-rw-r--r--Transceiver52M/ms/l1ctl_server.c275
-rw-r--r--Transceiver52M/ms/l1ctl_server_cb.cpp71
-rw-r--r--Transceiver52M/ms/logging.c98
-rw-r--r--Transceiver52M/ms/ms.cpp160
-rw-r--r--Transceiver52M/ms/ms.h301
-rw-r--r--Transceiver52M/ms/ms_rx_lower.cpp420
-rw-r--r--Transceiver52M/ms/ms_trxcon_if.cpp78
-rw-r--r--Transceiver52M/ms/ms_trxcon_if.h42
-rw-r--r--Transceiver52M/ms/ms_upper.cpp512
-rw-r--r--Transceiver52M/ms/ms_upper.h48
-rw-r--r--Transceiver52M/ms/sch.c329
-rw-r--r--Transceiver52M/ms/sch.h48
-rw-r--r--Transceiver52M/ms/threadpool.h95
-rw-r--r--Transceiver52M/ms/threadsched.cpp104
-rw-r--r--Transceiver52M/ms/threadsched.h68
-rw-r--r--Transceiver52M/ms/uhd_specific.h279
-rw-r--r--Transceiver52M/osmo-trx.cpp135
-rw-r--r--Transceiver52M/proto_trxd.h10
-rw-r--r--Transceiver52M/radioBuffer.cpp3
-rw-r--r--Transceiver52M/radioInterface.cpp16
-rw-r--r--Transceiver52M/radioInterface.h13
-rw-r--r--Transceiver52M/radioInterfaceMulti.cpp32
-rw-r--r--Transceiver52M/radioInterfaceResamp.cpp9
-rw-r--r--Transceiver52M/radioVector.cpp9
-rw-r--r--Transceiver52M/radioVector.h4
-rw-r--r--Transceiver52M/sigProcLib.cpp343
-rw-r--r--Transceiver52M/sigProcLib.h18
-rw-r--r--configure.ac72
-rwxr-xr-xcontrib/jenkins.sh26
-rw-r--r--contrib/osmo-trx.spec.in22
-rw-r--r--contrib/systemd/osmo-trx-ipc.service9
-rw-r--r--contrib/systemd/osmo-trx-lms.service9
-rw-r--r--contrib/systemd/osmo-trx-uhd.service10
-rw-r--r--contrib/systemd/osmo-trx-usrp1.service9
-rw-r--r--debian/changelog322
-rw-r--r--debian/compat2
-rw-r--r--debian/control30
-rw-r--r--debian/copyright2
-rw-r--r--debian/osmo-trx-ms-blade.install1
-rw-r--r--debian/patches/build-for-debian8.patch60
-rw-r--r--debian/patches/series1
-rwxr-xr-xdebian/rules11
-rw-r--r--doc/examples/Makefile.am13
-rw-r--r--doc/examples/osmo-trx-ipc/osmo-trx-ipc.cfg6
-rw-r--r--doc/examples/osmo-trx-lms/osmo-trx-limesdr.cfg6
-rw-r--r--doc/examples/osmo-trx-uhd/osmo-trx-limesdr.cfg6
-rw-r--r--doc/examples/osmo-trx-uhd/osmo-trx-umtrx.cfg6
-rw-r--r--doc/examples/osmo-trx-uhd/osmo-trx-usrp_b200.cfg6
-rw-r--r--doc/manuals/Makefile.am21
-rw-r--r--doc/manuals/chapters/code-architecture.adoc4
-rw-r--r--doc/manuals/chapters/configuration.adoc2
-rw-r--r--doc/manuals/chapters/ipc_if.adoc301
-rw-r--r--doc/manuals/chapters/trx-backends.adoc100
-rw-r--r--doc/manuals/osmotrx-usermanual.adoc2
-rw-r--r--doc/manuals/vty/Makefile.vty-reference.inc37
-rw-r--r--doc/manuals/vty/osmotrx-vty-reference.xml (renamed from doc/manuals/osmotrx-vty-reference.xml)11
-rw-r--r--doc/manuals/vty/trx_vty_reference.xml1406
m---------osmocom-bb0
-rw-r--r--tests/CommonLibs/InterthreadTest.cpp6
-rw-r--r--tests/CommonLibs/LogTest.cpp4
-rw-r--r--tests/CommonLibs/Makefile.am31
-rw-r--r--tests/CommonLibs/PRBSTest.cpp4
-rw-r--r--tests/Transceiver52M/Makefile.am20
-rw-r--r--utils/Makefile.am2
-rw-r--r--utils/va-test/Makefile.am17
-rw-r--r--utils/va-test/burst-gen.cpp531
-rw-r--r--utils/va-test/demodbits_tsc7.s8bin0 -> 148 bytes
-rw-r--r--utils/va-test/nb_chunk_tsc7.cfilebin0 -> 12000 bytes
145 files changed, 8479 insertions, 2511 deletions
diff --git a/.checkpatch.conf b/.checkpatch.conf
new file mode 100644
index 0000000..1699801
--- /dev/null
+++ b/.checkpatch.conf
@@ -0,0 +1,5 @@
+--exclude osmocom-bb/.*
+--exclude .*h
+--exclude Transceiver52M/grgsm_vitac/.*
+--exclude utils/va-test/.*
+--ignore FUNCTION_WITHOUT_ARGS \ No newline at end of file
diff --git a/.clang-format b/.clang-format
index a11c325..4de65a5 100644
--- a/.clang-format
+++ b/.clang-format
@@ -25,7 +25,7 @@ AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
-AlwaysBreakTemplateDeclarations: false
+AlwaysBreakTemplateDeclarations: true
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
@@ -515,7 +515,7 @@ SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
-Standard: Cpp03
+Standard: Cpp11
TabWidth: 8
UseTab: Always
...
diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 0000000..7592deb
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1 @@
+open_collective: osmocom
diff --git a/.gitignore b/.gitignore
index 4baa72a..fc567db 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,6 +6,15 @@ Transceiver52M/osmo-trx-uhd
Transceiver52M/osmo-trx-usrp1
Transceiver52M/osmo-trx-lms
Transceiver52M/osmo-trx-ipc
+Transceiver52M/osmo-trx-blade
+Transceiver52M/osmo-trx-ipc2
+Transceiver52M/osmo-trx-syncthing-blade
+Transceiver52M/osmo-trx-syncthing-uhd
+Transceiver52M/osmo-trx-syncthing-ipc
+Transceiver52M/osmo-trx-ms-blade
+Transceiver52M/osmo-trx-ms-uhd
+Transceiver52M/osmo-trx-ms-ipc
+Transceiver52M/device/ipc/uhddev_ipc.cpp
.clang-format
@@ -29,6 +38,7 @@ Transceiver52M/device/ipc/ipc-driver-test
.deps
.libs
.dirstamp
+.version
*~
Makefile
config.log
@@ -64,7 +74,9 @@ doc/manuals/*.pdf
doc/manuals/*__*.png
doc/manuals/*.check
doc/manuals/generated/
-doc/manuals/osmomsc-usermanual.xml
+doc/manuals/vty/osmotrx-*-vty-reference.xml
+doc/manuals/vty/osmotrx-*-vty-reference.xml.inc.gen
+doc/manuals/vty/osmotrx-*-vty-reference.xml.inc.merged
doc/manuals/common
doc/manuals/build
@@ -72,3 +84,7 @@ contrib/osmo-trx.spec
!contrib/osmo-trx.spec.in
utils/osmo-prbs-tool
+utils/va-test/osmo-burst-gen
+/.qtc_clangd/*
+/.cache/*
+/.vscode/*
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..99afef8
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "osmocom-bb"]
+ path = osmocom-bb
+ url = https://gitea.osmocom.org/phone-side/osmocom-bb.git
diff --git a/CommonLibs/Interthread.h b/CommonLibs/Interthread.h
index 881e1a8..207ada2 100644
--- a/CommonLibs/Interthread.h
+++ b/CommonLibs/Interthread.h
@@ -517,7 +517,7 @@ public:
@param timeout The blocking timeout in ms.
@return Pointer at key or NULL on timeout.
*/
- D* read(const K &key, unsigned timeout) const
+ D* read(const K &key, unsigned timeout)
{
if (timeout==0) return readNoBlock(key);
ScopedLock lock(mLock);
@@ -537,7 +537,7 @@ public:
@param key The key to read from.
@return Pointer at key.
*/
- D* read(const K &key) const
+ D* read(const K &key)
{
ScopedLock lock(mLock);
typename Map::const_iterator iter = mMap.find(key);
diff --git a/CommonLibs/Logger.h b/CommonLibs/Logger.h
index b752e51..6452e25 100644
--- a/CommonLibs/Logger.h
+++ b/CommonLibs/Logger.h
@@ -50,19 +50,19 @@ extern "C" {
#endif
#define LOG(level) \
- Log(DMAIN, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
+ Log(DMAIN, LOGL_##level, __BASE_FILE__, __LINE__).get()
#define LOGC(category, level) \
- Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
+ Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get()
#define LOGLV(category, level) \
- Log(category, level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
+ Log(category, level, __BASE_FILE__, __LINE__).get()
#define LOGSRC(category, level, file, line) \
- Log(category, level, file, line).get() << "[tid=" << pthread_self() << "] "
+ Log(category, level, file, line).get()
#define LOGCHAN(chan, category, level) \
- Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "][chan=" << chan << "] "
+ Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[chan=" << chan << "] "
/**
A C++ stream-based thread-safe logger.
diff --git a/CommonLibs/Makefile.am b/CommonLibs/Makefile.am
index 2c449e0..4a0652d 100644
--- a/CommonLibs/Makefile.am
+++ b/CommonLibs/Makefile.am
@@ -22,8 +22,8 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
-AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
-AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
+AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
+AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
noinst_LTLIBRARIES = libcommon.la
@@ -37,7 +37,12 @@ libcommon_la_SOURCES = \
trx_rate_ctr.cpp \
trx_vty.c \
debug.c
-libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
+libcommon_la_LIBADD = \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOCTRL_LIBS) \
+ $(LIBOSMOVTY_LIBS) \
+ -lpthread \
+ $(NULL)
noinst_HEADERS = \
BitVector.h \
diff --git a/CommonLibs/PRBS.h b/CommonLibs/PRBS.h
index 9235327..8d08fa3 100644
--- a/CommonLibs/PRBS.h
+++ b/CommonLibs/PRBS.h
@@ -12,10 +12,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef PRBS_H
diff --git a/CommonLibs/Threads.cpp b/CommonLibs/Threads.cpp
index 020d94e..377a1b0 100644
--- a/CommonLibs/Threads.cpp
+++ b/CommonLibs/Threads.cpp
@@ -32,11 +32,9 @@
#include "Timeval.h"
#include "Logger.h"
-#ifndef gettid
-#include <sys/syscall.h>
-#define gettid() syscall(SYS_gettid)
-#endif
-
+extern "C" {
+#include <osmocom/core/thread.h>
+}
using namespace std;
@@ -45,76 +43,11 @@ using namespace std;
#endif
-Mutex gStreamLock; ///< Global lock to control access to cout and cerr.
-
-void lockCout()
-{
- gStreamLock.lock();
- Timeval entryTime;
- cout << entryTime << " " << pthread_self() << ": ";
-}
-
-
-void unlockCout()
-{
- cout << dec << endl << flush;
- gStreamLock.unlock();
-}
-
-
-void lockCerr()
-{
- gStreamLock.lock();
- Timeval entryTime;
- cerr << entryTime << " " << pthread_self() << ": ";
-}
-
-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)
{
pthread_t selfid = pthread_self();
- pid_t tid = gettid();
+ pid_t tid = osmo_gettid();
if (pthread_setname_np(selfid, name) == 0) {
LOG(INFO) << "Thread "<< selfid << " (task " << tid << ") set name: " << name;
} else {
diff --git a/CommonLibs/Threads.h b/CommonLibs/Threads.h
index 5ff137b..24fec00 100644
--- a/CommonLibs/Threads.h
+++ b/CommonLibs/Threads.h
@@ -28,143 +28,96 @@
#ifndef THREADS_H
#define THREADS_H
-#include "config.h"
-
+#include <chrono>
+#include <mutex>
+#include <condition_variable>
#include <pthread.h>
#include <iostream>
-#include <assert.h>
+#include <cassert>
#include <unistd.h>
-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
-//@}
-//@}
-
+#include "config.h"
+#include "Timeval.h"
+class Mutex;
/**@defgroup C++ wrappers for pthread mechanisms. */
//@{
-/** A class for recursive mutexes based on pthread_mutex. */
+/** A class for recursive mutexes. */
class Mutex {
+ std::recursive_mutex m;
- private:
+ public:
- pthread_mutex_t mMutex;
- pthread_mutexattr_t mAttribs;
-
- public:
-
- Mutex();
-
- ~Mutex();
-
- void lock() { pthread_mutex_lock(&mMutex); }
+ void lock() {
+ m.lock();
+ }
- bool trylock() { return pthread_mutex_trylock(&mMutex)==0; }
+ bool trylock() {
+ return m.try_lock();
+ }
- void unlock() { pthread_mutex_unlock(&mMutex); }
+ void unlock() {
+ m.unlock();
+ }
friend class Signal;
-
};
-
class ScopedLock {
+ Mutex &mMutex;
- private:
- Mutex& mMutex;
-
- public:
- ScopedLock(Mutex& wMutex) :mMutex(wMutex) { mMutex.lock(); }
- ~ScopedLock() { mMutex.unlock(); }
-
+ public:
+ ScopedLock(Mutex &wMutex) : mMutex(wMutex) {
+ mMutex.lock();
+ }
+ ~ScopedLock() {
+ mMutex.unlock();
+ }
};
-
-
-
-/** A C++ interthread signal based on pthread condition variables. */
+/** A C++ interthread signal. */
class Signal {
+ /* any, because for some reason our mutex is recursive... */
+ std::condition_variable_any mSignal;
- private:
-
- mutable pthread_cond_t mSignal;
-
- public:
-
- Signal() { int s = pthread_cond_init(&mSignal,NULL); assert(!s); }
-
- ~Signal() { pthread_cond_destroy(&mSignal); }
+ public:
- /**
- 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 wait(Mutex &wMutex, unsigned timeout) {
+ mSignal.wait_for(wMutex.m, std::chrono::milliseconds(timeout));
+ }
- void signal() { pthread_cond_signal(&mSignal); }
+ void wait(Mutex &wMutex) {
+ mSignal.wait(wMutex.m);
+ }
- void broadcast() { pthread_cond_broadcast(&mSignal); }
+ void signal() {
+ mSignal.notify_one();
+ }
+ 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 thread_enable_cancel(bool cancel);
/** A C++ wrapper for pthread threads. */
class Thread {
-
- private:
-
+ private:
pthread_t mThread;
pthread_attr_t mAttrib;
// FIXME -- Can this be reduced now?
size_t mStackSize;
-
- public:
-
+ public:
/** Create a thread in a non-running state. */
- Thread(size_t wStackSize = 0):mThread((pthread_t)0) {
- pthread_attr_init(&mAttrib); // (pat) moved this here.
- mStackSize=wStackSize;
+ Thread(size_t wStackSize = 0) : mThread((pthread_t)0)
+ {
+ pthread_attr_init(&mAttrib); // (pat) moved this here.
+ mStackSize = wStackSize;
}
/**
@@ -172,14 +125,17 @@ class Thread {
It should be stopped and joined.
*/
// (pat) If the Thread is destroyed without being started, then mAttrib is undefined. Oops.
- ~Thread() { pthread_attr_destroy(&mAttrib); }
-
+ ~Thread()
+ {
+ pthread_attr_destroy(&mAttrib);
+ }
/** 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. */
- void join() {
+ void join()
+ {
if (mThread) {
int s = pthread_join(mThread, NULL);
assert(!s);
@@ -187,7 +143,10 @@ class Thread {
}
/** Send cancellation to thread */
- void cancel() { pthread_cancel(mThread); }
+ void cancel()
+ {
+ pthread_cancel(mThread);
+ }
};
#ifdef HAVE_ATOMIC_OPS
diff --git a/CommonLibs/Utils.cpp b/CommonLibs/Utils.cpp
index e500475..3ab598d 100644
--- a/CommonLibs/Utils.cpp
+++ b/CommonLibs/Utils.cpp
@@ -12,10 +12,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <vector>
diff --git a/CommonLibs/Utils.h b/CommonLibs/Utils.h
index 3c5c802..8e61a9f 100644
--- a/CommonLibs/Utils.h
+++ b/CommonLibs/Utils.h
@@ -12,10 +12,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
diff --git a/CommonLibs/config_defs.h b/CommonLibs/config_defs.h
index a9ed25e..07a3981 100644
--- a/CommonLibs/config_defs.h
+++ b/CommonLibs/config_defs.h
@@ -46,7 +46,9 @@ struct trx_cfg {
enum FillerType filler;
bool multi_arfcn;
double offset;
+ double freq_offset_khz;
double rssi_offset;
+ int ul_fn_offset;
bool force_rssi_offset; /* Force value set in VTY? */
bool swap_channels;
bool ext_rach;
@@ -55,4 +57,15 @@ struct trx_cfg {
unsigned int stack_size;
unsigned int num_chans;
struct trx_chan chans[TRX_CHAN_MAX];
+ struct {
+ bool ul_freq_override;
+ bool dl_freq_override;
+ bool ul_gain_override;
+ bool dl_gain_override;
+ double ul_freq;
+ double dl_freq;
+ double ul_gain;
+ double dl_gain;
+ } overrides;
+ bool use_va;
};
diff --git a/CommonLibs/debug.c b/CommonLibs/debug.c
index 7ee8184..08954f5 100644
--- a/CommonLibs/debug.c
+++ b/CommonLibs/debug.c
@@ -21,18 +21,6 @@
* See the COPYING file in the main directory for details.
*/
-#include "config.h"
-
-/* If HAVE_GETTID, then "_GNU_SOURCE" may need to be defined to use gettid() */
-#if HAVE_GETTID
-#define _GNU_SOURCE
-#endif
-
-#include <sys/types.h>
-#include <unistd.h>
-#include <sys/syscall.h>
-#include "config.h"
-
#include <osmocom/core/logging.h>
#include <osmocom/core/utils.h>
#include "debug.h"
@@ -93,15 +81,3 @@ const struct log_info log_info = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
};
-
-pid_t my_gettid(void)
-{
-#if HAVE_GETTID
- return gettid();
-#elif defined(LINUX) && defined(__NR_gettid)
- return (pid_t) syscall(__NR_gettid);
-#else
- #pragma message ("use pid as tid")
- return getpid();
-#endif
-}
diff --git a/CommonLibs/debug.h b/CommonLibs/debug.h
index dd78168..71f344e 100644
--- a/CommonLibs/debug.h
+++ b/CommonLibs/debug.h
@@ -1,7 +1,6 @@
#pragma once
#include <stdbool.h>
-#include <sys/types.h>
#include <osmocom/core/logging.h>
@@ -19,12 +18,6 @@ enum {
DCTR,
};
-pid_t my_gettid(void);
-
-#define CLOGC(category, level, fmt, args...) do { \
- LOGP(category, level, "[tid=%ld] " fmt, (long int) my_gettid(), ##args); \
-} while(0)
-
#define CLOGCHAN(chan, category, level, fmt, args...) do { \
- LOGP(category, level, "[tid=%ld][chan=%zu] " fmt, (long int) my_gettid(), chan, ##args); \
+ LOGP(category, level, "[chan=%zu] " fmt, chan, ##args); \
} while(0)
diff --git a/CommonLibs/trx_rate_ctr.cpp b/CommonLibs/trx_rate_ctr.cpp
index e902ff1..3806268 100644
--- a/CommonLibs/trx_rate_ctr.cpp
+++ b/CommonLibs/trx_rate_ctr.cpp
@@ -147,17 +147,17 @@ static int dev_rate_ctr_timerfd_cb(struct osmo_fd *ofd, unsigned int what) {
if (dev_ctrs_pending[chan].chan == PENDING_CHAN_NONE)
continue;
LOGCHAN(chan, DCTR, DEBUG) << "rate_ctr update";
- ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_RX_OVERRUNS];
+ ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_RX_OVERRUNS);
rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_overruns - ctr->current);
- ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_TX_UNDERRUNS];
+ ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_TX_UNDERRUNS);
rate_ctr_add(ctr, dev_ctrs_pending[chan].tx_underruns - ctr->current);
- ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_RX_DROP_EV];
+ ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_RX_DROP_EV);
rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_dropped_events - ctr->current);
- ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_RX_DROP_SMPL];
+ ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_RX_DROP_SMPL);
rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_dropped_samples - ctr->current);
- ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_TX_DROP_EV];
+ ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_TX_DROP_EV);
rate_ctr_add(ctr, dev_ctrs_pending[chan].tx_dropped_events - ctr->current);
- ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_TX_DROP_SMPL];
+ ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_TX_DROP_SMPL);
rate_ctr_add(ctr, dev_ctrs_pending[chan].tx_dropped_samples - ctr->current);
/* Mark as done */
@@ -178,21 +178,21 @@ static int trx_rate_ctr_timerfd_cb(struct osmo_fd *ofd, unsigned int what) {
if (trx_ctrs_pending[chan].chan == PENDING_CHAN_NONE)
continue;
LOGCHAN(chan, DCTR, DEBUG) << "rate_ctr update";
- ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TX_STALE_BURSTS];
+ ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TX_STALE_BURSTS);
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_stale_bursts - ctr->current);
- ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS];
+ ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS);
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_unavailable_bursts - ctr->current);
- ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TRXD_FN_REPEATED];
+ ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TRXD_FN_REPEATED);
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_repeated - ctr->current);
- ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TRXD_FN_OUTOFORDER];
+ ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TRXD_FN_OUTOFORDER);
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_outoforder - ctr->current);
- ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TRXD_FN_SKIPPED];
+ ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TRXD_FN_SKIPPED);
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_skipped - ctr->current);
- ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_RX_EMPTY_BURST];
+ ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_RX_EMPTY_BURST);
rate_ctr_add(ctr, trx_ctrs_pending[chan].rx_empty_burst - ctr->current);
- ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_RX_CLIPPING];
+ ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_RX_CLIPPING);
rate_ctr_add(ctr, trx_ctrs_pending[chan].rx_clipping - ctr->current);
- ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_RX_NO_BURST_DETECTED];
+ ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_RX_NO_BURST_DETECTED);
rate_ctr_add(ctr, trx_ctrs_pending[chan].rx_no_burst_detected - ctr->current);
/* Mark as done */
trx_ctrs_pending[chan].chan = PENDING_CHAN_NONE;
@@ -214,6 +214,7 @@ static int device_sig_cb(unsigned int subsys, unsigned int signal,
struct timespec next_sched = {.tv_sec = 0, .tv_nsec = 20*1000*1000};
/* no automatic re-trigger */
struct timespec intv_sched = {.tv_sec = 0, .tv_nsec = 0};
+ char err_buf[256];
switch (signal) {
case S_DEVICE_COUNTER_CHANGE:
@@ -222,7 +223,8 @@ static int device_sig_cb(unsigned int subsys, unsigned int signal,
dev_rate_ctr_mutex.lock();
dev_ctrs_pending[dev_ctr->chan] = *dev_ctr;
if (osmo_timerfd_schedule(&dev_rate_ctr_timerfd, &next_sched, &intv_sched) < 0) {
- LOGC(DCTR, ERROR) << "Failed to schedule timerfd: " << errno << " = "<< strerror(errno);
+ LOGC(DCTR, ERROR) << "Failed to schedule timerfd: " << errno
+ << " = "<< strerror_r(errno, err_buf, sizeof(err_buf));
}
dev_rate_ctr_mutex.unlock();
break;
@@ -232,7 +234,8 @@ static int device_sig_cb(unsigned int subsys, unsigned int signal,
trx_rate_ctr_mutex.lock();
trx_ctrs_pending[trx_ctr->chan] = *trx_ctr;
if (osmo_timerfd_schedule(&trx_rate_ctr_timerfd, &next_sched, &intv_sched) < 0) {
- LOGC(DCTR, ERROR) << "Failed to schedule timerfd: " << errno << " = "<< strerror(errno);
+ LOGC(DCTR, ERROR) << "Failed to schedule timerfd: " << errno
+ << " = "<< strerror_r(errno, err_buf, sizeof(err_buf));
}
trx_rate_ctr_mutex.unlock();
break;
@@ -263,7 +266,7 @@ static void threshold_timer_cb(void *data)
llist_for_each_entry(ctr_thr, &threshold_list, list) {
for (chan = 0; chan < chan_len; chan++) {
- rate_ctr = &rate_ctrs[chan]->ctr[ctr_thr->ctr_id];
+ rate_ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], ctr_thr->ctr_id);
LOGCHAN(chan, DCTR, INFO) << "checking threshold: " << ctr_threshold_2_vty_str(ctr_thr)
<< " ("<< rate_ctr->intv[ctr_thr->intv].rate << " vs " << ctr_thr->val << ")";
if (rate_ctr->intv[ctr_thr->intv].rate >= ctr_thr->val) {
@@ -309,8 +312,7 @@ static void threshold_timer_update_intv() {
return;
if (llist_empty(&threshold_list)) {
- if (osmo_timer_pending(&threshold_timer))
- osmo_timer_del(&threshold_timer);
+ osmo_timer_del(&threshold_timer);
return;
}
diff --git a/CommonLibs/trx_vty.c b/CommonLibs/trx_vty.c
index fd6faed..bd1b0ce 100644
--- a/CommonLibs/trx_vty.c
+++ b/CommonLibs/trx_vty.c
@@ -67,6 +67,15 @@ static const struct value_string filler_types[] = {
{ 0, NULL }
};
+static const struct value_string filler_docs[] = {
+ { FILLER_DUMMY, "Send a Dummy Burst on C0 (TRX0) and empty burst on other channels" },
+ { FILLER_ZERO, "Send an empty burst (default)" },
+ { FILLER_NORM_RAND, "Send a GMSK modulated Normal Burst with random bits (spectrum mask testing)" },
+ { FILLER_EDGE_RAND, "Send an 8-PSK modulated Normal Burst with random bits (spectrum mask testing)" },
+ { FILLER_ACCESS_RAND, "Send an Access Burst with random bits (Rx/Tx alignment testing)" },
+ { 0, NULL }
+};
+
struct trx_ctx *trx_from_vty(struct vty *v)
{
@@ -112,7 +121,7 @@ DEFUN(cfg_trx, cfg_trx_cmd,
}
DEFUN(cfg_bind_ip, cfg_bind_ip_cmd,
- "bind-ip A.B.C.D",
+ "bind-ip " VTY_IPV4_CMD,
"Set the IP address for the local bind\n"
"IPv4 Address\n")
{
@@ -124,7 +133,7 @@ DEFUN(cfg_bind_ip, cfg_bind_ip_cmd,
}
DEFUN(cfg_remote_ip, cfg_remote_ip_cmd,
- "remote-ip A.B.C.D",
+ "remote-ip " VTY_IPV4_CMD,
"Set the IP address for the remote BTS\n"
"IPv4 Address\n")
{
@@ -162,7 +171,9 @@ DEFUN(cfg_dev_args, cfg_dev_args_cmd,
DEFUN(cfg_tx_sps, cfg_tx_sps_cmd,
"tx-sps (1|4)",
"Set the Tx Samples-per-Symbol\n"
- "Tx Samples-per-Symbol\n")
+ "Tx Samples-per-Symbol\n"
+ "1 Sample-per-Symbol\n"
+ "4 Samples-per-Symbol\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -174,7 +185,9 @@ DEFUN(cfg_tx_sps, cfg_tx_sps_cmd,
DEFUN(cfg_rx_sps, cfg_rx_sps_cmd,
"rx-sps (1|4)",
"Set the Rx Samples-per-Symbol\n"
- "Rx Samples-per-Symbol\n")
+ "Rx Samples-per-Symbol\n"
+ "1 Sample-per-Symbol\n"
+ "4 Samples-per-Symbol\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -199,7 +212,8 @@ DEFUN(cfg_clock_ref, cfg_clock_ref_cmd,
DEFUN(cfg_multi_arfcn, cfg_multi_arfcn_cmd,
"multi-arfcn (disable|enable)",
- "Enable multi-ARFCN transceiver (default=disable)\n")
+ "Multi-ARFCN transceiver mode (default=disable)\n"
+ "Enable multi-ARFCN mode\n" "Disable multi-ARFCN mode\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -230,6 +244,19 @@ DEFUN(cfg_offset, cfg_offset_cmd,
return CMD_SUCCESS;
}
+DEFUN_ATTR(cfg_freq_offset, cfg_freq_offset_cmd,
+ "freq-offset FLOAT",
+ "Apply an artificial offset to Rx/Tx carrier frequency\n"
+ "Frequency offset in kHz (e.g. -145300)\n",
+ CMD_ATTR_HIDDEN)
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx->cfg.freq_offset_khz = atof(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_rssi_offset, cfg_rssi_offset_cmd,
"rssi-offset FLOAT [relative]",
"Set the RSSI to dBm offset in dB (default=0)\n"
@@ -244,9 +271,98 @@ DEFUN(cfg_rssi_offset, cfg_rssi_offset_cmd,
return CMD_SUCCESS;
}
+
+DEFUN_ATTR(cfg_ul_fn_offset, cfg_ul_fn_offset_cmd,
+ "ul-fn-offset <-10-10>",
+ "Adjusts the uplink frame FN by the specified amount\n"
+ "Frame Number offset\n",
+ CMD_ATTR_HIDDEN)
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx->cfg.ul_fn_offset = atoi(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_ul_freq_override, cfg_ul_freq_override_cmd,
+ "ul-freq-override FLOAT",
+ "Overrides Rx carrier frequency\n"
+ "Frequency in Hz (e.g. 145300000)\n",
+ CMD_ATTR_HIDDEN)
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx->cfg.overrides.ul_freq_override = true;
+ trx->cfg.overrides.ul_freq = atof(argv[0]);
+
+ return CMD_SUCCESS;
+}
+DEFUN_ATTR(cfg_dl_freq_override, cfg_dl_freq_override_cmd,
+ "dl-freq-override FLOAT",
+ "Overrides Tx carrier frequency\n"
+ "Frequency in Hz (e.g. 145300000)\n",
+ CMD_ATTR_HIDDEN)
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx->cfg.overrides.dl_freq_override = true;
+ trx->cfg.overrides.dl_freq = atof(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_ul_gain_override, cfg_ul_gain_override_cmd,
+ "ul-gain-override FLOAT",
+ "Overrides Rx gain\n"
+ "gain in dB\n",
+ CMD_ATTR_HIDDEN)
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx->cfg.overrides.ul_gain_override = true;
+ trx->cfg.overrides.ul_gain = atof(argv[0]);
+
+ return CMD_SUCCESS;
+}
+DEFUN_ATTR(cfg_dl_gain_override, cfg_dl_gain_override_cmd,
+ "dl-gain-override FLOAT",
+ "Overrides Tx gain\n"
+ "gain in dB\n",
+ CMD_ATTR_HIDDEN)
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ trx->cfg.overrides.dl_gain_override = true;
+ trx->cfg.overrides.dl_gain = atof(argv[0]);
+
+ return CMD_SUCCESS;
+}
+
+DEFUN_ATTR(cfg_use_viterbi, cfg_use_viterbi_cmd,
+ "viterbi-eq (disable|enable)",
+ "Use viterbi equalizer for gmsk (default=disable)\n"
+ "Disable VA\n"
+ "Enable VA\n",
+ CMD_ATTR_HIDDEN)
+{
+ struct trx_ctx *trx = trx_from_vty(vty);
+
+ if (strcmp("disable", argv[0]) == 0)
+ trx->cfg.use_va = false;
+ else if (strcmp("enable", argv[0]) == 0)
+ trx->cfg.use_va = true;
+ else
+ return CMD_WARNING;
+
+ return CMD_SUCCESS;
+}
+
DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
"swap-channels (disable|enable)",
- "Swap channels (default=disable)\n")
+ "Swap primary and secondary channels of the PHY (if any)\n"
+ "Do not swap primary and secondary channels (default)\n"
+ "Swap primary and secondary channels\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -263,7 +379,9 @@ DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
DEFUN(cfg_egprs, cfg_egprs_cmd,
"egprs (disable|enable)",
- "Enable EDGE receiver (default=disable)\n")
+ "EGPRS (8-PSK demodulation) support (default=disable)\n"
+ "Disable EGPRS (8-PSK demodulation) support\n"
+ "Enable EGPRS (8-PSK demodulation) support\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -280,7 +398,9 @@ DEFUN(cfg_egprs, cfg_egprs_cmd,
DEFUN(cfg_ext_rach, cfg_ext_rach_cmd,
"ext-rach (disable|enable)",
- "Enable extended (11-bit) RACH (default=disable)\n")
+ "11-bit Access Burst correlation support (default=disable)\n"
+ "Disable 11-bit Access Burst (TS1 & TS2) correlation\n"
+ "Enable 11-bit Access Burst (TS1 & TS2) correlation\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -319,20 +439,11 @@ DEFUN(cfg_stack_size, cfg_stack_size_cmd,
return CMD_SUCCESS;
}
-DEFUN(cfg_filler, cfg_filler_type_cmd,
- "filler type (zero|dummy|random-nb-gmsk|random-nb-8psk|random-ab)",
+#define CFG_FILLER_DOC_STR \
"Filler burst settings\n"
- "Filler burst type (default=zero)\n"
- "Send an empty burst when there is nothing to send (default)\n"
- "Send a dummy burst when there is nothing to send on C0 (TRX0) and empty burst on other channels."
- " Use for OpenBTS compatibility only, don't use with OsmoBTS as it breaks encryption.\n"
- "Send a GMSK modulated Normal Burst with random bits when there is nothing to send."
- " Use for spectrum mask testing. Configure 'filler tsc' to set training sequence.\n"
- "Send an 8-PSK modulated Normal Burst with random bits when there is nothing to send."
- " Use for spectrum mask testing. Configure 'filler tsc' to set training sequence.\n"
- "Send an Access Burst with random bits when there is nothing to send. Use for Rx/Tx alignment."
- " Configure 'filler access-burst-delay' to introduce artificial delay.\n"
-)
+
+DEFUN(cfg_filler, cfg_filler_type_cmd,
+ "AUTO-GENERATED", "AUTO-GENERATED")
{
struct trx_ctx *trx = trx_from_vty(vty);
// trx->cfg.filler is unsigned, so we need an interim int var to detect errors
@@ -349,7 +460,7 @@ DEFUN(cfg_filler, cfg_filler_type_cmd,
DEFUN(cfg_test_rtsc, cfg_filler_tsc_cmd,
"filler tsc <0-7>",
- "Filler burst settings\n"
+ CFG_FILLER_DOC_STR
"Set the TSC for GMSK/8-PSK Normal Burst random fillers. Used only with 'random-nb-gmsk' and"
" 'random-nb-8psk' filler types. (default=0)\n"
"TSC\n")
@@ -363,7 +474,7 @@ DEFUN(cfg_test_rtsc, cfg_filler_tsc_cmd,
DEFUN(cfg_test_rach_delay, cfg_filler_rach_delay_cmd,
"filler access-burst-delay <0-68>",
- "Filler burst settings\n"
+ CFG_FILLER_DOC_STR
"Set the delay for Access Burst random fillers. Used only with 'random-ab' filler type. (default=0)\n"
"RACH delay in symbols\n")
{
@@ -428,7 +539,6 @@ DEFUN_ATTR(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,
int rc;
struct ctr_threshold ctr;
- struct trx_ctx *trx = trx_from_vty(vty);
rc = vty_ctr_name_2_id(argv[0]);
if (rc < 0) {
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
@@ -460,7 +570,6 @@ DEFUN_ATTR(cfg_no_ctr_error_threshold, cfg_no_ctr_error_threshold_cmd,
int rc;
struct ctr_threshold ctr;
- struct trx_ctx *trx = trx_from_vty(vty);
rc = vty_ctr_name_2_id(argv[0]);
if (rc < 0) {
vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
@@ -571,7 +680,7 @@ static int config_write_trx(struct vty *vty)
vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
if (trx->cfg.base_port != DEFAULT_TRX_PORT)
vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE);
- if (trx->cfg.dev_args)
+ if (strlen(trx->cfg.dev_args))
vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE);
if (trx->cfg.tx_sps != DEFAULT_TX_SPS)
vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
@@ -582,6 +691,8 @@ static int config_write_trx(struct vty *vty)
vty_out(vty, " multi-arfcn %s%s", trx->cfg.multi_arfcn ? "enable" : "disable", VTY_NEWLINE);
if (trx->cfg.offset != 0)
vty_out(vty, " offset %f%s", trx->cfg.offset, VTY_NEWLINE);
+ if (trx->cfg.freq_offset_khz != 0)
+ vty_out(vty, " freq-offset %f%s", trx->cfg.freq_offset_khz, VTY_NEWLINE);
if (!(trx->cfg.rssi_offset == 0 && !trx->cfg.force_rssi_offset))
vty_out(vty, " rssi-offset %f%s%s", trx->cfg.rssi_offset,
trx->cfg.force_rssi_offset ? " relative": "", VTY_NEWLINE);
@@ -598,6 +709,18 @@ static int config_write_trx(struct vty *vty)
vty_out(vty, " filler access-burst-delay %u%s", trx->cfg.rach_delay, VTY_NEWLINE);
if (trx->cfg.stack_size != 0)
vty_out(vty, " stack-size %u%s", trx->cfg.stack_size, VTY_NEWLINE);
+ if (trx->cfg.ul_fn_offset != 0)
+ vty_out(vty, " ul-fn-offset %d%s", trx->cfg.ul_fn_offset, VTY_NEWLINE);
+ if (trx->cfg.overrides.dl_freq_override)
+ vty_out(vty, " dl-freq-override %f%s", trx->cfg.overrides.dl_freq, VTY_NEWLINE);
+ if (trx->cfg.overrides.ul_freq_override)
+ vty_out(vty, " ul-freq-override %f%s", trx->cfg.overrides.ul_freq, VTY_NEWLINE);
+ if (trx->cfg.overrides.dl_gain_override)
+ vty_out(vty, " dl-gain-override %f%s", trx->cfg.overrides.dl_gain, VTY_NEWLINE);
+ if (trx->cfg.overrides.ul_gain_override)
+ vty_out(vty, " ul-gain-override %f%s", trx->cfg.overrides.ul_gain, VTY_NEWLINE);
+ if (trx->cfg.use_va)
+ vty_out(vty, " viterbi-eq %s%s", trx->cfg.use_va ? "enable" : "disable", VTY_NEWLINE);
trx_rate_ctr_threshold_write_config(vty, " ");
for (i = 0; i < trx->cfg.num_chans; i++) {
@@ -720,12 +843,19 @@ struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx)
trx->cfg.rx_sps = DEFAULT_RX_SPS;
trx->cfg.filler = FILLER_ZERO;
trx->cfg.rssi_offset = 0.0f;
+ trx->cfg.dev_args = talloc_strdup(trx, "");
return trx;
}
int trx_vty_init(struct trx_ctx* trx)
{
+ cfg_filler_type_cmd.string = vty_cmd_string_from_valstr(trx, filler_types,
+ "filler type (", "|", ")", 0);
+ cfg_filler_type_cmd.doc = vty_cmd_string_from_valstr(trx, filler_docs,
+ CFG_FILLER_DOC_STR "What to do when there is nothing to send "
+ "(filler type, default=zero)\n", "\n", "", 0);
+
g_trx_ctx = trx;
install_element_ve(&show_trx_cmd);
@@ -741,6 +871,7 @@ int trx_vty_init(struct trx_ctx* trx)
install_element(TRX_NODE, &cfg_clock_ref_cmd);
install_element(TRX_NODE, &cfg_multi_arfcn_cmd);
install_element(TRX_NODE, &cfg_offset_cmd);
+ install_element(TRX_NODE, &cfg_freq_offset_cmd);
install_element(TRX_NODE, &cfg_rssi_offset_cmd);
install_element(TRX_NODE, &cfg_swap_channels_cmd);
install_element(TRX_NODE, &cfg_egprs_cmd);
@@ -754,6 +885,12 @@ int trx_vty_init(struct trx_ctx* trx)
install_element(TRX_NODE, &cfg_stack_size_cmd);
install_element(TRX_NODE, &cfg_chan_cmd);
+ install_element(TRX_NODE, &cfg_ul_fn_offset_cmd);
+ install_element(TRX_NODE, &cfg_ul_freq_override_cmd);
+ install_element(TRX_NODE, &cfg_dl_freq_override_cmd);
+ install_element(TRX_NODE, &cfg_ul_gain_override_cmd);
+ install_element(TRX_NODE, &cfg_dl_gain_override_cmd);
+ install_element(TRX_NODE, &cfg_use_viterbi_cmd);
install_node(&chan_node, dummy_config_write);
install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
diff --git a/GSM/GSMCommon.cpp b/GSM/GSMCommon.cpp
index 5e9e4ae..a9e2bb1 100644
--- a/GSM/GSMCommon.cpp
+++ b/GSM/GSMCommon.cpp
@@ -55,12 +55,15 @@ const BitVector GSM::gEdgeTrainingSequence[] = {
};
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
+const BitVector GSM::gDummyBurstTSC("01110001011100010111000101");
/* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */
const BitVector GSM::gRACHSynchSequenceTS0("01001011011111111001100110101010001111000"); /* GSM, GMSK (default) */
const BitVector GSM::gRACHSynchSequenceTS1("01010100111110001000011000101111001001101"); /* EGPRS, 8-PSK */
const BitVector GSM::gRACHSynchSequenceTS2("11101111001001110101011000001101101110111"); /* EGPRS, GMSK */
+const BitVector GSM::gSCHSynchSequence("1011100101100010000001000000111100101101010001010111011000011011");
+
// |-head-||---------midamble----------------------||--------------data----------------||t|
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
diff --git a/GSM/GSMCommon.h b/GSM/GSMCommon.h
index 48723b4..aa059c2 100644
--- a/GSM/GSMCommon.h
+++ b/GSM/GSMCommon.h
@@ -52,11 +52,16 @@ extern const BitVector gEdgeTrainingSequence[];
/** C0T0 filler burst, GSM 05.02, 5.2.6 */
extern const BitVector gDummyBurst;
+extern const BitVector gDummyBurstTSC;
/** Random access burst synch. sequence */
extern const BitVector gRACHSynchSequenceTS0;
extern const BitVector gRACHSynchSequenceTS1;
extern const BitVector gRACHSynchSequenceTS2;
+
+/** Synchronization burst sync sequence */
+extern const BitVector gSCHSynchSequence;
+
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
extern const BitVector gRACHBurst;
diff --git a/Makefile.am b/Makefile.am
index 0152341..aee0d9d 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -26,22 +26,37 @@ AM_CXXFLAGS = -Wall -pthread
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread
+SUBDIRS =
+
+if ENABLE_MS_TRX
+SUBDIRS += $(LIBTRXCON_DIR)
+endif
+
# Order must be preserved
-SUBDIRS = \
- doc \
+SUBDIRS += \
CommonLibs \
GSM \
Transceiver52M \
contrib \
tests \
- utils
+ utils \
+ doc \
+ $(NULL)
+
+BUILT_SOURCES = $(top_srcdir)/.version
+$(top_srcdir)/.version:
+ echo $(VERSION) > $@-t && mv $@-t $@
+dist-hook:
+ echo $(VERSION) > $(distdir)/.tarball-version
EXTRA_DIST = \
+ .version \
LEGAL \
COPYING \
README.md \
contrib/osmo-trx.spec.in \
debian \
+ git-version-gen \
$(NULL)
AM_DISTCHECK_CONFIGURE_FLAGS = \
diff --git a/README.md b/README.md
index dc026e0..9361631 100644
--- a/README.md
+++ b/README.md
@@ -1,22 +1,20 @@
-About OsmTRX
-============
+About OsmoTRX
+=============
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications:
-* TS 05.01 "Physical layer on the radio path"
-* TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
-* TS 05.04 "Modulation"
-* TS 05.10 "Radio subsystem synchronization"
+* TS 05.01 *Physical layer on the radio path*
+* TS 05.02 *Multiplexing and Multiple Access on the Radio Path*
+* TS 05.04 *Modulation*
+* TS 05.10 *Radio subsystem synchronization*
-OsmoTRX is based on the transceiver code from the
+OsmoTRX is originally based on the transceiver code from the
[OpenBTS](https://osmocom.org/projects/osmobts/wiki/OpenBTS) project, but setup
to operate independently with the purpose of using with non-OpenBTS software and
-projects, while still maintaining backwards compatibility with OpenBTS when
-possible. Currently there are numerous features contained in OsmoTRX that extend
-the functionality of the OpenBTS transceiver. These features include enhanced
-support for various embedded platforms - notably ARM - and dual channel
-diversity support for the Fairwaves umtrx.
+projects, specifically within the Osmocom stack. Used together with
+[OsmoBTS](https://osmocom.org/projects/osmobts/wiki) you can get a pretty
+standard GSM/GPRS/EGPRS BTS with Abis interface as per the relevant 3GPP specifications.
Homepage
--------
@@ -29,9 +27,9 @@ GIT Repository
You can clone from the official osmo-trx.git repository using
- git clone git://git.osmocom.org/osmo-trx.git
+ git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
-There is a cgit interface at <http://git.osmocom.org/osmo-trx/>
+There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-trx>
Documentation
-------------
@@ -39,7 +37,14 @@ Documentation
Doxygen-generated API documentation is generated during the build process, but
also available online for each of the sub-libraries at User Manual for OsmoTRX
can be generated during the build process, and is also available online at
-<http://ftp.osmocom.org/docs/latest/osmotrx-usermanual.pdf>.
+<https://ftp.osmocom.org/docs/latest/osmotrx-usermanual.pdf>.
+
+Forum
+-----
+
+We welcome any osmo-trx related discussions in the
+[Cellular Network Infrastructure -> 2 RAN (GERAN)](https://discourse.osmocom.org/c/cni/geran)
+section of the osmocom discourse (web based Forum).
Mailing List
------------
@@ -52,6 +57,13 @@ Please observe the [Osmocom Mailing List
Rules](https://osmocom.org/projects/cellular-infrastructure/wiki/Mailing_List_Rules)
when posting.
+Issue Tracker
+-------------
+
+We use the [issue tracker of the osmo-trx project on osmocom.org](https://osmocom.org/projects/osmotrx/issues) for
+tracking the state of bug reports and feature requests. Feel free to submit any issues you may find, or help
+us out by resolving existing issues.
+
Contributing
------------
diff --git a/TODO-RELEASE b/TODO-RELEASE
index 75fe7b8..e69de29 100644
--- a/TODO-RELEASE
+++ b/TODO-RELEASE
@@ -1,2 +0,0 @@
-* update libosmocore dependency to > 1.3.x for osmo_sched_vty_init(), osmo_sched_vty_apply_localthread()
-* update osmo-gsm-manuals dependency to > 0.3.0 for vty_cpu_sched.adoc include.
diff --git a/Transceiver52M/ChannelizerBase.cpp b/Transceiver52M/ChannelizerBase.cpp
index e6eeed2..9910091 100644
--- a/Transceiver52M/ChannelizerBase.cpp
+++ b/Transceiver52M/ChannelizerBase.cpp
@@ -244,6 +244,7 @@ ChannelizerBase::~ChannelizerBase()
free(subFilters[i]);
delete[] hist[i];
}
+ free(subFilters);
fft_free(fftInput);
fft_free(fftOutput);
diff --git a/Transceiver52M/Complex.h b/Transceiver52M/Complex.h
index 6e72346..597a26f 100644
--- a/Transceiver52M/Complex.h
+++ b/Transceiver52M/Complex.h
@@ -29,7 +29,7 @@ unlike the built-in complex<> templates, these inline most operations for speed
template<class Real> class Complex {
public:
-
+ typedef Real value_type;
Real r, i;
/**@name constructors */
diff --git a/Transceiver52M/Makefile.am b/Transceiver52M/Makefile.am
index 7dad159..0b63b16 100644
--- a/Transceiver52M/Makefile.am
+++ b/Transceiver52M/Makefile.am
@@ -40,7 +40,9 @@ COMMON_SOURCES = \
ChannelizerBase.cpp \
Channelizer.cpp \
Synthesis.cpp \
- proto_trxd.c
+ proto_trxd.c \
+ grgsm_vitac/grgsm_vitac.cpp \
+ grgsm_vitac/viterbi_detector.cc
libtransceiver_common_la_SOURCES = \
$(COMMON_SOURCES) \
@@ -73,6 +75,47 @@ COMMON_LDADD = \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_LIBS)
+if ENABLE_MS_TRX
+AM_CPPFLAGS += -I$(top_srcdir)/osmocom-bb/src/host/trxcon/include/
+AM_CPPFLAGS += -I${srcdir}
+
+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/libl1gprs.a \
+ $(LIBOSMOCODING_LIBS)
+
+MS_LOWER_SRC = \
+ ms/sch.c \
+ ms/ms.cpp \
+ ms/threadsched.cpp \
+ ms/ms_rx_lower.cpp \
+ grgsm_vitac/grgsm_vitac.cpp \
+ grgsm_vitac/viterbi_detector.cc
+
+MS_UPPER_SRC = \
+ ms/ms_upper.cpp \
+ ms/l1ctl_server.c \
+ ms/logging.c \
+ ms/l1ctl_server_cb.cpp \
+ ms/ms_trxcon_if.cpp
+
+noinst_HEADERS += \
+ ms/ms.h \
+ ms/threadsched.h \
+ ms/bladerf_specific.h \
+ ms/uhd_specific.h \
+ ms/ms_upper.h \
+ ms/ms_trxcon_if.h \
+ ms/itrq.h \
+ ms/sch.h \
+ ms/threadpool.h \
+ grgsm_vitac/viterbi_detector.h \
+ grgsm_vitac/constants.h \
+ grgsm_vitac/grgsm_vitac.h
+
+endif
+
bin_PROGRAMS =
if DEVICE_UHD
@@ -83,6 +126,17 @@ osmo_trx_uhd_LDADD = \
$(COMMON_LDADD) \
$(UHD_LIBS)
osmo_trx_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
+
+#if ENABLE_MS_TRX
+#bin_PROGRAMS += osmo-trx-ms-uhd
+#osmo_trx_ms_uhd_SOURCES = $(MS_LOWER_SRC) $(MS_UPPER_SRC)
+#osmo_trx_ms_uhd_LDADD = \
+# $(builddir)/device/uhd/libdevice.la \
+# $(COMMON_LDADD) \
+# $(UHD_LIBS) \
+# $(TRXCON_LDADD)
+#osmo_trx_ms_uhd_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS) -DBUILDUHD
+#endif
endif
if DEVICE_USRP1
@@ -105,6 +159,27 @@ osmo_trx_lms_LDADD = \
osmo_trx_lms_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
endif
+if DEVICE_BLADE
+bin_PROGRAMS += osmo-trx-blade
+osmo_trx_blade_SOURCES = osmo-trx.cpp
+osmo_trx_blade_LDADD = \
+ $(builddir)/device/bladerf/libdevice.la \
+ $(COMMON_LDADD) \
+ $(BLADE_LIBS)
+osmo_trx_blade_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
+
+if ENABLE_MS_TRX
+bin_PROGRAMS += osmo-trx-ms-blade
+osmo_trx_ms_blade_SOURCES = $(MS_LOWER_SRC) $(MS_UPPER_SRC)
+osmo_trx_ms_blade_LDADD = \
+ $(builddir)/device/bladerf/libdevice.la \
+ $(COMMON_LDADD) \
+ $(BLADE_LIBS) \
+ $(TRXCON_LDADD)
+osmo_trx_ms_blade_CPPFLAGS = $(AM_CPPFLAGS) $(BLADE_CFLAGS) -DBUILDBLADE
+endif
+endif
+
if DEVICE_IPC
bin_PROGRAMS += osmo-trx-ipc
osmo_trx_ipc_SOURCES = osmo-trx.cpp
@@ -113,4 +188,3 @@ osmo_trx_ipc_LDADD = \
$(COMMON_LDADD)
osmo_trx_ipc_CPPFLAGS = $(AM_CPPFLAGS)
endif
-
diff --git a/Transceiver52M/Resampler.cpp b/Transceiver52M/Resampler.cpp
index 2ca6406..841c3a9 100644
--- a/Transceiver52M/Resampler.cpp
+++ b/Transceiver52M/Resampler.cpp
@@ -13,10 +13,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <stdlib.h>
@@ -36,7 +32,7 @@ extern "C" {
#define M_PI 3.14159265358979323846264338327f
#endif
-#define MAX_OUTPUT_LEN 4096
+#define MAX_OUTPUT_LEN 4096*4
using namespace std;
diff --git a/Transceiver52M/Resampler.h b/Transceiver52M/Resampler.h
index 5af8d26..e1962c7 100644
--- a/Transceiver52M/Resampler.h
+++ b/Transceiver52M/Resampler.h
@@ -13,10 +13,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef _RESAMPLER_H_
diff --git a/Transceiver52M/Transceiver.cpp b/Transceiver52M/Transceiver.cpp
index 881633e..f5ddd9b 100644
--- a/Transceiver52M/Transceiver.cpp
+++ b/Transceiver52M/Transceiver.cpp
@@ -29,6 +29,7 @@
#include <fstream>
#include "Transceiver.h"
#include <Logger.h>
+#include <grgsm_vitac/grgsm_vitac.h>
extern "C" {
#include "osmo_signal.h"
@@ -135,11 +136,13 @@ bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t r
Transceiver::Transceiver(const struct trx_cfg *cfg,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface)
- : cfg(cfg), mClockSocket(-1),
- mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
- mChans(cfg->num_chans), mOn(false), mForceClockInterface(false),
- mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0),
- mMaxExpectedDelayNB(0), mWriteBurstToDiskMask(0)
+ : mChans(cfg->num_chans), cfg(cfg),
+ mCtrlSockets(mChans), mClockSocket(-1),
+ mTxPriorityQueues(mChans), mReceiveFIFO(mChans),
+ mRxServiceLoopThreads(mChans), mRxLowerLoopThread(nullptr), mTxLowerLoopThread(nullptr),
+ mTxPriorityQueueServiceLoopThreads(mChans), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
+ mOn(false),mForceClockInterface(false), mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0),
+ mMaxExpectedDelayNB(0), mWriteBurstToDiskMask(0), mVersionTRXD(mChans), mStates(mChans)
{
txFullScale = mRadioInterface->fullScaleInputValue();
rxFullScale = mRadioInterface->fullScaleOutputValue();
@@ -206,15 +209,10 @@ bool Transceiver::init()
return false;
}
+ initvita();
+
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 */
if (cfg->filler == FILLER_DUMMY)
@@ -395,7 +393,7 @@ void Transceiver::addRadioVector(size_t chan, BitVector &bits,
else
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), cfg->tx_sps);
- scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
+ scaleVector(*burst, txFullScale * pow(10, (double) -RSSI / 20));
radio_burst = new radioVector(wTime, burst);
@@ -583,7 +581,7 @@ CorrType Transceiver::expectedCorrType(GSM::Time currTime,
case XIII: {
int mod52 = burstFN % 52;
if ((mod52 == 12) || (mod52 == 38))
- return cfg->ext_rach ? EXT_RACH : RACH;
+ return RACH; /* RACH is always 8-bit on PTCCH/U */
else if ((mod52 == 25) || (mod52 == 51))
return IDLE;
else /* Enable 8-PSK burst detection if EDGE is enabled */
@@ -619,6 +617,44 @@ double Transceiver::rssiOffset(size_t chan)
return mRadioInterface->rssiOffset(chan) + cfg->rssi_offset;
}
+static SoftVector *demodAnyBurst_va(const signalVector &burst, CorrType type, int sps, int rach_max_toa, int tsc)
+{
+ auto conved_beg = reinterpret_cast<const std::complex<float> *>(&burst.begin()[0]);
+ std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
+ float ncmax;
+ const unsigned burst_len_bits = 148 + 8;
+ char demodded_softbits[burst_len_bits];
+ SoftVector *bits = new SoftVector(burst_len_bits);
+
+ if (type == CorrType::TSC) {
+ auto rach_burst_start = get_norm_chan_imp_resp(conved_beg, chan_imp_resp, &ncmax, tsc);
+ rach_burst_start = std::max(rach_burst_start, 0);
+ detect_burst_nb(conved_beg, chan_imp_resp, rach_burst_start, demodded_softbits);
+ } else {
+ auto normal_burst_start = get_access_imp_resp(conved_beg, chan_imp_resp, &ncmax, 0);
+ normal_burst_start = std::max(normal_burst_start, 0);
+ detect_burst_ab(conved_beg, chan_imp_resp, normal_burst_start, demodded_softbits, rach_max_toa);
+ }
+
+ float *s = &bits->begin()[0];
+ for (unsigned int i = 0; i < 148; i++)
+ s[i] = demodded_softbits[i] * -1;
+ for (unsigned int i = 148; i < burst_len_bits; i++)
+ s[i] = 0;
+ return bits;
+}
+
+#define USE_VA
+
+#ifdef USE_VA
+// signalvector is owning despite claiming not to, but we can pretend, too..
+static void dummy_free(void *wData){};
+static void *dummy_alloc(size_t newSize)
+{
+ return 0;
+};
+#endif
+
/*
* Pull bursts from the FIFO and handle according to the slot
* and burst correlation type. Equalzation is currently disabled.
@@ -639,6 +675,9 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
TransceiverState *state = &mStates[chan];
bool ctr_changed = false;
double rssi_offset;
+ static complex burst_shift_buffer[625];
+ static signalVector shift_vec(burst_shift_buffer, 0, 625, dummy_alloc, dummy_free);
+ signalVector *shvec_ptr = &shift_vec;
/* Blocking FIFO read */
radioVector *radio_burst = mReceiveFIFO[chan]->read();
@@ -648,7 +687,7 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
}
/* Set time and determine correlation type */
- burstTime = radio_burst->getTime();
+ burstTime = radio_burst->getTime() + cfg->ul_fn_offset;
CorrType type = expectedCorrType(burstTime, chan);
/* Initialize struct bi */
@@ -718,8 +757,15 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
max_toa = (type == RACH || type == EXT_RACH) ?
mMaxExpectedDelayAB : mMaxExpectedDelayNB;
+ if (cfg->use_va) {
+ // shifted burst copy to make the old demod and detection happy
+ std::copy(burst->begin() + 20, burst->end() - 20, shift_vec.begin());
+ } else {
+ shvec_ptr = burst;
+ }
+
/* Detect normal or RACH bursts */
- rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
+ rc = detectAnyBurst(*shvec_ptr, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
if (rc <= 0) {
if (rc == -SIGERR_CLIP) {
LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst";
@@ -733,11 +779,16 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
goto ret_idle;
}
- type = (CorrType) rc;
+ if (cfg->use_va) {
+ scaleVector(*burst, { (1. / (float)((1 << 14) - 1)), 0 });
+ rxBurst = demodAnyBurst_va(*burst, (CorrType)rc, cfg->rx_sps, max_toa, mTSC);
+ } else {
+ rxBurst = demodAnyBurst(*shvec_ptr, (CorrType)rc, cfg->rx_sps, &ebp);
+ }
+
bi->toa = ebp.toa;
bi->tsc = ebp.tsc;
bi->ci = ebp.ci;
- rxBurst = demodAnyBurst(*burst, cfg->rx_sps, ebp.amp, ebp.toa, type);
/* EDGE demodulator returns 444 (gSlotLen * 3) bits */
if (rxBurst->size() == EDGE_BURST_NBITS) {
@@ -805,7 +856,7 @@ void Transceiver::ctrl_sock_send(ctrl_msg& m, int chan)
struct osmo_fd *conn_bfd = &s.conn_bfd;
s.txmsgqueue.push_back(m);
- conn_bfd->when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(conn_bfd);
}
int Transceiver::ctrl_sock_write(int chan)
@@ -820,7 +871,7 @@ int Transceiver::ctrl_sock_write(int chan)
while (s.txmsgqueue.size()) {
const ctrl_msg m = s.txmsgqueue.front();
- s.conn_bfd.when &= ~OSMO_FD_WRITE;
+ osmo_fd_write_disable(&s.conn_bfd);
/* try to send it over the socket */
rc = write(s.conn_bfd.fd, m.data, strlen(m.data) + 1);
@@ -828,7 +879,7 @@ int Transceiver::ctrl_sock_write(int chan)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
- s.conn_bfd.when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(&s.conn_bfd);
break;
}
goto close;
@@ -908,19 +959,18 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss);
}
} else if (match_cmd(command, "SETMAXDLY", &params)) {
- //set expected maximum time-of-arrival
+ //set expected maximum time-of-arrival for Access Bursts
int maxDelay;
sscanf(params, "%d", &maxDelay);
mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
} else if (match_cmd(command, "SETMAXDLYNB", &params)) {
- //set expected maximum time-of-arrival
+ //set expected maximum time-of-arrival for Normal Bursts
int maxDelay;
sscanf(params, "%d", &maxDelay);
mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
} else if (match_cmd(command, "SETRXGAIN", &params)) {
- //set expected maximum time-of-arrival
int newGain;
sscanf(params, "%d", &newGain);
newGain = mRadioInterface->setRxGain(newGain, chan);
@@ -954,7 +1004,7 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
// tune receiver
int freqKhz;
sscanf(params, "%d", &freqKhz);
- mRxFreq = freqKhz * 1e3;
+ mRxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3;
if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
LOGCHAN(chan, DTRXCTRL, FATAL) << "RX failed to tune";
sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
@@ -965,7 +1015,7 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
// tune txmtr
int freqKhz;
sscanf(params, "%d", &freqKhz);
- mTxFreq = freqKhz * 1e3;
+ mTxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3;
if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
LOGCHAN(chan, DTRXCTRL, FATAL) << "TX failed to tune";
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
@@ -1003,7 +1053,7 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
LOGCHAN(chan, DTRXCTRL, INFO) << "BTS requests TRXD version switch: " << version_recv;
if (version_recv > TRX_DATA_FORMAT_VER) {
LOGCHAN(chan, DTRXCTRL, INFO) << "rejecting TRXD version " << version_recv
- << "in favor of " << TRX_DATA_FORMAT_VER;
+ << " in favor of " << TRX_DATA_FORMAT_VER;
sprintf(response, "RSP SETFORMAT %u %u", TRX_DATA_FORMAT_VER, version_recv);
} else {
LOGCHAN(chan, DTRXCTRL, NOTICE) << "switching to TRXD version " << version_recv;
diff --git a/Transceiver52M/Transceiver.h b/Transceiver52M/Transceiver.h
index 45a4159..babe420 100644
--- a/Transceiver52M/Transceiver.h
+++ b/Transceiver52M/Transceiver.h
@@ -80,7 +80,7 @@ struct TransceiverState {
/* Received noise energy levels */
float mNoiseLev;
- noiseVector mNoises;
+ avgVector mNoises;
/* Shadowed downlink attenuation */
int mPower;
@@ -148,6 +148,7 @@ public:
} ChannelCombination;
private:
+ size_t mChans;
struct ctrl_msg {
char data[101];
ctrl_msg() {};
@@ -161,9 +162,9 @@ struct ctrl_sock_state {
}
~ctrl_sock_state() {
if(conn_bfd.fd >= 0) {
+ osmo_fd_unregister(&conn_bfd);
close(conn_bfd.fd);
conn_bfd.fd = -1;
- osmo_fd_unregister(&conn_bfd);
}
}
};
@@ -218,7 +219,7 @@ struct ctrl_sock_state {
/** drive handling of control messages from GSM core */
int ctrl_sock_handle_rx(int chan);
- size_t mChans;
+
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 mHandover[8][8]; ///< expect handover to the timeslot/subslot
diff --git a/Transceiver52M/arch/arm/convert.c b/Transceiver52M/arch/arm/convert.c
index ace1b6f..7145579 100644
--- a/Transceiver52M/arch/arm/convert.c
+++ b/Transceiver52M/arch/arm/convert.c
@@ -13,10 +13,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
diff --git a/Transceiver52M/arch/arm/convert_neon.S b/Transceiver52M/arch/arm/convert_neon.S
index a1fbd40..6cf3c03 100644
--- a/Transceiver52M/arch/arm/convert_neon.S
+++ b/Transceiver52M/arch/arm/convert_neon.S
@@ -13,10 +13,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
.syntax unified
diff --git a/Transceiver52M/arch/arm/convolve.c b/Transceiver52M/arch/arm/convolve.c
index adb718d..c2611b4 100644
--- a/Transceiver52M/arch/arm/convolve.c
+++ b/Transceiver52M/arch/arm/convolve.c
@@ -13,10 +13,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
diff --git a/Transceiver52M/arch/arm/convolve_neon.S b/Transceiver52M/arch/arm/convolve_neon.S
index a3e1ba5..3209d60 100644
--- a/Transceiver52M/arch/arm/convolve_neon.S
+++ b/Transceiver52M/arch/arm/convolve_neon.S
@@ -13,10 +13,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
diff --git a/Transceiver52M/arch/arm/mult.c b/Transceiver52M/arch/arm/mult.c
index 9851626..251b3c9 100644
--- a/Transceiver52M/arch/arm/mult.c
+++ b/Transceiver52M/arch/arm/mult.c
@@ -13,10 +13,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
diff --git a/Transceiver52M/arch/arm/mult_neon.S b/Transceiver52M/arch/arm/mult_neon.S
index 6318c50..b4cd229 100644
--- a/Transceiver52M/arch/arm/mult_neon.S
+++ b/Transceiver52M/arch/arm/mult_neon.S
@@ -13,10 +13,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
.syntax unified
diff --git a/Transceiver52M/arch/arm/scale.c b/Transceiver52M/arch/arm/scale.c
index a3214f7..d44ffc8 100644
--- a/Transceiver52M/arch/arm/scale.c
+++ b/Transceiver52M/arch/arm/scale.c
@@ -13,10 +13,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
diff --git a/Transceiver52M/arch/arm/scale_neon.S b/Transceiver52M/arch/arm/scale_neon.S
index f10de1e..c348f30 100644
--- a/Transceiver52M/arch/arm/scale_neon.S
+++ b/Transceiver52M/arch/arm/scale_neon.S
@@ -13,10 +13,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
.syntax unified
diff --git a/Transceiver52M/arch/common/convert_base.c b/Transceiver52M/arch/common/convert_base.c
index 9876e83..9a01a1e 100644
--- a/Transceiver52M/arch/common/convert_base.c
+++ b/Transceiver52M/arch/common/convert_base.c
@@ -13,10 +13,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "convert.h"
diff --git a/Transceiver52M/arch/common/convolve_base.c b/Transceiver52M/arch/common/convolve_base.c
index bfda783..3765c5c 100644
--- a/Transceiver52M/arch/common/convolve_base.c
+++ b/Transceiver52M/arch/common/convolve_base.c
@@ -13,10 +13,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
diff --git a/Transceiver52M/arch/x86/convert.c b/Transceiver52M/arch/x86/convert.c
index bbcfd67..596233c 100644
--- a/Transceiver52M/arch/x86/convert.c
+++ b/Transceiver52M/arch/x86/convert.c
@@ -11,10 +11,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
diff --git a/Transceiver52M/arch/x86/convert_sse_3.c b/Transceiver52M/arch/x86/convert_sse_3.c
index 255db67..f00ecf5 100644
--- a/Transceiver52M/arch/x86/convert_sse_3.c
+++ b/Transceiver52M/arch/x86/convert_sse_3.c
@@ -11,10 +11,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
diff --git a/Transceiver52M/arch/x86/convert_sse_3.h b/Transceiver52M/arch/x86/convert_sse_3.h
index c2f87d7..b5abe47 100644
--- a/Transceiver52M/arch/x86/convert_sse_3.h
+++ b/Transceiver52M/arch/x86/convert_sse_3.h
@@ -11,10 +11,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
diff --git a/Transceiver52M/arch/x86/convert_sse_4_1.c b/Transceiver52M/arch/x86/convert_sse_4_1.c
index 42a235c..736a376 100644
--- a/Transceiver52M/arch/x86/convert_sse_4_1.c
+++ b/Transceiver52M/arch/x86/convert_sse_4_1.c
@@ -11,10 +11,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
diff --git a/Transceiver52M/arch/x86/convert_sse_4_1.h b/Transceiver52M/arch/x86/convert_sse_4_1.h
index 57a5efb..63305e5 100644
--- a/Transceiver52M/arch/x86/convert_sse_4_1.h
+++ b/Transceiver52M/arch/x86/convert_sse_4_1.h
@@ -11,10 +11,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
diff --git a/Transceiver52M/arch/x86/convolve.c b/Transceiver52M/arch/x86/convolve.c
index be62721..45a3719 100644
--- a/Transceiver52M/arch/x86/convolve.c
+++ b/Transceiver52M/arch/x86/convolve.c
@@ -11,10 +11,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
diff --git a/Transceiver52M/arch/x86/convolve_sse_3.c b/Transceiver52M/arch/x86/convolve_sse_3.c
index 8fd3b5e..ca4dc71 100644
--- a/Transceiver52M/arch/x86/convolve_sse_3.c
+++ b/Transceiver52M/arch/x86/convolve_sse_3.c
@@ -11,10 +11,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <malloc.h>
diff --git a/Transceiver52M/arch/x86/convolve_sse_3.h b/Transceiver52M/arch/x86/convolve_sse_3.h
index d929ef6..2cbc037 100644
--- a/Transceiver52M/arch/x86/convolve_sse_3.h
+++ b/Transceiver52M/arch/x86/convolve_sse_3.h
@@ -11,10 +11,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#pragma once
diff --git a/Transceiver52M/device/Makefile.am b/Transceiver52M/device/Makefile.am
index 93ba7e3..9af18f7 100644
--- a/Transceiver52M/device/Makefile.am
+++ b/Transceiver52M/device/Makefile.am
@@ -17,3 +17,7 @@ endif
if DEVICE_LMS
SUBDIRS += lms
endif
+
+if DEVICE_BLADE
+SUBDIRS += bladerf
+endif
diff --git a/Transceiver52M/device/bladerf/Makefile.am b/Transceiver52M/device/bladerf/Makefile.am
new file mode 100644
index 0000000..e9917af
--- /dev/null
+++ b/Transceiver52M/device/bladerf/Makefile.am
@@ -0,0 +1,11 @@
+include $(top_srcdir)/Makefile.common
+
+AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
+AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(BLADE_CFLAGS)
+
+noinst_HEADERS = bladerf.h
+
+noinst_LTLIBRARIES = libdevice.la
+
+libdevice_la_SOURCES = bladerf.cpp
+libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
diff --git a/Transceiver52M/device/bladerf/bladerf.cpp b/Transceiver52M/device/bladerf/bladerf.cpp
new file mode 100644
index 0000000..2f3c86a
--- /dev/null
+++ b/Transceiver52M/device/bladerf/bladerf.cpp
@@ -0,0 +1,611 @@
+/*
+ * Copyright 2022 sysmocom - s.f.m.c. GmbH
+ *
+ * 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 <map>
+#include <libbladeRF.h>
+#include "radioDevice.h"
+#include "bladerf.h"
+#include "Threads.h"
+#include "Logger.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+extern "C" {
+#include <osmocom/core/utils.h>
+#include <osmocom/gsm/gsm_utils.h>
+#include <osmocom/vty/cpu_sched_vty.h>
+}
+
+#define SAMPLE_BUF_SZ (1 << 20)
+
+#define B2XX_TIMING_4_4SPS 6.18462e-5
+
+#define CHKRET() \
+ { \
+ if (status != 0) \
+ LOGC(DDEV, ERROR) << bladerf_strerror(status); \
+ }
+
+static const dev_map_t dev_param_map{
+ { std::make_tuple(blade_dev_type::BLADE2, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
+};
+
+static const power_map_t dev_band_nom_power_param_map{
+ { std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
+ { std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
+ { std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
+ { std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_1900), { 89.75, 7.7, -11.0 } },
+};
+
+/* So far measurements done for B210 show really close to linear relationship
+ * between gain and real output power, so we simply adjust the measured offset
+ */
+static double TxGain2TxPower(const dev_band_desc &desc, double tx_gain_db)
+{
+ return desc.nom_out_tx_power - (desc.nom_uhd_tx_gain - tx_gain_db);
+}
+static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
+{
+ return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
+}
+
+blade_device::blade_device(InterfaceType iface, const struct trx_cfg *cfg)
+ : RadioDevice(iface, cfg), band_manager(dev_band_nom_power_param_map, dev_param_map), dev(nullptr),
+ rx_gain_min(0.0), rx_gain_max(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), drop_cnt(0),
+ prev_ts(0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
+{
+}
+
+blade_device::~blade_device()
+{
+ if (dev) {
+ bladerf_enable_module(dev, BLADERF_CHANNEL_RX(0), false);
+ bladerf_enable_module(dev, BLADERF_CHANNEL_TX(0), false);
+ }
+
+ stop();
+
+ for (size_t i = 0; i < rx_buffers.size(); i++)
+ delete rx_buffers[i];
+}
+
+void blade_device::init_gains()
+{
+ double tx_gain_min, tx_gain_max;
+ int status;
+
+ const struct bladerf_range *r;
+ bladerf_get_gain_range(dev, BLADERF_RX, &r);
+
+ rx_gain_min = r->min;
+ rx_gain_max = r->max;
+ LOGC(DDEV, INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
+
+ for (size_t i = 0; i < rx_gains.size(); i++) {
+ double gain = (rx_gain_min + rx_gain_max) / 2;
+ status = bladerf_set_gain_mode(dev, BLADERF_CHANNEL_RX(i), BLADERF_GAIN_MGC);
+ CHKRET()
+ bladerf_gain_mode m;
+ bladerf_get_gain_mode(dev, BLADERF_CHANNEL_RX(i), &m);
+ LOGC(DDEV, INFO) << (m == BLADERF_GAIN_MANUAL ? "gain manual" : "gain AUTO");
+
+ status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0);
+ CHKRET()
+ int actual_gain;
+ status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain);
+ CHKRET()
+ LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain << " scale "
+ << r->scale << " actual " << actual_gain;
+ rx_gains[i] = actual_gain;
+
+ status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0);
+ CHKRET()
+ status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain);
+ CHKRET()
+ LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain << " scale "
+ << r->scale << " actual " << actual_gain;
+ rx_gains[i] = actual_gain;
+ }
+
+ status = bladerf_get_gain_range(dev, BLADERF_TX, &r);
+ CHKRET()
+ tx_gain_min = r->min;
+ tx_gain_max = r->max;
+ LOGC(DDEV, INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
+
+ for (size_t i = 0; i < tx_gains.size(); i++) {
+ double gain = (tx_gain_min + tx_gain_max) / 2;
+ status = bladerf_set_gain(dev, BLADERF_CHANNEL_TX(i), 30);
+ CHKRET()
+ int actual_gain;
+ status = bladerf_get_gain(dev, BLADERF_CHANNEL_TX(i), &actual_gain);
+ CHKRET()
+ LOGC(DDEV, INFO) << "Default setting Tx gain for channel " << i << " to " << gain << " scale "
+ << r->scale << " actual " << actual_gain;
+ tx_gains[i] = actual_gain;
+ }
+
+ return;
+}
+
+void blade_device::set_rates()
+{
+ 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);
+ CHKRET()
+ status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_TX(0), &rate, &actual);
+ CHKRET()
+
+ tx_rate = rx_rate = (double)rate.num / (double)rate.den;
+
+ LOGC(DDEV, INFO) << "Rates set to" << tx_rate << " / " << rx_rate;
+
+ 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);
+
+ ts_offset = 60; // FIXME: actual blade offset, should equal b2xx
+}
+
+double blade_device::setRxGain(double db, size_t chan)
+{
+ if (chan >= rx_gains.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return 0.0f;
+ }
+
+ bladerf_set_gain(dev, BLADERF_CHANNEL_RX(chan), 30); //db);
+ int actual_gain;
+ bladerf_get_gain(dev, BLADERF_CHANNEL_RX(chan), &actual_gain);
+
+ rx_gains[chan] = actual_gain;
+
+ LOGC(DDEV, INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
+
+ return rx_gains[chan];
+}
+
+double blade_device::getRxGain(size_t chan)
+{
+ if (chan >= rx_gains.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return 0.0f;
+ }
+
+ return rx_gains[chan];
+}
+
+double blade_device::rssiOffset(size_t chan)
+{
+ double rssiOffset;
+ dev_band_desc desc;
+
+ if (chan >= rx_gains.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return 0.0f;
+ }
+
+ get_dev_band_desc(desc);
+ rssiOffset = rx_gains[chan] + desc.rxgain2rssioffset_rel;
+ return rssiOffset;
+}
+
+double blade_device::setPowerAttenuation(int atten, size_t chan)
+{
+ double tx_power, db;
+ dev_band_desc desc;
+
+ if (chan >= tx_gains.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel" << chan;
+ return 0.0f;
+ }
+
+ get_dev_band_desc(desc);
+ tx_power = desc.nom_out_tx_power - atten;
+ db = TxPower2TxGain(desc, tx_power);
+
+ bladerf_set_gain(dev, BLADERF_CHANNEL_TX(chan), 30);
+ int actual_gain;
+ bladerf_get_gain(dev, BLADERF_CHANNEL_RX(chan), &actual_gain);
+
+ tx_gains[chan] = actual_gain;
+
+ LOGC(DDEV, INFO)
+ << "Set TX gain to " << tx_gains[chan] << "dB, ~" << TxGain2TxPower(desc, tx_gains[chan]) << " dBm "
+ << "(asked for " << db << " dB, ~" << tx_power << " dBm)";
+
+ return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
+}
+double blade_device::getPowerAttenuation(size_t chan)
+{
+ dev_band_desc desc;
+ if (chan >= tx_gains.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return 0.0f;
+ }
+
+ get_dev_band_desc(desc);
+ return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
+}
+
+int blade_device::getNominalTxPower(size_t chan)
+{
+ dev_band_desc desc;
+ get_dev_band_desc(desc);
+
+ return desc.nom_out_tx_power;
+}
+
+int blade_device::open()
+{
+ bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_VERBOSE);
+ bladerf_set_usb_reset_on_open(true);
+ auto success = bladerf_open(&dev, cfg->dev_args);
+ if (success != 0) {
+ struct bladerf_devinfo *info;
+ auto num_devs = bladerf_get_device_list(&info);
+ LOGC(DDEV, ALERT) << "No bladerf devices found with identifier '" << cfg->dev_args << "'";
+ if (num_devs) {
+ for (int i = 0; i < num_devs; i++)
+ LOGC(DDEV, ALERT) << "Found device:" << info[i].product << " serial " << info[i].serial;
+ }
+
+ return -1;
+ }
+ if (strcmp("bladerf2", bladerf_get_board_name(dev))) {
+ LOGC(DDEV, ALERT) << "Only BladeRF2 supported! found:" << bladerf_get_board_name(dev);
+ return -1;
+ }
+
+ dev_type = blade_dev_type::BLADE2;
+ tx_window = TX_WINDOW_FIXED;
+ update_band_dev(dev_key(dev_type, tx_sps, rx_sps));
+
+ struct bladerf_devinfo info;
+ bladerf_get_devinfo(dev, &info);
+ LOGC(DDEV, INFO) << "Using discovered bladerf device " << info.serial;
+
+ tx_freqs.resize(chans);
+ rx_freqs.resize(chans);
+ tx_gains.resize(chans);
+ rx_gains.resize(chans);
+ rx_buffers.resize(chans);
+
+ switch (cfg->clock_ref) {
+ case REF_INTERNAL:
+ case REF_EXTERNAL:
+ break;
+ default:
+ LOGC(DDEV, ALERT) << "Invalid reference type";
+ return -1;
+ }
+
+ if (cfg->clock_ref == REF_EXTERNAL) {
+ bool is_locked;
+ int status = bladerf_set_pll_enable(dev, true);
+ CHKRET()
+ status = bladerf_set_pll_refclk(dev, 10000000);
+ CHKRET()
+ for (int i = 0; i < 20; i++) {
+ usleep(50 * 1000);
+ status = bladerf_get_pll_lock_state(dev, &is_locked);
+ CHKRET()
+ if (is_locked)
+ break;
+ }
+ if (!is_locked) {
+ LOGC(DDEV, ALERT) << "unable to lock refclk!";
+ return -1;
+ }
+ }
+
+ LOGC(DDEV, INFO)
+ << "Selected clock source is " << ((cfg->clock_ref == REF_INTERNAL) ? "internal" : "external 10Mhz");
+
+ set_rates();
+
+ /*
+ 1ts = 3/5200s
+ 1024*2 = small gap(~180us) every 9.23ms = every 16 ts? -> every 2 frames
+ 1024*1 = large gap(~627us) every 9.23ms = every 16 ts? -> every 2 frames
+
+ rif convertbuffer = 625*4 = 2500 -> 4 ts
+ rif rxtxbuf = 4 * segment(625*4) = 10000 -> 16 ts
+ */
+ const unsigned int num_buffers = 256;
+ const unsigned int buffer_size = 1024 * 4; /* Must be a multiple of 1024 */
+ const unsigned int num_transfers = 32;
+ const unsigned int timeout_ms = 3500;
+
+ bladerf_sync_config(dev, BLADERF_RX_X1, BLADERF_FORMAT_SC16_Q11_META, num_buffers, buffer_size, num_transfers,
+ timeout_ms);
+
+ bladerf_sync_config(dev, BLADERF_TX_X1, BLADERF_FORMAT_SC16_Q11_META, num_buffers, buffer_size, num_transfers,
+ timeout_ms);
+
+ /* Number of samples per over-the-wire packet */
+ tx_spp = rx_spp = buffer_size;
+
+ size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
+ for (size_t i = 0; i < rx_buffers.size(); i++)
+ rx_buffers[i] = new smpl_buf(buf_len);
+
+ pkt_bufs = std::vector<std::vector<short> >(chans, std::vector<short>(2 * rx_spp));
+ for (size_t i = 0; i < pkt_bufs.size(); i++)
+ pkt_ptrs.push_back(&pkt_bufs[i].front());
+
+ init_gains();
+
+ return NORMAL;
+}
+
+bool blade_device::restart()
+{
+ /* Allow 100 ms delay to align multi-channel streams */
+ double delay = 0.2;
+ int status;
+
+ status = bladerf_enable_module(dev, BLADERF_CHANNEL_RX(0), true);
+ CHKRET()
+ status = bladerf_enable_module(dev, BLADERF_CHANNEL_TX(0), true);
+ CHKRET()
+
+ bladerf_timestamp now;
+ status = bladerf_get_timestamp(dev, BLADERF_RX, &now);
+ ts_initial = now + rx_rate * delay;
+ LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
+
+ return true;
+}
+
+bool blade_device::start()
+{
+ LOGC(DDEV, INFO) << "Starting USRP...";
+
+ if (started) {
+ LOGC(DDEV, ERROR) << "Device already started";
+ return false;
+ }
+
+ if (!restart())
+ return false;
+
+ started = true;
+ return true;
+}
+
+bool blade_device::stop()
+{
+ if (!started)
+ return false;
+
+ /* reset internal buffer timestamps */
+ for (size_t i = 0; i < rx_buffers.size(); i++)
+ rx_buffers[i]->reset();
+
+ band_reset();
+
+ started = false;
+ return true;
+}
+
+int blade_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun)
+{
+ ssize_t rc;
+ uint64_t ts;
+
+ if (bufs.size() != chans) {
+ LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
+ return -1;
+ }
+
+ *overrun = false;
+ *underrun = false;
+
+ // Shift read time with respect to transmit clock
+ timestamp += ts_offset;
+
+ ts = timestamp;
+ LOGC(DDEV, DEBUG) << "Requested timestamp = " << ts;
+
+ // 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;
+ }
+
+ struct bladerf_metadata meta = {};
+ meta.timestamp = ts;
+
+ while (rx_buffers[0]->avail_smpls(timestamp) < len) {
+ thread_enable_cancel(false);
+ int status = bladerf_sync_rx(dev, pkt_ptrs[0], len, &meta, 200U);
+ thread_enable_cancel(true);
+
+ if (status != 0)
+ LOGC(DDEV, ERROR) << "RX broken: " << bladerf_strerror(status);
+ if (meta.flags & BLADERF_META_STATUS_OVERRUN)
+ LOGC(DDEV, ERROR) << "RX borken, OVERRUN: " << bladerf_strerror(status);
+
+ size_t num_smpls = meta.actual_count;
+ ;
+ ts = meta.timestamp;
+
+ for (size_t i = 0; i < rx_buffers.size(); i++) {
+ rc = rx_buffers[i]->write((short *)&pkt_bufs[i].front(), num_smpls, ts);
+
+ // Continue on local overrun, exit on other errors
+ if ((rc < 0)) {
+ LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
+ LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
+ if (rc != smpl_buf::ERROR_OVERFLOW)
+ return 0;
+ }
+ }
+ meta = {};
+ meta.timestamp = ts + num_smpls;
+ }
+
+ for (size_t i = 0; i < rx_buffers.size(); i++) {
+ rc = rx_buffers[i]->read(bufs[i], len, timestamp);
+ if ((rc < 0) || (rc != len)) {
+ LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
+ LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
+ return 0;
+ }
+ }
+
+ return len;
+}
+
+int blade_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun, unsigned long long timestamp)
+{
+ *underrun = false;
+ static bool first_tx = true;
+ struct bladerf_metadata meta = {};
+ if (first_tx) {
+ meta.timestamp = timestamp;
+ meta.flags = BLADERF_META_FLAG_TX_BURST_START;
+ first_tx = false;
+ }
+
+ thread_enable_cancel(false);
+ int status = bladerf_sync_tx(dev, (const void *)bufs[0], len, &meta, 200U);
+ thread_enable_cancel(true);
+
+ if (status != 0)
+ LOGC(DDEV, ERROR) << "TX broken: " << bladerf_strerror(status);
+
+ return len;
+}
+
+bool blade_device::updateAlignment(TIMESTAMP timestamp)
+{
+ return true;
+}
+
+bool blade_device::set_freq(double freq, size_t chan, bool tx)
+{
+ if (tx) {
+ bladerf_set_frequency(dev, BLADERF_CHANNEL_TX(chan), freq);
+ bladerf_frequency f;
+ bladerf_get_frequency(dev, BLADERF_CHANNEL_TX(chan), &f);
+ tx_freqs[chan] = f;
+ } else {
+ bladerf_set_frequency(dev, BLADERF_CHANNEL_RX(chan), freq);
+ bladerf_frequency f;
+ bladerf_get_frequency(dev, BLADERF_CHANNEL_RX(chan), &f);
+ rx_freqs[chan] = f;
+ }
+ LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << (tx ? "TX" : "RX") << "): " << std::endl;
+
+ return true;
+}
+
+bool blade_device::setTxFreq(double wFreq, size_t chan)
+{
+ if (chan >= tx_freqs.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return false;
+ }
+ ScopedLock lock(tune_lock);
+
+ if (!update_band_from_freq(wFreq, chan, true))
+ return false;
+
+ if (!set_freq(wFreq, chan, true))
+ return false;
+
+ return true;
+}
+
+bool blade_device::setRxFreq(double wFreq, size_t chan)
+{
+ if (chan >= rx_freqs.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return false;
+ }
+ ScopedLock lock(tune_lock);
+
+ if (!update_band_from_freq(wFreq, chan, false))
+ return false;
+
+ return set_freq(wFreq, chan, false);
+}
+
+double blade_device::getTxFreq(size_t chan)
+{
+ if (chan >= tx_freqs.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return 0.0;
+ }
+
+ return tx_freqs[chan];
+}
+
+double blade_device::getRxFreq(size_t chan)
+{
+ if (chan >= rx_freqs.size()) {
+ LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
+ return 0.0;
+ }
+
+ return rx_freqs[chan];
+}
+
+bool blade_device::requiresRadioAlign()
+{
+ return false;
+}
+
+GSM::Time blade_device::minLatency()
+{
+ return GSM::Time(6, 7);
+}
+
+TIMESTAMP blade_device::initialWriteTimestamp()
+{
+ return ts_initial;
+}
+
+TIMESTAMP blade_device::initialReadTimestamp()
+{
+ return ts_initial;
+}
+
+double blade_device::fullScaleInputValue()
+{
+ return (double)2047;
+}
+
+double blade_device::fullScaleOutputValue()
+{
+ return (double)2047;
+}
+
+RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
+{
+ return new blade_device(type, cfg);
+}
diff --git a/Transceiver52M/device/bladerf/bladerf.h b/Transceiver52M/device/bladerf/bladerf.h
new file mode 100644
index 0000000..e32581e
--- /dev/null
+++ b/Transceiver52M/device/bladerf/bladerf.h
@@ -0,0 +1,195 @@
+/*
+ * Copyright 2022 sysmocom - s.f.m.c. GmbH
+ *
+ * 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.
+ */
+
+#pragma once
+
+#include <map>
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include "bandmanager.h"
+#include "radioDevice.h"
+#include "smpl_buf.h"
+
+extern "C" {
+#include <osmocom/gsm/gsm_utils.h>
+}
+
+enum class blade_dev_type { BLADE1, BLADE2 };
+
+struct dev_band_desc {
+ /* Maximum UHD Tx Gain which can be set/used without distorting the
+ output signal, and the resulting real output power measured when that
+ gain is used. Correct measured values only provided for B210 so far. */
+ double nom_uhd_tx_gain; /* dB */
+ double nom_out_tx_power; /* dBm */
+ /* Factor used to infer base real RSSI offset on the Rx path based on current
+ configured RxGain. The resulting rssiOffset is added to the per burst
+ calculated energy in upper layers. These values were empirically
+ found and may change based on multiple factors, see OS#4468.
+ rssiOffset = rxGain + rxgain2rssioffset_rel;
+ */
+ double rxgain2rssioffset_rel; /* dB */
+};
+
+/* Device parameter descriptor */
+struct dev_desc {
+ unsigned channels;
+ double mcr;
+ double rate;
+ double offset;
+ std::string desc_str;
+};
+
+using dev_key = std::tuple<blade_dev_type, int, int>;
+using dev_band_key = std::tuple<blade_dev_type, enum gsm_band>;
+using power_map_t = std::map<dev_band_key, dev_band_desc>;
+using dev_map_t = std::map<dev_key, dev_desc>;
+
+class blade_device : public RadioDevice, public band_manager<power_map_t, dev_map_t> {
+ public:
+ blade_device(InterfaceType iface, const struct trx_cfg *cfg);
+ ~blade_device();
+
+ int open();
+ bool start();
+ bool stop();
+ bool restart();
+ enum TxWindowType getWindowType()
+ {
+ return tx_window;
+ }
+
+ int readSamples(std::vector<short *> &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun);
+
+ int writeSamples(std::vector<short *> &bufs, int len, bool *underrun, TIMESTAMP timestamp);
+
+ bool updateAlignment(TIMESTAMP timestamp);
+
+ bool setTxFreq(double wFreq, size_t chan);
+ bool setRxFreq(double wFreq, size_t chan);
+
+ TIMESTAMP initialWriteTimestamp();
+ TIMESTAMP initialReadTimestamp();
+
+ double fullScaleInputValue();
+ double fullScaleOutputValue();
+
+ double setRxGain(double db, size_t chan);
+ double getRxGain(size_t chan);
+ double maxRxGain(void)
+ {
+ return rx_gain_max;
+ }
+ double minRxGain(void)
+ {
+ return rx_gain_min;
+ }
+ double rssiOffset(size_t chan);
+
+ double setPowerAttenuation(int atten, size_t chan);
+ double getPowerAttenuation(size_t chan = 0);
+
+ int getNominalTxPower(size_t chan = 0);
+
+ double getTxFreq(size_t chan);
+ double getRxFreq(size_t chan);
+ double getRxFreq();
+
+ bool setRxAntenna(const std::string &ant, size_t chan)
+ {
+ return {};
+ };
+ std::string getRxAntenna(size_t chan)
+ {
+ return {};
+ };
+ bool setTxAntenna(const std::string &ant, size_t chan)
+ {
+ return {};
+ };
+ std::string getTxAntenna(size_t chan)
+ {
+ return {};
+ };
+
+ bool requiresRadioAlign();
+
+ GSM::Time minLatency();
+
+ inline double getSampleRate()
+ {
+ return tx_rate;
+ }
+
+ /** Receive and process asynchronous message
+ @return true if message received or false on timeout or error
+ */
+ bool recv_async_msg();
+
+ enum err_code {
+ ERROR_TIMING = -1,
+ ERROR_TIMEOUT = -2,
+ ERROR_UNRECOVERABLE = -3,
+ ERROR_UNHANDLED = -4,
+ };
+
+ protected:
+ struct bladerf *dev;
+ void *usrp_dev;
+
+ enum TxWindowType tx_window;
+ enum blade_dev_type dev_type;
+
+ double tx_rate, rx_rate;
+
+ double rx_gain_min, rx_gain_max;
+
+ std::vector<double> tx_gains, rx_gains;
+ std::vector<double> tx_freqs, rx_freqs;
+ size_t tx_spp, rx_spp;
+
+ bool started;
+ bool aligned;
+
+ size_t drop_cnt;
+ uint64_t prev_ts;
+
+ TIMESTAMP ts_initial, ts_offset;
+ std::vector<smpl_buf *> rx_buffers;
+ /* Sample buffers used to receive samples: */
+ std::vector<std::vector<short> > pkt_bufs;
+ /* Used to call UHD API: Buffer pointer of each elem in pkt_ptrs will
+ point to corresponding buffer of vector pkt_bufs. */
+ std::vector<short *> pkt_ptrs;
+
+ void init_gains();
+ void set_channels(bool swap);
+ void set_rates();
+ bool flush_recv(size_t num_pkts);
+
+ bool set_freq(double freq, size_t chan, bool tx);
+
+ Thread *async_event_thrd;
+ Mutex tune_lock;
+};
diff --git a/Transceiver52M/device/common/Makefile.am b/Transceiver52M/device/common/Makefile.am
index 4d29e98..1a33592 100644
--- a/Transceiver52M/device/common/Makefile.am
+++ b/Transceiver52M/device/common/Makefile.am
@@ -4,7 +4,7 @@ AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
-noinst_HEADERS = radioDevice.h smpl_buf.h
+noinst_HEADERS = radioDevice.h smpl_buf.h bandmanager.h
noinst_LTLIBRARIES = libdevice_common.la
diff --git a/Transceiver52M/device/common/bandmanager.h b/Transceiver52M/device/common/bandmanager.h
new file mode 100644
index 0000000..d07f21d
--- /dev/null
+++ b/Transceiver52M/device/common/bandmanager.h
@@ -0,0 +1,137 @@
+#pragma once
+/*
+ * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Eric Wild <ewild@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <string>
+#include <tuple>
+
+#include "Logger.h"
+
+extern "C" {
+#include <osmocom/gsm/gsm_utils.h>
+}
+
+template <typename powermapt, typename devmapt>
+class band_manager {
+ using powerkeyt = typename powermapt::key_type;
+ using powermappedt = typename powermapt::mapped_type;
+ using devkeyt = typename devmapt::key_type;
+ devkeyt m_dev_type;
+ const powermapt &m_power_map;
+ const devmapt &m_dev_map;
+ powerkeyt m_fallback;
+ enum gsm_band m_band;
+ powermappedt m_band_desc;
+ bool band_ass_curr_sess{}; /* true if "band" was set after last POWEROFF */
+
+ // looks up either first tuple element (->enum) or straight enum
+ template <typename T, typename std::enable_if<std::is_enum<T>::value>::type *dummy = nullptr>
+ auto key_helper(T &t) -> T
+ {
+ return t;
+ }
+
+ template <typename T>
+ auto key_helper(T t) -> typename std::tuple_element<0, T>::type
+ {
+ return std::get<0>(t);
+ }
+
+ void assign_band_desc(enum gsm_band req_band)
+ {
+ auto key = key_helper(m_dev_type);
+ auto fallback_key = key_helper(m_fallback);
+ auto it = m_power_map.find({ key, req_band });
+ if (it == m_power_map.end()) {
+ auto desc = m_dev_map.at(m_dev_type);
+ LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device " << desc.desc_str
+ << " on band " << gsm_band_name(req_band) << ", using fallback..";
+ it = m_power_map.find({ fallback_key, req_band });
+ }
+ OSMO_ASSERT(it != m_power_map.end());
+ m_band_desc = it->second;
+ }
+
+ bool set_band(enum gsm_band req_band)
+ {
+ if (band_ass_curr_sess && req_band != m_band) {
+ LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
+ << " different from previous band " << gsm_band_name(m_band);
+ return false;
+ }
+
+ if (req_band != m_band) {
+ m_band = req_band;
+ assign_band_desc(m_band);
+ }
+ band_ass_curr_sess = true;
+ return true;
+ }
+
+ public:
+ band_manager(const devkeyt &dev_type, const powermapt &power_map, const devmapt &dev_map, powerkeyt fallback)
+ : m_dev_type(dev_type), m_power_map(power_map), m_dev_map(dev_map), m_fallback(fallback),
+ m_band((enum gsm_band)0)
+ {
+ }
+ band_manager(const powermapt &power_map, const devmapt &dev_map)
+ : m_dev_type(dev_map.begin()->first), m_power_map(power_map), m_dev_map(dev_map),
+ m_fallback(m_power_map.begin()->first), m_band((enum gsm_band)0)
+ {
+ }
+ void band_reset()
+ {
+ band_ass_curr_sess = false;
+ }
+
+ void update_band_dev(devkeyt dev_type) {
+ m_dev_type = dev_type;
+ }
+
+ void get_dev_band_desc(powermappedt &desc)
+ {
+ if (m_band == 0) {
+ LOGC(DDEV, ERROR)
+ << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
+ assign_band_desc(GSM_BAND_900);
+ }
+ desc = m_band_desc;
+ }
+
+ bool update_band_from_freq(double wFreq, int chan, bool is_tx)
+ {
+ enum gsm_band req_band;
+ auto dirstr = is_tx ? "Tx" : "Rx";
+ auto req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, !is_tx);
+ if (req_arfcn == 0xffff) {
+ LOGCHAN(chan, DDEV, ALERT)
+ << "Unknown ARFCN for " << dirstr << " Frequency " << wFreq / 1000 << " kHz";
+ return false;
+ }
+ if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
+ LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for " << dirstr << " Frequency " << wFreq
+ << " Hz (ARFCN " << req_arfcn << " )";
+ return false;
+ }
+
+ return set_band(req_band);
+ }
+}; \ No newline at end of file
diff --git a/Transceiver52M/device/common/radioDevice.h b/Transceiver52M/device/common/radioDevice.h
index 3f5da1f..5c962d1 100644
--- a/Transceiver52M/device/common/radioDevice.h
+++ b/Transceiver52M/device/common/radioDevice.h
@@ -51,13 +51,10 @@ class RadioDevice {
MULTI_ARFCN,
};
- static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type,
- size_t chans = 1, double offset = 0.0,
- const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""),
- const std::vector<std::string>& rx_paths = std::vector<std::string>(1, ""));
+ static RadioDevice *make(InterfaceType type, const struct trx_cfg *cfg);
/** Initialize the USRP */
- virtual int open(const std::string &args, int ref, bool swap_channels)=0;
+ virtual int open() = 0;
virtual ~RadioDevice() { }
@@ -164,24 +161,30 @@ class RadioDevice {
double lo_offset;
std::vector<std::string> tx_paths, rx_paths;
std::vector<struct device_counters> m_ctr;
-
- RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chan_num, double offset,
- const std::vector<std::string>& tx_paths,
- const std::vector<std::string>& rx_paths):
- tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chan_num), lo_offset(offset),
- tx_paths(tx_paths), rx_paths(rx_paths)
- {
- if (iface == MULTI_ARFCN) {
- LOGC(DDEV, INFO) << "Multi-ARFCN: "<< chan_num << " logical chans -> 1 physical chans";
- chans = 1;
- }
-
- m_ctr.resize(chans);
- for (size_t i = 0; i < chans; i++) {
- memset(&m_ctr[i], 0, sizeof(m_ctr[i]));
- m_ctr[i].chan = i;
- }
- }
+ const struct trx_cfg *cfg;
+
+#define charp2str(a) ((a) ? std::string(a) : std::string(""))
+
+ RadioDevice(InterfaceType type, const struct trx_cfg *cfg)
+ : tx_sps(cfg->tx_sps), rx_sps(cfg->rx_sps), iface(type), chans(cfg->num_chans), lo_offset(cfg->offset),
+ m_ctr(chans), cfg(cfg)
+ {
+ /* Generate vector of rx/tx_path: */
+ for (unsigned int i = 0; i < cfg->num_chans; i++) {
+ rx_paths.push_back(charp2str(cfg->chans[i].rx_path));
+ tx_paths.push_back(charp2str(cfg->chans[i].tx_path));
+ }
+
+ if (iface == MULTI_ARFCN) {
+ LOGC(DDEV, INFO) << "Multi-ARFCN: " << chans << " logical chans -> 1 physical chans";
+ chans = 1;
+ }
+
+ for (size_t i = 0; i < chans; i++) {
+ memset(&m_ctr[i], 0, sizeof(m_ctr[i]));
+ m_ctr[i].chan = i;
+ }
+ }
bool set_antennas() {
unsigned int i;
diff --git a/Transceiver52M/device/ipc/IPCDevice.cpp b/Transceiver52M/device/ipc/IPCDevice.cpp
index 102cb9b..1d2b89a 100644
--- a/Transceiver52M/device/ipc/IPCDevice.cpp
+++ b/Transceiver52M/device/ipc/IPCDevice.cpp
@@ -56,20 +56,13 @@ using namespace std;
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,
- 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), tx_attenuation(),
- tmp_state(IPC_IF_MSG_GREETING_REQ), shm(NULL), shm_dec(0), started(false)
+IPCDevice::IPCDevice(InterfaceType iface, const struct trx_cfg *cfg)
+ : RadioDevice(iface, cfg), sk_chan_state(chans, ipc_per_trx_sock_state()), tx_attenuation(),
+ tmp_state(IPC_IF_MSG_GREETING_REQ), shm(NULL), shm_dec(0), rx_buffers(chans), started(false), tx_gains(chans),
+ rx_gains(chans)
{
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 */
for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
@@ -100,12 +93,14 @@ IPCDevice::~IPCDevice()
int IPCDevice::ipc_shm_connect(const char *shm_name)
{
int fd;
+ char err_buf[256];
size_t shm_len;
int rc;
LOGP(DDEV, LOGL_NOTICE, "Opening shm path %s\n", shm_name);
if ((fd = shm_open(shm_name, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR)) < 0) {
- LOGP(DDEV, LOGL_ERROR, "shm_open %d: %s\n", errno, strerror(errno));
+ LOGP(DDEV, LOGL_ERROR, "shm_open %d: %s\n", errno,
+ strerror_r(errno, err_buf, sizeof(err_buf)));
rc = -errno;
goto err_shm_open;
}
@@ -113,7 +108,8 @@ int IPCDevice::ipc_shm_connect(const char *shm_name)
// Get size of the allocated memory
struct stat shm_stat;
if (fstat(fd, &shm_stat) < 0) {
- LOGP(DDEV, LOGL_ERROR, "fstat %d: %s\n", errno, strerror(errno));
+ LOGP(DDEV, LOGL_ERROR, "fstat %d: %s\n", errno,
+ strerror_r(errno, err_buf, sizeof(err_buf)));
rc = -errno;
goto err_mmap;
}
@@ -122,7 +118,8 @@ int IPCDevice::ipc_shm_connect(const char *shm_name)
LOGP(DDEV, LOGL_NOTICE, "mmaping shared memory fd %d (size=%zu)\n", fd, shm_len);
if ((shm = mmap(NULL, shm_len, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)) == MAP_FAILED) {
- LOGP(DDEV, LOGL_ERROR, "mmap %d: %s\n", errno, strerror(errno));
+ LOGP(DDEV, LOGL_ERROR, "mmap %d: %s\n", errno,
+ strerror_r(errno, err_buf, sizeof(err_buf)));
rc = -errno;
goto err_mmap;
}
@@ -522,7 +519,7 @@ static int ipc_sock_send(struct ipc_per_trx_sock_state *state, struct msgb *msg)
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
- conn_bfd->when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(conn_bfd);
return 0;
}
@@ -539,9 +536,9 @@ void IPCDevice::ipc_sock_close(struct ipc_per_trx_sock_state *state)
LOGP(DDEV, LOGL_NOTICE, "IPC socket has LOST connection\n");
+ osmo_fd_unregister(bfd);
close(bfd->fd);
bfd->fd = -1;
- osmo_fd_unregister(bfd);
/* flush the queue */
while (!llist_empty(&state->upqueue)) {
@@ -659,7 +656,7 @@ int IPCDevice::ipc_sock_write(struct osmo_fd *bfd)
msg = llist_entry(master_sk_state.upqueue.next, struct msgb, list);
ipc_prim = (struct ipc_sk_if *)msg->data;
- bfd->when &= ~OSMO_FD_WRITE;
+ osmo_fd_write_disable(bfd);
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
@@ -676,7 +673,7 @@ int IPCDevice::ipc_sock_write(struct osmo_fd *bfd)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
- bfd->when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(bfd);
break;
}
goto close;
@@ -706,7 +703,7 @@ int IPCDevice::ipc_chan_sock_write(struct osmo_fd *bfd)
/* peek at the beginning of the queue */
msg = llist_entry(sk_chan_state[bfd->priv_nr].upqueue.next, struct msgb, list);
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
- bfd->when &= ~OSMO_FD_WRITE;
+ osmo_fd_write_disable(bfd);
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
LOGP(DDEV, LOGL_ERROR,
@@ -722,7 +719,7 @@ int IPCDevice::ipc_chan_sock_write(struct osmo_fd *bfd)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
- bfd->when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(bfd);
break;
}
goto close;
@@ -773,11 +770,12 @@ static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags)
return rc;
}
-int IPCDevice::open(const std::string &args, int ref, bool swap_channels)
+int IPCDevice::open()
{
std::string k, v;
std::string::size_type keyend;
int rc;
+ std::string args(cfg->dev_args);
if ((keyend = args.find('=')) != std::string::npos) {
k = args.substr(0, keyend++);
@@ -793,7 +791,7 @@ int IPCDevice::open(const std::string &args, int ref, bool swap_channels)
INIT_LLIST_HEAD(&master_sk_state.upqueue);
rc = osmo_sock_unix_init_ofd(&master_sk_state.conn_bfd, SOCK_SEQPACKET, 0, v.c_str(), OSMO_SOCK_F_CONNECT);
if (rc < 0) {
- LOGC(DDEV, ERROR) << "Failed to connect to the BTS (" << v << "). "
+ LOGC(DDEV, ERROR) << "Failed to connect to the IPC device (" << v << "). "
<< "Retrying...\n";
osmo_timer_setup(&master_sk_state.timer, ipc_sock_timeout, NULL);
osmo_timer_schedule(&master_sk_state.timer, 5, 0);
@@ -812,7 +810,7 @@ int IPCDevice::open(const std::string &args, int ref, bool swap_channels)
while (tmp_state != IPC_IF_MSG_INFO_CNF)
osmo_select_main(0);
- ipc_tx_open_req(&master_sk_state, chans, ref);
+ ipc_tx_open_req(&master_sk_state, chans, cfg->clock_ref);
/* Wait until confirmation is recieved */
while (tmp_state != IPC_IF_MSG_OPEN_CNF)
osmo_select_main(0);
@@ -835,6 +833,7 @@ void IPCDevice::manually_poll_sock_fds()
{
struct timeval wait = { 0, 100000 };
fd_set crfds, cwfds;
+ char err_buf[256];
int max_fd = 0;
FD_ZERO(&crfds);
@@ -849,7 +848,11 @@ void IPCDevice::manually_poll_sock_fds()
FD_SET(curr_fd->fd, &cwfds);
}
- select(max_fd + 1, &crfds, &cwfds, 0, &wait);
+ if (select(max_fd + 1, &crfds, &cwfds, 0, &wait) < 0) {
+ LOGP(DDEV, LOGL_ERROR, "select() failed: %s\n",
+ strerror_r(errno, err_buf, sizeof(err_buf)));
+ return;
+ }
for (unsigned int i = 0; i < chans; i++) {
int flags = 0;
@@ -1256,16 +1259,15 @@ bool IPCDevice::setRxFreq(double wFreq, size_t chan)
return send_chan_wait_rsp(chan, msg, IPC_IF_MSG_SETFREQ_CNF);
}
-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)
+RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
{
- if (tx_sps != rx_sps) {
+ if (cfg->tx_sps != cfg->rx_sps) {
LOGC(DDEV, ERROR) << "IPC Requires tx_sps == rx_sps";
return NULL;
}
- if (lo_offset != 0.0) {
+ if (cfg->offset != 0.0) {
LOGC(DDEV, ERROR) << "IPC doesn't support lo_offset";
return NULL;
}
- return new IPCDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
+ return new IPCDevice(type, cfg);
}
diff --git a/Transceiver52M/device/ipc/IPCDevice.h b/Transceiver52M/device/ipc/IPCDevice.h
index 35279a2..0cf3d46 100644
--- a/Transceiver52M/device/ipc/IPCDevice.h
+++ b/Transceiver52M/device/ipc/IPCDevice.h
@@ -115,12 +115,11 @@ class IPCDevice : public RadioDevice {
int ipc_chan_sock_write(osmo_fd *bfd);
/** Object constructor */
- 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);
+ IPCDevice(InterfaceType iface, const struct trx_cfg *cfg);
virtual ~IPCDevice() override;
/** Instantiate the IPC */
- virtual int open(const std::string &args, int ref, bool swap_channels) override;
+ virtual int open() override;
/** Start the IPC */
virtual bool start() override;
@@ -199,7 +198,7 @@ class IPCDevice : public RadioDevice {
virtual double minRxGain(void) override;
/* FIXME: return rx_gains[chan] ? receive factor from IPC Driver? */
- double rssiOffset(size_t chan) { return 0.0f; };
+ double rssiOffset(size_t chan) override { return 0.0f; };
double setPowerAttenuation(int atten, size_t chan) override;
double getPowerAttenuation(size_t chan = 0) override;
diff --git a/Transceiver52M/device/ipc/Makefile.am b/Transceiver52M/device/ipc/Makefile.am
index 4fe0090..c08621f 100644
--- a/Transceiver52M/device/ipc/Makefile.am
+++ b/Transceiver52M/device/ipc/Makefile.am
@@ -1,9 +1,8 @@
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
+AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
+AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(UHD_CFLAGS)
+AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(UHD_CFLAGS)
noinst_HEADERS = IPCDevice.h shm.h ipc_shm.h ipc_chan.h ipc_sock.h
@@ -14,28 +13,22 @@ endif
noinst_LTLIBRARIES = libdevice.la
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_CXXFLAGS = $(AM_CXXFLAGS) -DIPCMAGIC
+libdevice_la_CPPFLAGS = $(AM_CPPFLAGS) -DIPCMAGIC
+libdevice_la_LIBADD = \
+ $(top_builddir)/Transceiver52M/device/common/libdevice_common.la \
+ -lpthread \
+ -lrt \
+ $(NULL)
if DEVICE_UHD
-#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
-
bin_PROGRAMS = ipc-driver-test
#ipc_driver_test_SHORTNAME = drvt
-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_SOURCES = ipc-driver-test.c uhdwrap.cpp ../uhd/UHDDevice.cpp
ipc_driver_test_LDADD = \
- shm.lo \
+ libdevice.la \
+ $(COMMON_LA) \
$(LIBOSMOCORE_LIBS) \
+ $(UHD_LIBS) \
$(NULL)
-ipc_driver_test_CXXFLAGS = $(AM_CXXFLAGS) $(UHD_CFLAGS)
-ipc_driver_test_CPPFLAGS = $(AM_CPPFLAGS) $(UHD_CFLAGS)
-ipc_driver_test_CFLAGS = $(AM_CFLAGS) $(UHD_CFLAGS)
-ipc_driver_test_LDFLAGS = $(AM_LDFLAGS) $(UHD_LIBS)
-ipc_driver_test_LDADD += $(top_builddir)/Transceiver52M/device/common/libdevice_common.la $(top_builddir)/CommonLibs/libcommon.la
endif
diff --git a/Transceiver52M/device/ipc/ipc-driver-test.c b/Transceiver52M/device/ipc/ipc-driver-test.c
index da53463..d8284c7 100644
--- a/Transceiver52M/device/ipc/ipc-driver-test.c
+++ b/Transceiver52M/device/ipc/ipc-driver-test.c
@@ -49,6 +49,7 @@
#include "ipc_sock.h"
#define DEFAULT_SHM_NAME "/osmo-trx-ipc-driver-shm2"
+#define IPC_SOCK_PATH_PREFIX "/tmp"
static void *tall_ctx;
struct ipc_sock_state *global_ipc_sock_state;
@@ -63,6 +64,11 @@ void *global_dev;
static struct ipc_shm_region *decoded_region;
+static struct {
+ int msocknum;
+ char *ud_prefix_dir;
+} cmdline_cfg;
+
static const struct log_info_cat default_categories[] = {
[DMAIN] = {
.name = "DMAIN",
@@ -185,8 +191,8 @@ static int ipc_tx_open_cnf(int rc, uint32_t num_chans, int32_t timingoffset)
chan_info = ipc_prim->u.open_cnf.chan_info;
for (i = 0; i < num_chans; i++) {
- snprintf(chan_info->chan_ipc_sk_path, sizeof(chan_info->chan_ipc_sk_path), "%s_%d",
- IPC_SOCK_PATH_PREFIX, i);
+ snprintf(chan_info->chan_ipc_sk_path, sizeof(chan_info->chan_ipc_sk_path),"%s/ipc_sock%d_%d",
+ cmdline_cfg.ud_prefix_dir, cmdline_cfg.msocknum, i);
/* FIXME: dynamc chan limit, currently 8 */
if (i < 8)
ipc_sock_init(chan_info->chan_ipc_sk_path, &global_ctrl_socks[i], ipc_chan_sock_accept, i);
@@ -413,20 +419,20 @@ static void print_help(void)
{
printf("ipc-driver-test Usage:\n"
" -h --help This message\n"
+ " -u --unix-sk-dir DIR Existing directory where to create the Master socket\n"
" -n --sock-num NR Master socket suffix number NR\n");
}
-static int msocknum = 0;
-
static void handle_options(int argc, char **argv)
{
while (1) {
int option_index = 0, c;
const struct option long_options[] = { { "help", 0, 0, 'h' },
+ { "unix-sk-dir", 1, 0, 'u' },
{ "sock-num", 1, 0, 'n' },
{ 0, 0, 0, 0 } };
- c = getopt_long(argc, argv, "hn:", long_options, &option_index);
+ c = getopt_long(argc, argv, "hu:n:", long_options, &option_index);
if (c == -1)
break;
@@ -435,9 +441,13 @@ static void handle_options(int argc, char **argv)
print_help();
exit(0);
break;
+ case 'u':
+ cmdline_cfg.ud_prefix_dir = talloc_strdup(tall_ctx, optarg);
+ break;
case 'n':
- msocknum = atoi(optarg);
+ cmdline_cfg.msocknum = atoi(optarg);
break;
+
default:
exit(2);
break;
@@ -452,14 +462,19 @@ static void handle_options(int argc, char **argv)
int main(int argc, char **argv)
{
- char ipc_msock_path[sizeof(IPC_SOCK_PATH_PREFIX) + 3];
+ char ipc_msock_path[128];
tall_ctx = talloc_named_const(NULL, 0, "OsmoTRX");
msgb_talloc_ctx_init(tall_ctx, 0);
osmo_init_logging2(tall_ctx, &log_infox);
log_enable_multithread();
handle_options(argc, argv);
- snprintf(ipc_msock_path, sizeof(ipc_msock_path), "%s%d", IPC_SOCK_PATH_PREFIX, msocknum);
+
+ if (!cmdline_cfg.ud_prefix_dir)
+ cmdline_cfg.ud_prefix_dir = talloc_strdup(tall_ctx, IPC_SOCK_PATH_PREFIX);
+
+
+ snprintf(ipc_msock_path, sizeof(ipc_msock_path), "%s/ipc_sock%d", cmdline_cfg.ud_prefix_dir, cmdline_cfg.msocknum);
LOGP(DMAIN, LOGL_INFO, "Starting %s\n", argv[0]);
ipc_sock_init(ipc_msock_path, &global_ipc_sock_state, ipc_sock_accept, 0);
diff --git a/Transceiver52M/device/ipc/ipc_chan.c b/Transceiver52M/device/ipc/ipc_chan.c
index 50782b9..2a6f490 100644
--- a/Transceiver52M/device/ipc/ipc_chan.c
+++ b/Transceiver52M/device/ipc/ipc_chan.c
@@ -142,7 +142,7 @@ int ipc_chan_sock_send(struct msgb *msg, uint8_t chan_nr)
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
- conn_bfd->when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(conn_bfd);
return 0;
}
@@ -160,7 +160,7 @@ static int ipc_chan_sock_write(struct osmo_fd *bfd)
msg = llist_entry(state->upqueue.next, struct msgb, list);
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
- bfd->when &= ~OSMO_FD_WRITE;
+ osmo_fd_write_disable(bfd);
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
@@ -177,7 +177,7 @@ static int ipc_chan_sock_write(struct osmo_fd *bfd)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
- bfd->when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(bfd);
break;
}
goto close;
@@ -231,7 +231,7 @@ int ipc_chan_sock_accept(struct osmo_fd *bfd, unsigned int flags)
"osmo-trx connects but we already have "
"another active connection ?!?\n");
/* We already have one IPC connected, this is all we support */
- state->listen_bfd.when &= ~OSMO_FD_READ;
+ osmo_fd_read_disable(&state->listen_bfd);
close(rc);
return 0;
}
diff --git a/Transceiver52M/device/ipc/ipc_sock.c b/Transceiver52M/device/ipc/ipc_sock.c
index b014fac..9e8ab82 100644
--- a/Transceiver52M/device/ipc/ipc_sock.c
+++ b/Transceiver52M/device/ipc/ipc_sock.c
@@ -84,7 +84,7 @@ int ipc_sock_send(struct msgb *msg)
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
- conn_bfd->when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(conn_bfd);
return 0;
}
@@ -97,12 +97,12 @@ void ipc_sock_close(struct ipc_sock_state *state)
ipc_exit_requested = 1;
+ osmo_fd_unregister(bfd);
close(bfd->fd);
bfd->fd = -1;
- osmo_fd_unregister(bfd);
/* re-enable the generation of ACCEPT for new connections */
- state->listen_bfd.when |= OSMO_FD_READ;
+ osmo_fd_read_enable(&state->listen_bfd);
/* flush the queue */
while (!llist_empty(&state->upqueue)) {
@@ -172,7 +172,7 @@ static int ipc_sock_write(struct osmo_fd *bfd)
msg = llist_entry(state->upqueue.next, struct msgb, list);
ipc_prim = (struct ipc_sk_if *)msg->data;
- bfd->when &= ~OSMO_FD_WRITE;
+ osmo_fd_write_disable(bfd);
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
@@ -189,7 +189,7 @@ static int ipc_sock_write(struct osmo_fd *bfd)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
- bfd->when |= OSMO_FD_WRITE;
+ osmo_fd_write_enable(bfd);
break;
}
goto close;
@@ -244,7 +244,7 @@ int ipc_sock_accept(struct osmo_fd *bfd, unsigned int flags)
"ip clent connects but we already have "
"another active connection ?!?\n");
/* We already have one IPC connected, this is all we support */
- state->listen_bfd.when &= ~OSMO_FD_READ;
+ osmo_fd_read_disable(&state->listen_bfd);
close(rc);
return 0;
}
diff --git a/Transceiver52M/device/ipc/shm.h b/Transceiver52M/device/ipc/shm.h
index fcddd54..46c3add 100644
--- a/Transceiver52M/device/ipc/shm.h
+++ b/Transceiver52M/device/ipc/shm.h
@@ -79,8 +79,6 @@ struct ipc_shm_region *ipc_shm_decode_region(void *tall_ctx, struct ipc_shm_raw_
//////////////////
// Master socket
//////////////////
-
-#define IPC_SOCK_PATH_PREFIX "/tmp/ipc_sock"
#define IPC_SOCK_API_VERSION 1
/* msg_type */
diff --git a/Transceiver52M/device/ipc/uhdwrap.cpp b/Transceiver52M/device/ipc/uhdwrap.cpp
index d7114da..302f763 100644
--- a/Transceiver52M/device/ipc/uhdwrap.cpp
+++ b/Transceiver52M/device/ipc/uhdwrap.cpp
@@ -35,9 +35,12 @@ extern "C" {
#include "Threads.h"
#include "Utils.h"
-int uhd_wrap::open(const std::string &args, int ref, bool swap_channels)
+// no vty source for cfg params here, so we have to build our own
+static struct trx_cfg actual_cfg = {};
+
+int uhd_wrap::open()
{
- int rv = uhd_device::open(args, ref, swap_channels);
+ int rv = uhd_device::open();
samps_per_buff_rx = rx_stream->get_max_num_samps();
samps_per_buff_tx = tx_stream->get_max_num_samps();
channel_count = usrp_dev->get_rx_num_channels();
@@ -84,36 +87,33 @@ int uhd_wrap::wrap_read(TIMESTAMP *timestamp)
extern "C" void *uhdwrap_open(struct ipc_sk_if_open_req *open_req)
{
- unsigned int rx_sps, tx_sps;
+ actual_cfg.num_chans = open_req->num_chans;
+ actual_cfg.swap_channels = false;
+ /* FIXME: this is actually the sps value, not the sample rate!
+ * sample rate is looked up according to the sps rate by uhd backend */
+ actual_cfg.rx_sps = open_req->rx_sample_freq_num / open_req->rx_sample_freq_den;
+ actual_cfg.tx_sps = open_req->tx_sample_freq_num / open_req->tx_sample_freq_den;
/* FIXME: dev arg string* */
/* FIXME: rx frontend bw? */
/* FIXME: tx frontend bw? */
- ReferenceType cref;
switch (open_req->clockref) {
case FEATURE_MASK_CLOCKREF_EXTERNAL:
- cref = ReferenceType::REF_EXTERNAL;
+ actual_cfg.clock_ref = ReferenceType::REF_EXTERNAL;
break;
case FEATURE_MASK_CLOCKREF_INTERNAL:
default:
- cref = ReferenceType::REF_INTERNAL;
+ actual_cfg.clock_ref = ReferenceType::REF_INTERNAL;
break;
}
- std::vector<std::string> tx_paths;
- std::vector<std::string> rx_paths;
for (unsigned int i = 0; i < open_req->num_chans; i++) {
- tx_paths.push_back(open_req->chan_info[i].tx_path);
- rx_paths.push_back(open_req->chan_info[i].rx_path);
+ actual_cfg.chans[i].rx_path = open_req->chan_info[i].tx_path;
+ actual_cfg.chans[i].tx_path = open_req->chan_info[i].rx_path;
}
- /* FIXME: this is actually the sps value, not the sample rate!
- * sample rate is looked up according to the sps rate by uhd backend */
- rx_sps = open_req->rx_sample_freq_num / open_req->rx_sample_freq_den;
- tx_sps = open_req->tx_sample_freq_num / open_req->tx_sample_freq_den;
- uhd_wrap *uhd_wrap_dev =
- new uhd_wrap(tx_sps, rx_sps, RadioDevice::NORMAL, open_req->num_chans, 0.0, tx_paths, rx_paths);
- uhd_wrap_dev->open("", cref, false);
+ uhd_wrap *uhd_wrap_dev = new uhd_wrap(RadioDevice::NORMAL, &actual_cfg);
+ uhd_wrap_dev->open();
return uhd_wrap_dev;
}
diff --git a/Transceiver52M/device/ipc/uhdwrap.h b/Transceiver52M/device/ipc/uhdwrap.h
index e235fe7..44cd9aa 100644
--- a/Transceiver52M/device/ipc/uhdwrap.h
+++ b/Transceiver52M/device/ipc/uhdwrap.h
@@ -41,7 +41,7 @@ class uhd_wrap : public uhd_device {
// void ipc_sock_close() override {};
int wrap_read(TIMESTAMP *timestamp);
- virtual int open(const std::string &args, int ref, bool swap_channels) override;
+ virtual int open() override;
// bool start() override;
// bool stop() override;
diff --git a/Transceiver52M/device/lms/LMSDevice.cpp b/Transceiver52M/device/lms/LMSDevice.cpp
index 9dc3ab2..7c220d2 100644
--- a/Transceiver52M/device/lms/LMSDevice.cpp
+++ b/Transceiver52M/device/lms/LMSDevice.cpp
@@ -52,39 +52,16 @@ extern "C" {
#define LMS_DEV_SDR_MINI_PREFIX_NAME "LimeSDR-Mini"
#define LMS_DEV_NET_MICRO_PREFIX_NAME "LimeNET-Micro"
-/* Device parameter descriptor */
-struct dev_desc {
- /* Does LimeSuite allow switching the clock source for this device?
- * LimeSDR-Mini does not have switches but needs soldering to select
- * external/internal clock. Any call to LMS_SetClockFreq() will fail.
- */
- bool clock_src_switchable;
- /* Does LimeSuite allow using REF_INTERNAL for this device?
- * LimeNET-Micro does not like selecting internal clock
- */
- bool clock_src_int_usable;
- /* Sample rate coef (without having TX/RX samples per symbol into account) */
- double rate;
- /* Sample rate coef (without having TX/RX samples per symbol into account), if multi-arfcn is enabled */
- double rate_multiarfcn;
- /* Coefficient multiplied by TX sample rate in order to shift Tx time */
- double ts_offset_coef;
- /* Coefficient multiplied by TX sample rate in order to shift Tx time, if multi-arfcn is enabled */
- double ts_offset_coef_multiarfcn;
- /* Device Name Prefix as presented by LimeSuite API LMS_GetDeviceInfo() */
- std::string name_prefix;
-};
-static const std::map<enum lms_dev_type, struct dev_desc> dev_param_map {
+
+static const dev_map_t dev_param_map {
{ LMS_DEV_SDR_USB, { true, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_SDR_USB_PREFIX_NAME } },
{ LMS_DEV_SDR_MINI, { false, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 8.2e-5, LMS_DEV_SDR_MINI_PREFIX_NAME } },
{ LMS_DEV_NET_MICRO, { true, false, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_NET_MICRO_PREFIX_NAME } },
{ LMS_DEV_UNKNOWN, { true, true, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, "UNKNOWN" } },
};
-typedef std::tuple<lms_dev_type, enum gsm_band> dev_band_key;
-typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
-static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
+static const power_map_t dev_band_nom_power_param_map {
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_850), { 73.0, 11.2, -6.0 } },
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_900), { 73.0, 10.8, -6.0 } },
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_1800), { 65.0, -3.5, -17.0 } }, /* FIXME: OS#4583: 1800Mhz is failing above TxGain=65, which is around -3.5dBm (already < 0 dBm) */
@@ -122,8 +99,8 @@ static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
enum lms_dev_type dev_type = it->first;
struct dev_desc desc = it->second;
- if (strncmp(device_info->deviceName, desc.name_prefix.c_str(), desc.name_prefix.length()) == 0) {
- LOGC(DDEV, INFO) << "Device identified as " << desc.name_prefix;
+ if (strncmp(device_info->deviceName, desc.desc_str.c_str(), desc.desc_str.length()) == 0) {
+ LOGC(DDEV, INFO) << "Device identified as " << desc.desc_str;
return dev_type;
}
it++;
@@ -131,11 +108,10 @@ static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
return LMS_DEV_UNKNOWN;
}
-LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
- const std::vector<std::string>& tx_paths,
- const std::vector<std::string>& rx_paths):
- RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
- m_lms_dev(NULL), started(false), band((enum gsm_band)0), m_dev_type(LMS_DEV_UNKNOWN)
+LMSDevice::LMSDevice(InterfaceType iface, const struct trx_cfg *cfg)
+ : RadioDevice(iface, cfg),
+ band_manager(m_dev_type, dev_band_nom_power_param_map, dev_param_map, {LMS_DEV_SDR_USB, GSM_BAND_850}), m_lms_dev(NULL),
+ started(false), m_dev_type(LMS_DEV_UNKNOWN)
{
LOGC(DDEV, INFO) << "creating LMS device...";
@@ -222,45 +198,7 @@ int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::str
return -1;
}
-void LMSDevice::assign_band_desc(enum gsm_band req_band)
-{
- dev_band_map_it it;
-
- it = dev_band_nom_power_param_map.find(dev_band_key(m_dev_type, req_band));
- if (it == dev_band_nom_power_param_map.end()) {
- dev_desc desc = dev_param_map.at(m_dev_type);
- LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device "
- << desc.name_prefix << " on band " << gsm_band_name(req_band)
- << ", using LimeSDR-USB ones as fallback";
- it = dev_band_nom_power_param_map.find(dev_band_key(LMS_DEV_SDR_USB, req_band));
- }
- OSMO_ASSERT(it != dev_band_nom_power_param_map.end());
- band_desc = it->second;
-}
-
-bool LMSDevice::set_band(enum gsm_band req_band)
-{
- if (band != 0 && req_band != band) {
- LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
- << " different from previous band " << gsm_band_name(band);
- return false;
- }
-
- band = req_band;
- assign_band_desc(band);
- return true;
-}
-
-void LMSDevice::get_dev_band_desc(dev_band_desc& desc)
-{
- if (band == 0) {
- LOGC(DDEV, ERROR) << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
- assign_band_desc(GSM_BAND_900);
- }
- desc = band_desc;
-}
-
-int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
+int LMSDevice::open()
{
lms_info_str_t* info_list;
lms_range_t range_sr;
@@ -273,11 +211,12 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
LMS_RegisterLogHandler(&lms_log_callback);
- if ((n = LMS_GetDeviceList(NULL)) < 0)
+ if ((rc = LMS_GetDeviceList(NULL)) < 0)
LOGC(DDEV, ERROR) << "LMS_GetDeviceList(NULL) failed";
- LOGC(DDEV, INFO) << "Devices found: " << n;
- if (n < 1)
+ LOGC(DDEV, INFO) << "Devices found: " << rc;
+ if (rc < 1)
return -1;
+ n = rc;
info_list = new lms_info_str_t[n];
@@ -287,9 +226,9 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
for (i = 0; i < n; i++)
LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i];
- dev_id = info_list_find(info_list, n, args);
+ dev_id = info_list_find(info_list, n, cfg->dev_args);
if (dev_id == -1) {
- LOGC(DDEV, ERROR) << "No LMS device found with address '" << args << "'";
+ LOGC(DDEV, ERROR) << "No LMS device found with address '" << cfg->dev_args << "'";
delete[] info_list;
return -1;
}
@@ -306,14 +245,15 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
m_dev_type = parse_dev_type(m_lms_dev);
dev_desc = dev_param_map.at(m_dev_type);
+ update_band_dev(m_dev_type);
- if ((ref != REF_EXTERNAL) && (ref != REF_INTERNAL)){
+ if ((cfg->clock_ref != REF_EXTERNAL) && (cfg->clock_ref != REF_INTERNAL)) {
LOGC(DDEV, ERROR) << "Invalid reference type";
goto out_close;
}
/* if reference clock is external, setup must happen _before_ calling LMS_Init */
- if (ref == REF_EXTERNAL) {
+ if (cfg->clock_ref == REF_EXTERNAL) {
LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz";
/* FIXME: Assume an external 10 MHz reference clock. make
external reference frequency configurable */
@@ -328,7 +268,7 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
}
/* if reference clock is internal, setup must happen _after_ calling LMS_Init */
- if (ref == REF_INTERNAL) {
+ if (cfg->clock_ref == REF_INTERNAL) {
LOGC(DDEV, INFO) << "Setting Internal clock reference";
/* Internal freq param is not used */
if (!do_clock_src_freq(REF_INTERNAL, 0))
@@ -464,6 +404,8 @@ bool LMSDevice::stop()
LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]);
}
+ band_reset();
+
started = false;
return true;
}
@@ -479,8 +421,8 @@ bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
break;
case REF_INTERNAL:
if (!dev_desc.clock_src_int_usable) {
- LOGC(DDEV, ERROR) << "Device type " << dev_desc.name_prefix
- << " doesn't support internal reference clock";
+ LOGC(DDEV, ERROR)
+ << "Device type " << dev_desc.desc_str << " doesn't support internal reference clock";
return false;
}
/* According to lms using LMS_CLOCK_EXTREF with a
@@ -498,8 +440,8 @@ bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
if (LMS_SetClockFreq(m_lms_dev, lms_clk_id, freq) < 0)
return false;
} else {
- LOGC(DDEV, INFO) << "Device type " << dev_desc.name_prefix
- << " doesn't support switching clock source through SW";
+ LOGC(DDEV, INFO)
+ << "Device type " << dev_desc.desc_str << " doesn't support switching clock source through SW";
}
return true;
@@ -992,9 +934,6 @@ bool LMSDevice::updateAlignment(TIMESTAMP timestamp)
bool LMSDevice::setTxFreq(double wFreq, size_t chan)
{
- uint16_t req_arfcn;
- enum gsm_band req_band;
-
if (chan >= chans) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
@@ -1002,29 +941,14 @@ bool LMSDevice::setTxFreq(double wFreq, size_t chan)
LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz";
- req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
- if (req_arfcn == 0xffff) {
- LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
- return false;
- }
- if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
- LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
- << " Hz (ARFCN " << req_arfcn << " )";
+ if (!update_band_from_freq(wFreq, chan, true))
return false;
- }
-
- if (band != 0 && req_band != band) {
- LOGCHAN(chan, DDEV, ALERT) << "Requesting Tx Frequency " << wFreq
- << " Hz different from previous band " << gsm_band_name(band);
- return false;
- }
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
LOGCHAN(chan, DDEV, ERROR) << "Error setting Tx Freq to " << wFreq << " Hz";
return false;
}
- band = req_band;
return true;
}
@@ -1032,6 +956,9 @@ bool LMSDevice::setRxFreq(double wFreq, size_t chan)
{
LOGCHAN(chan, DDEV, NOTICE) << "Setting Rx Freq to " << wFreq << " Hz";
+ if (!update_band_from_freq(wFreq, chan, false))
+ return false;
+
if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) {
LOGCHAN(chan, DDEV, ERROR) << "Error setting Rx Freq to " << wFreq << " Hz";
return false;
@@ -1040,18 +967,15 @@ bool LMSDevice::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)
+RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
{
- if (tx_sps != rx_sps) {
- LOGC(DDEV, ERROR) << "LMS Requires tx_sps == rx_sps";
+ if (cfg->tx_sps != cfg->rx_sps) {
+ LOGC(DDEV, ERROR) << "LMS requires tx_sps == rx_sps";
return NULL;
}
- if (lo_offset != 0.0) {
+ if (cfg->offset != 0.0) {
LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset";
return NULL;
}
- return new LMSDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
+ return new LMSDevice(type, cfg);
}
diff --git a/Transceiver52M/device/lms/LMSDevice.h b/Transceiver52M/device/lms/LMSDevice.h
index 4ce8ed6..2e5ca4c 100644
--- a/Transceiver52M/device/lms/LMSDevice.h
+++ b/Transceiver52M/device/lms/LMSDevice.h
@@ -18,11 +18,13 @@
#ifndef _LMS_DEVICE_H_
#define _LMS_DEVICE_H_
+#include <map>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "radioDevice.h"
+#include "bandmanager.h"
#include "smpl_buf.h"
#include <sys/time.h>
@@ -69,8 +71,35 @@ struct dev_band_desc {
double rxgain2rssioffset_rel; /* dB */
};
+/* Device parameter descriptor */
+struct dev_desc {
+ /* Does LimeSuite allow switching the clock source for this device?
+ * LimeSDR-Mini does not have switches but needs soldering to select
+ * external/internal clock. Any call to LMS_SetClockFreq() will fail.
+ */
+ bool clock_src_switchable;
+ /* Does LimeSuite allow using REF_INTERNAL for this device?
+ * LimeNET-Micro does not like selecting internal clock
+ */
+ bool clock_src_int_usable;
+ /* Sample rate coef (without having TX/RX samples per symbol into account) */
+ double rate;
+ /* Sample rate coef (without having TX/RX samples per symbol into account), if multi-arfcn is enabled */
+ double rate_multiarfcn;
+ /* Coefficient multiplied by TX sample rate in order to shift Tx time */
+ double ts_offset_coef;
+ /* Coefficient multiplied by TX sample rate in order to shift Tx time, if multi-arfcn is enabled */
+ double ts_offset_coef_multiarfcn;
+ /* Device Name Prefix as presented by LimeSuite API LMS_GetDeviceInfo() */
+ std::string desc_str;
+};
+
+using dev_band_key_t = std::tuple<lms_dev_type, gsm_band>;
+using power_map_t = std::map<dev_band_key_t, dev_band_desc>;
+using dev_map_t = std::map<lms_dev_type, struct dev_desc>;
+
/** A class to handle a LimeSuite supported device */
-class LMSDevice:public RadioDevice {
+class LMSDevice:public RadioDevice, public band_manager<power_map_t, dev_map_t> {
private:
lms_device_t *m_lms_dev;
@@ -87,8 +116,6 @@ private:
TIMESTAMP ts_initial, ts_offset;
std::vector<double> tx_gains, rx_gains;
- enum gsm_band band;
- struct dev_band_desc band_desc;
enum lms_dev_type m_dev_type;
@@ -100,29 +127,25 @@ private:
void update_stream_stats_rx(size_t chan, bool *overrun);
void update_stream_stats_tx(size_t chan, bool *underrun);
bool do_clock_src_freq(enum ReferenceType ref, double freq);
- void get_dev_band_desc(dev_band_desc& desc);
- bool set_band(enum gsm_band req_band);
- void assign_band_desc(enum gsm_band req_band);
public:
/** Object constructor */
- LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
- const std::vector<std::string>& tx_paths,
- const std::vector<std::string>& rx_paths);
- ~LMSDevice();
+ LMSDevice(InterfaceType iface, const struct trx_cfg *cfg);
+ ~LMSDevice();
- /** Instantiate the LMS */
- int open(const std::string &args, int ref, bool swap_channels);
+ /** Instantiate the LMS */
+ int open();
- /** Start the LMS */
- bool start();
+ /** Start the LMS */
+ bool start();
- /** Stop the LMS */
- bool stop();
+ /** Stop the LMS */
+ bool stop();
- enum TxWindowType getWindowType() {
- return TX_WINDOW_LMS1;
- }
+ enum TxWindowType getWindowType()
+ {
+ return TX_WINDOW_LMS1;
+ }
/**
Read samples from the LMS.
diff --git a/Transceiver52M/device/uhd/UHDDevice.cpp b/Transceiver52M/device/uhd/UHDDevice.cpp
index 010fa8c..85e9e38 100644
--- a/Transceiver52M/device/uhd/UHDDevice.cpp
+++ b/Transceiver52M/device/uhd/UHDDevice.cpp
@@ -91,19 +91,7 @@ extern "C" {
* USRP1 with timestamps is not supported by UHD.
*/
-/* Device Type, Tx-SPS, Rx-SPS */
-typedef std::tuple<uhd_dev_type, int, int> dev_key;
-
-/* Device parameter descriptor */
-struct dev_desc {
- unsigned channels;
- double mcr;
- double rate;
- double offset;
- std::string str;
-};
-
-static const std::map<dev_key, dev_desc> dev_param_map {
+static const dev_map_t dev_param_map {
{ std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } },
{ std::make_tuple(USRP2, 4, 1), { 1, 0.0, 390625, 7.6547e-5, "N2XX 4/1 Tx/Rx SPS" } },
{ std::make_tuple(USRP2, 4, 4), { 1, 0.0, 390625, 4.6080e-5, "N2XX 4 SPS" } },
@@ -129,9 +117,7 @@ static const std::map<dev_key, dev_desc> dev_param_map {
{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
};
-typedef std::tuple<uhd_dev_type, enum gsm_band> dev_band_key;
-typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
-static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
+static const power_map_t dev_band_nom_power_param_map {
{ std::make_tuple(B200, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
{ std::make_tuple(B200, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
{ std::make_tuple(B200, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
@@ -220,15 +206,10 @@ static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
}
-uhd_device::uhd_device(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_gain_min(0.0), rx_gain_max(0.0),
- band((enum gsm_band)0), tx_spp(0), rx_spp(0),
- started(false), aligned(false), drop_cnt(0),
- prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
+uhd_device::uhd_device(InterfaceType iface, const struct trx_cfg *cfg)
+ : RadioDevice(iface, cfg), band_manager(dev_band_nom_power_param_map, dev_param_map), rx_gain_min(0.0),
+ rx_gain_max(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), drop_cnt(0), prev_ts(0, 0),
+ ts_initial(0), ts_offset(0), async_event_thrd(NULL)
{
}
@@ -240,44 +221,6 @@ uhd_device::~uhd_device()
delete rx_buffers[i];
}
-void uhd_device::assign_band_desc(enum gsm_band req_band)
-{
- dev_band_map_it it;
-
- it = dev_band_nom_power_param_map.find(dev_band_key(dev_type, req_band));
- if (it == dev_band_nom_power_param_map.end()) {
- dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
- LOGC(DDEV, ERROR) << "No Power parameters exist for device "
- << desc.str << " on band " << gsm_band_name(req_band)
- << ", using B210 ones as fallback";
- it = dev_band_nom_power_param_map.find(dev_band_key(B210, req_band));
- }
- OSMO_ASSERT(it != dev_band_nom_power_param_map.end())
- band_desc = it->second;
-}
-
-bool uhd_device::set_band(enum gsm_band req_band)
-{
- if (band != 0 && req_band != band) {
- LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
- << " different from previous band " << gsm_band_name(band);
- return false;
- }
-
- band = req_band;
- assign_band_desc(band);
- return true;
-}
-
-void uhd_device::get_dev_band_desc(dev_band_desc& desc)
-{
- if (band == 0) {
- LOGC(DDEV, ERROR) << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
- assign_band_desc(GSM_BAND_900);
- }
- desc = band_desc;
-}
-
void uhd_device::init_gains()
{
double tx_gain_min, tx_gain_max;
@@ -342,7 +285,7 @@ void uhd_device::set_rates()
rx_rate = usrp_dev->get_rx_rate();
ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
- LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
+ LOGC(DDEV, INFO) << "Rates configured for " << desc.desc_str;
}
double uhd_device::setRxGain(double db, size_t chan)
@@ -352,6 +295,9 @@ double uhd_device::setRxGain(double db, size_t chan)
return 0.0f;
}
+ if (cfg->overrides.ul_gain_override)
+ return rx_gains[chan];
+
usrp_dev->set_rx_gain(db, chan);
rx_gains[chan] = usrp_dev->get_rx_gain(chan);
@@ -394,6 +340,9 @@ double uhd_device::setPowerAttenuation(int atten, size_t chan) {
return 0.0f;
}
+ if (cfg->overrides.dl_gain_override)
+ return atten; // ensures caller does not apply digital attenuation
+
get_dev_band_desc(desc);
tx_power = desc.nom_out_tx_power - atten;
db = TxPower2TxGain(desc, tx_power);
@@ -545,9 +494,10 @@ void uhd_device::set_channels(bool swap)
}
}
-int uhd_device::open(const std::string &args, int ref, bool swap_channels)
+int uhd_device::open()
{
const char *refstr;
+ int clock_lock_attempts = 15;
/* Register msg handler. Different APIs depending on UHD version */
#ifdef USE_UHD_3_11
@@ -560,10 +510,10 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
#endif
// Find UHD devices
- uhd::device_addr_t addr(args);
+ uhd::device_addr_t addr(cfg->dev_args);
uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
if (dev_addrs.size() == 0) {
- LOGC(DDEV, ALERT) << "No UHD devices found with address '" << args << "'";
+ LOGC(DDEV, ALERT) << "No UHD devices found with address '" << cfg->dev_args << "'";
return -1;
}
@@ -572,7 +522,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
try {
usrp_dev = uhd::usrp::multi_usrp::make(addr);
} catch(uhd::key_error::exception &e) {
- LOGC(DDEV, ALERT) << "UHD make failed, device " << args << ", exception:\n" << e.what();
+ LOGC(DDEV, ALERT) << "UHD make failed, device " << cfg->dev_args << ", exception:\n" << e.what();
return -1;
}
@@ -580,14 +530,16 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
if (!parse_dev_type())
return -1;
+ update_band_dev(dev_key(dev_type, tx_sps, rx_sps));
+
if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater";
return -1;
}
try {
- set_channels(swap_channels);
- } catch (const std::exception &e) {
+ set_channels(cfg->swap_channels);
+ } catch (const std::exception &e) {
LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
return -1;
}
@@ -603,7 +555,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
rx_gains.resize(chans);
rx_buffers.resize(chans);
- switch (ref) {
+ switch (cfg->clock_ref) {
case REF_INTERNAL:
refstr = "internal";
break;
@@ -620,6 +572,19 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
usrp_dev->set_clock_source(refstr);
+ std::vector<std::string> sensor_names = usrp_dev->get_mboard_sensor_names();
+ if (std::find(sensor_names.begin(), sensor_names.end(), "ref_locked") != sensor_names.end()) {
+ LOGC(DDEV, INFO) << "Waiting for clock reference lock (max " << clock_lock_attempts << "s)..." << std::flush;
+ while (!usrp_dev->get_mboard_sensor("ref_locked", 0).to_bool() && clock_lock_attempts--)
+ sleep(1);
+
+ if (!clock_lock_attempts) {
+ LOGC(DDEV, ALERT) << "Locking to external 10Mhz failed!";
+ return -1;
+ }
+ }
+ LOGC(DDEV, INFO) << "Selected clock source is " << usrp_dev->get_clock_source(0);
+
try {
set_rates();
} catch (const std::exception &e) {
@@ -669,6 +634,32 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
// Print configuration
LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
+ if (cfg->overrides.dl_freq_override) {
+ uhd::tune_request_t treq_tx = uhd::tune_request_t(cfg->overrides.dl_freq, 0);
+ auto tres = usrp_dev->set_tx_freq(treq_tx, 0);
+ tx_freqs[0] = usrp_dev->get_tx_freq(0);
+ LOGCHAN(0, DDEV, INFO) << "OVERRIDE set_freq(" << tx_freqs[0] << ", TX): " << tres.to_pp_string() << std::endl;
+ }
+
+ if (cfg->overrides.ul_freq_override) {
+ uhd::tune_request_t treq_rx = uhd::tune_request_t(cfg->overrides.ul_freq, 0);
+ auto tres = usrp_dev->set_rx_freq(treq_rx, 0);
+ rx_freqs[0] = usrp_dev->get_rx_freq(0);
+ LOGCHAN(0, DDEV, INFO) << "OVERRIDE set_freq(" << rx_freqs[0] << ", RX): " << tres.to_pp_string() << std::endl;
+ }
+
+ if (cfg->overrides.ul_gain_override) {
+ usrp_dev->set_rx_gain(cfg->overrides.ul_gain, 0);
+ rx_gains[0] = usrp_dev->get_rx_gain(0);
+ LOGCHAN(0, DDEV, INFO) << " OVERRIDE RX gain:" << rx_gains[0] << std::endl;
+ }
+
+ if (cfg->overrides.dl_gain_override) {
+ usrp_dev->set_tx_gain(cfg->overrides.dl_gain, 0);
+ tx_gains[0] = usrp_dev->get_tx_gain(0);
+ LOGCHAN(0, DDEV, INFO) << " OVERRIDE TX gain:" << tx_gains[0] << std::endl;
+ }
+
if (iface == MULTI_ARFCN)
return MULTI_ARFCN;
@@ -779,6 +770,8 @@ bool uhd_device::stop()
for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i]->reset();
+ band_reset();
+
started = false;
return true;
}
@@ -1017,17 +1010,22 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
{
std::vector<double> freqs;
uhd::tune_result_t tres;
+ std::string str_dir = tx ? "Tx" : "Rx";
+
+ if (cfg->overrides.dl_freq_override || cfg->overrides.ul_freq_override)
+ return true;
+
+ if (!update_band_from_freq(freq, chan, tx))
+ return false;
+
uhd::tune_request_t treq = select_freq(freq, chan, tx);
- std::string str_dir;
if (tx) {
tres = usrp_dev->set_tx_freq(treq, chan);
tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
- str_dir = "Tx";
} else {
tres = usrp_dev->set_rx_freq(treq, chan);
rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
- str_dir = "Rx";
}
LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
@@ -1057,33 +1055,12 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
bool uhd_device::setTxFreq(double wFreq, size_t chan)
{
- uint16_t req_arfcn;
- enum gsm_band req_band;
-
if (chan >= tx_freqs.size()) {
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
}
- ScopedLock lock(tune_lock);
-
- req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
- if (req_arfcn == 0xffff) {
- LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
- return false;
- }
- if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
- LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
- << " Hz (ARFCN " << req_arfcn << " )";
- return false;
- }
-
- if (!set_band(req_band))
- return false;
- if (!set_freq(wFreq, chan, true))
- return false;
-
- return true;
+ return set_freq(wFreq, chan, true);
}
bool uhd_device::setRxFreq(double wFreq, size_t chan)
@@ -1092,7 +1069,6 @@ bool uhd_device::setRxFreq(double wFreq, size_t chan)
LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
return false;
}
- ScopedLock lock(tune_lock);
return set_freq(wFreq, chan, false);
}
@@ -1342,11 +1318,8 @@ std::string uhd_device::str_code(uhd::async_metadata_t metadata)
}
#ifndef IPCMAGIC
-RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
- InterfaceType iface, size_t chans, double lo_offset,
- const std::vector<std::string>& tx_paths,
- const std::vector<std::string>& rx_paths)
+RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
{
- return new uhd_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
+ return new uhd_device(type, cfg);
}
#endif
diff --git a/Transceiver52M/device/uhd/UHDDevice.h b/Transceiver52M/device/uhd/UHDDevice.h
index 995b43c..f5e5232 100644
--- a/Transceiver52M/device/uhd/UHDDevice.h
+++ b/Transceiver52M/device/uhd/UHDDevice.h
@@ -30,6 +30,7 @@
#include "config.h"
#endif
+#include "bandmanager.h"
#include "radioDevice.h"
#include "smpl_buf.h"
@@ -71,6 +72,19 @@ struct dev_band_desc {
double rxgain2rssioffset_rel; /* dB */
};
+struct dev_desc {
+ unsigned channels;
+ double mcr;
+ double rate;
+ double offset;
+ std::string desc_str;
+};
+
+using dev_key = std::tuple<uhd_dev_type, int, int>;
+using dev_band_key = std::tuple<uhd_dev_type, enum gsm_band>;
+using power_map_t = std::map<dev_band_key, dev_band_desc>;
+using dev_map_t = std::map<dev_key, dev_desc>;
+
/*
uhd_device - UHD implementation of the Device interface. Timestamped samples
are sent to and received from the device. An intermediate buffer
@@ -78,19 +92,19 @@ struct dev_band_desc {
Events and errors such as underruns are reported asynchronously
by the device and received in a separate thread.
*/
-class uhd_device : public RadioDevice {
+class uhd_device : public RadioDevice, public band_manager<power_map_t, dev_map_t> {
public:
- uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
- size_t chan_num, double offset,
- const std::vector<std::string>& tx_paths,
- const std::vector<std::string>& rx_paths);
- ~uhd_device();
-
- int open(const std::string &args, int ref, bool swap_channels);
- bool start();
- bool stop();
- bool restart();
- enum TxWindowType getWindowType() { return tx_window; }
+ uhd_device(InterfaceType iface, const struct trx_cfg *cfg);
+ ~uhd_device();
+
+ int open();
+ bool start();
+ bool stop();
+ bool restart();
+ enum TxWindowType getWindowType()
+ {
+ return tx_window;
+ }
int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
TIMESTAMP timestamp, bool *underrun);
@@ -160,8 +174,6 @@ protected:
std::vector<double> tx_gains, rx_gains;
std::vector<double> tx_freqs, rx_freqs;
- enum gsm_band band;
- struct dev_band_desc band_desc;
size_t tx_spp, rx_spp;
bool started;
@@ -190,10 +202,6 @@ protected:
uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
bool set_freq(double freq, size_t chan, bool tx);
- void get_dev_band_desc(dev_band_desc& desc);
- bool set_band(enum gsm_band req_band);
- void assign_band_desc(enum gsm_band req_band);
Thread *async_event_thrd;
- Mutex tune_lock;
};
diff --git a/Transceiver52M/device/usrp1/USRPDevice.cpp b/Transceiver52M/device/usrp1/USRPDevice.cpp
index 5c40aa8..63a9bcc 100644
--- a/Transceiver52M/device/usrp1/USRPDevice.cpp
+++ b/Transceiver52M/device/usrp1/USRPDevice.cpp
@@ -60,11 +60,7 @@ const dboardConfigType dboardConfig = TXA_RXB;
const double USRPDevice::masterClockRate = 52.0e6;
-USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
- size_t chan_num, double lo_offset,
- const std::vector<std::string>& tx_paths,
- const std::vector<std::string>& rx_paths):
- RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths)
+USRPDevice::USRPDevice(InterfaceType iface, const struct trx_cfg *cfg) : RadioDevice(iface, cfg)
{
LOGC(DDEV, INFO) << "creating USRP device...";
@@ -94,7 +90,7 @@ USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
#endif
}
-int USRPDevice::open(const std::string &, int, bool)
+int USRPDevice::open()
{
writeLock.unlock();
@@ -587,8 +583,7 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
{
#ifndef SWLOOPBACK
short data[] = {0x00,0x02,0x00,0x00};
- uint32_t *wordPtr = (uint32_t *) data;
- *wordPtr = host_to_usrp_u32(*wordPtr);
+ /* FIXME: big endian */
bool tmpUnderrun;
std::vector<short *> buf(1, data);
@@ -659,22 +654,19 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
bool USRPDevice::setRxFreq(double wFreq) { return true;};
#endif
-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)
+RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
{
- if (tx_sps != rx_sps) {
- LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
- return NULL;
- }
- if (chans != 1) {
- LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
- return NULL;
- }
- if (lo_offset != 0.0) {
- LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
- return NULL;
- }
- return new USRPDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
+ if (cfg->tx_sps != cfg->rx_sps) {
+ LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
+ return NULL;
+ }
+ if (cfg->num_chans != 1) {
+ LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
+ return NULL;
+ }
+ if (cfg->offset != 0.0) {
+ LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
+ return NULL;
+ }
+ return new USRPDevice(type, cfg);
}
diff --git a/Transceiver52M/device/usrp1/USRPDevice.h b/Transceiver52M/device/usrp1/USRPDevice.h
index aa8dc69..4957ee6 100644
--- a/Transceiver52M/device/usrp1/USRPDevice.h
+++ b/Transceiver52M/device/usrp1/USRPDevice.h
@@ -104,20 +104,21 @@ private:
public:
/** Object constructor */
- USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
- const std::vector<std::string>& tx_paths,
- const std::vector<std::string>& rx_paths);
+ USRPDevice(InterfaceType iface, const struct trx_cfg *cfg);
- /** Instantiate the USRP */
- int open(const std::string &, int, bool);
+ /** Instantiate the USRP */
+ int open();
- /** Start the USRP */
- bool start();
+ /** Start the USRP */
+ bool start();
- /** Stop the USRP */
- bool stop();
+ /** Stop the USRP */
+ bool stop();
- enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
+ enum TxWindowType getWindowType()
+ {
+ return TX_WINDOW_USRP1;
+ }
/**
Read samples from the USRP.
diff --git a/Transceiver52M/grgsm_vitac/constants.h b/Transceiver52M/grgsm_vitac/constants.h
new file mode 100644
index 0000000..27bf6f4
--- /dev/null
+++ b/Transceiver52M/grgsm_vitac/constants.h
@@ -0,0 +1,149 @@
+#pragma once
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author (C) 2009-2017 by Piotr Krysik <ptrkrysik@gmail.com>
+ * @section LICENSE
+ *
+ * Gr-gsm is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * Gr-gsm 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gr-gsm; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <complex>
+
+#define gr_complex std::complex<float>
+
+
+#define GSM_SYMBOL_RATE (1625000.0/6.0) //symbols per second
+#define GSM_SYMBOL_PERIOD (1.0/GSM_SYMBOL_RATE) //seconds per symbol
+
+//Burst timing
+#define TAIL_BITS 3
+#define GUARD_BITS 8
+#define GUARD_FRACTIONAL 0.25 //fractional part of guard period
+#define GUARD_PERIOD GUARD_BITS + GUARD_FRACTIONAL
+#define DATA_BITS 57 //size of 1 data block in normal burst
+#define STEALING_BIT 1
+#define N_TRAIN_BITS 26
+#define N_SYNC_BITS 64
+#define N_ACCESS_BITS 41
+#define USEFUL_BITS 142 //(2*(DATA_BITS+STEALING_BIT) + N_TRAIN_BITS )
+#define FCCH_BITS USEFUL_BITS
+#define BURST_SIZE (USEFUL_BITS+2*TAIL_BITS)
+#define ACCESS_BURST_SIZE 88
+#define PROCESSED_CHUNK BURST_SIZE+2*GUARD_PERIOD
+
+#define SCH_DATA_LEN 39
+#define TS_BITS (TAIL_BITS+USEFUL_BITS+TAIL_BITS+GUARD_BITS) //a full TS (156 bits)
+#define TS_PER_FRAME 8
+#define FRAME_BITS (TS_PER_FRAME * TS_BITS + 2) // 156.25 * 8
+#define FCCH_POS TAIL_BITS
+#define SYNC_POS (TAIL_BITS + 39)
+#define TRAIN_POS ( TAIL_BITS + (DATA_BITS+STEALING_BIT) + 5) //first 5 bits of a training sequence
+ //aren't used for channel impulse response estimation
+#define TRAIN_BEGINNING 5
+#define SAFETY_MARGIN 6 //
+
+#define FCCH_HITS_NEEDED (USEFUL_BITS - 4)
+#define FCCH_MAX_MISSES 1
+#define FCCH_MAX_FREQ_OFFSET 100
+
+#define CHAN_IMP_RESP_LENGTH 5
+
+#define MAX_SCH_ERRORS 10 //maximum number of subsequent sch errors after which gsm receiver goes to find_next_fcch state
+
+typedef enum { empty, fcch_burst, sch_burst, normal_burst, rach_burst, dummy, dummy_or_normal, normal_or_noise } burst_type;
+typedef enum { unknown, multiframe_26, multiframe_51 } multiframe_type;
+
+static const unsigned char SYNC_BITS[] = {
+ 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0,
+ 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
+ 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1,
+ 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1
+};
+
+static const unsigned char ACCESS_BITS [] = {
+ 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0,
+ 0, 1, 1, 1, 1, 0, 0, 0
+};
+
+const unsigned FCCH_FRAMES[] = { 0, 10, 20, 30, 40 };
+const unsigned SCH_FRAMES[] = { 1, 11, 21, 31, 41 };
+
+const unsigned BCCH_FRAMES[] = { 2, 3, 4, 5 }; //!!the receiver shouldn't care about logical
+ //!!channels so this will be removed from this header
+const unsigned TEST_CCH_FRAMES[] = { 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 36, 37, 38, 39, 42, 43, 44, 45, 46, 47, 48, 49 };
+const unsigned TRAFFIC_CHANNEL_F[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 };
+const unsigned TEST51[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 };
+
+
+#define TSC0 0
+#define TSC1 1
+#define TSC2 2
+#define TSC3 3
+#define TSC4 4
+#define TSC5 5
+#define TSC6 6
+#define TSC7 7
+#define TS_DUMMY 8
+
+#define TRAIN_SEQ_NUM 9
+
+#define TIMESLOT0 0
+#define TIMESLOT1 1
+#define TIMESLOT2 2
+#define TIMESLOT3 3
+#define TIMESLOT4 4
+#define TIMESLOT5 5
+#define TIMESLOT6 6
+#define TIMESLOT7 7
+
+
+static const unsigned char train_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS] = {
+ {0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1},
+ {0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1},
+ {0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0},
+ {0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0},
+ {0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1},
+ {0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0},
+ {1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1},
+ {1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0},
+ {0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1} // DUMMY
+};
+
+
+//Dummy burst 0xFB 76 0A 4E 09 10 1F 1C 5C 5C 57 4A 33 39 E9 F1 2F A8
+static const unsigned char dummy_burst[] = {
+ 0, 0, 0,
+ 1, 1, 1, 1, 1, 0, 1, 1, 0, 1,
+ 1, 1, 0, 1, 1, 0, 0, 0, 0, 0,
+ 1, 0, 1, 0, 0, 1, 0, 0, 1, 1,
+ 1, 0, 0, 0, 0, 0, 1, 0, 0, 1,
+ 0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
+ 0, 1, 1, 1, 1, 1, 0, 0,
+
+ 0, 1, 1, 1, 0, 0, 0, 1, 0, 1,
+ 1, 1, 0, 0, 0, 1, 0, 1, 1, 1,
+ 0, 0, 0, 1, 0, 1,
+
+ 0, 1, 1, 1, 0, 1, 0, 0, 1, 0,
+ 1, 0, 0, 0, 1, 1, 0, 0, 1, 1,
+ 0, 0, 1, 1, 1, 0, 0, 1, 1, 1,
+ 1, 0, 1, 0, 0, 1, 1, 1, 1, 1,
+ 0, 0, 0, 1, 0, 0, 1, 0, 1, 1,
+ 1, 1, 1, 0, 1, 0, 1, 0,
+ 0, 0, 0
+};
diff --git a/Transceiver52M/grgsm_vitac/grgsm_vitac.cpp b/Transceiver52M/grgsm_vitac/grgsm_vitac.cpp
new file mode 100644
index 0000000..2016541
--- /dev/null
+++ b/Transceiver52M/grgsm_vitac/grgsm_vitac.cpp
@@ -0,0 +1,305 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author (C) 2009-2017 by Piotr Krysik <ptrkrysik@gmail.com>
+ * @author Contributions by sysmocom - s.f.m.c. GmbH / Eric Wild <ewild@sysmocom.de>
+ * @section LICENSE
+ *
+ * Gr-gsm is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * Gr-gsm 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gr-gsm; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include "constants.h"
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+#include <complex>
+
+
+#include <algorithm>
+#include <string.h>
+#include <iostream>
+#include <numeric>
+#include <vector>
+#include <fstream>
+
+#include "viterbi_detector.h"
+#include "grgsm_vitac.h"
+
+gr_complex d_acc_training_seq[N_ACCESS_BITS]; ///<encoded training sequence of a RACH burst
+gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a SCH burst
+gr_complex d_norm_training_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS]; ///<encoded training sequences of a normal and dummy burst
+const int d_chan_imp_length = CHAN_IMP_RESP_LENGTH;
+
+void initvita()
+{
+ /**
+ * Prepare SCH sequence bits
+ *
+ * (TS_BITS + 2 * GUARD_PERIOD)
+ * Burst and two guard periods
+ * (one guard period is an arbitrary overlap)
+ */
+ gmsk_mapper(SYNC_BITS, N_SYNC_BITS, d_sch_training_seq, gr_complex(0.0, -1.0));
+ for (auto &i : d_sch_training_seq)
+ i = conj(i);
+
+ /* ab */
+ gmsk_mapper(ACCESS_BITS, N_ACCESS_BITS, d_acc_training_seq, gr_complex(0.0, -1.0));
+ for (auto &i : d_acc_training_seq)
+ i = conj(i);
+
+ /* Prepare bits of training sequences */
+ for (int i = 0; i < TRAIN_SEQ_NUM; i++) {
+ /**
+ * If first bit of the sequence is 0
+ * => first symbol is 1, else -1
+ */
+ gr_complex startpoint = train_seq[i][0] == 0 ? gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
+ gmsk_mapper(train_seq[i], N_TRAIN_BITS, d_norm_training_seq[i], startpoint);
+ for (auto &i : d_norm_training_seq[i])
+ i = conj(i);
+ }
+}
+
+template <unsigned int burst_size>
+NO_UBSAN static void detect_burst_generic(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start,
+ char *output_binary, int ss)
+{
+ std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
+ unsigned int stop_states[2] = { 4, 12 };
+ gr_complex filtered_burst[burst_size];
+ gr_complex rhh[CHAN_IMP_RESP_LENGTH];
+ float output[burst_size];
+ int start_state = ss;
+
+ autocorrelation(chan_imp_resp, &rhh_temp[0], d_chan_imp_length * d_OSR);
+ for (int ii = 0; ii < d_chan_imp_length; ii++)
+ rhh[ii] = conj(rhh_temp[ii * d_OSR]);
+
+ mafi(&input[burst_start], burst_size, chan_imp_resp, d_chan_imp_length * d_OSR, filtered_burst);
+
+ viterbi_detector(filtered_burst, burst_size, rhh, start_state, stop_states, 2, output);
+
+ for (unsigned int i = 0; i < burst_size; i++)
+ output_binary[i] = output[i] > 0 ? -127 : 127; // pre flip bits!
+}
+
+NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary,
+ int ss)
+{
+ return detect_burst_generic<BURST_SIZE>(input, chan_imp_resp, burst_start, output_binary, ss);
+}
+NO_UBSAN void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary,
+ int ss)
+{
+ return detect_burst_generic<8 + 41 + 36 + 3>(input, chan_imp_resp, burst_start, output_binary, ss);
+}
+
+NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
+{
+ return detect_burst_nb(input, chan_imp_resp, burst_start, output_binary, 3);
+}
+NO_UBSAN void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary)
+{
+ return detect_burst_ab(input, chan_imp_resp, burst_start, output_binary, 3);
+}
+
+void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output, gr_complex start_point)
+{
+ gr_complex j = gr_complex(0.0, 1.0);
+ gmsk_output[0] = start_point;
+
+ int previous_symbol = 2 * input[0] - 1;
+ int current_symbol;
+ int encoded_symbol;
+
+ for (int i = 1; i < nitems; i++) {
+ /* Change bits representation to NRZ */
+ current_symbol = 2 * input[i] - 1;
+
+ /* Differentially encode */
+ encoded_symbol = current_symbol * previous_symbol;
+
+ /* And do GMSK mapping */
+ gmsk_output[i] = j * gr_complex(encoded_symbol, 0.0) * gmsk_output[i - 1];
+
+ previous_symbol = current_symbol;
+ }
+}
+
+gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_complex *input)
+{
+ gr_complex result(0.0, 0.0);
+
+ for (int ii = 0; ii < length; ii++)
+ result += sequence[ii] * input[ii * d_OSR];
+
+ return conj(result) / gr_complex(length, 0);
+}
+
+/* Computes autocorrelation for positive arguments */
+inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems)
+{
+ for (int k = nitems - 1; k >= 0; k--) {
+ out[k] = gr_complex(0, 0);
+ for (int i = k; i < nitems; i++)
+ out[k] += input[i] * conj(input[i - k]);
+ }
+}
+
+inline void mafi(const gr_complex *input, int nitems, gr_complex *filter, int filter_length, gr_complex *output)
+{
+ for (int n = 0; n < nitems; n++) {
+ int a = n * d_OSR;
+ output[n] = 0;
+
+ for (int ii = 0; ii < filter_length; ii++) {
+ if ((a + ii) >= nitems * d_OSR)
+ break;
+
+ output[n] += input[a + ii] * filter[ii];
+ }
+ }
+}
+
+int get_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, int search_start_pos, int search_stop_pos,
+ gr_complex *tseq, int tseqlen, float *corr_max)
+{
+ const int num_search_windows = search_stop_pos - search_start_pos;
+ const int power_search_window_len = d_chan_imp_length * d_OSR;
+ std::vector<float> window_energy_buffer;
+ std::vector<float> power_buffer;
+ std::vector<gr_complex> correlation_buffer;
+
+ power_buffer.reserve(num_search_windows);
+ correlation_buffer.reserve(num_search_windows);
+ window_energy_buffer.reserve(num_search_windows);
+
+ for (int ii = 0; ii < num_search_windows; ii++) {
+ gr_complex correlation = correlate_sequence(tseq, tseqlen, &input[search_start_pos + ii]);
+ correlation_buffer.push_back(correlation);
+ power_buffer.push_back(std::pow(abs(correlation), 2));
+ }
+
+ /* Compute window energies */
+ float windowSum = 0;
+
+ // first window
+ for (int i = 0; i < power_search_window_len; i++) {
+ windowSum += power_buffer[i];
+ }
+ window_energy_buffer.push_back(windowSum);
+
+ // slide windows
+ for (int i = power_search_window_len; i < num_search_windows; i++) {
+ windowSum += power_buffer[i] - power_buffer[i - power_search_window_len];
+ window_energy_buffer.push_back(windowSum);
+ }
+
+ int strongest_window_nr = std::max_element(window_energy_buffer.begin(), window_energy_buffer.end()) -
+ window_energy_buffer.begin();
+
+ float max_correlation = 0;
+ for (int ii = 0; ii < power_search_window_len; ii++) {
+ gr_complex correlation = correlation_buffer[strongest_window_nr + ii];
+ if (abs(correlation) > max_correlation)
+ max_correlation = abs(correlation);
+ chan_imp_resp[ii] = correlation;
+ }
+
+ *corr_max = max_correlation;
+
+ /**
+ * Compute first sample position, which corresponds
+ * to the first sample of the impulse response
+ */
+ return search_start_pos + strongest_window_nr;
+}
+
+/*
+8 ext tail bits
+41 sync seq
+36 encrypted bits
+3 tail bits
+68.25 extended tail bits (!)
+
+center at 8+5 (actually known tb -> known isi, start at 8?) FIXME
+*/
+int get_access_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int max_delay)
+{
+ const int search_center = 8 + 5;
+ const int search_start_pos = (search_center - 5) * d_OSR + 1;
+ const int search_stop_pos = (search_center + 5 + d_chan_imp_length + max_delay) * d_OSR;
+ const auto tseq = &d_acc_training_seq[TRAIN_BEGINNING];
+ const auto tseqlen = N_ACCESS_BITS - (2 * TRAIN_BEGINNING);
+ return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
+ search_center * d_OSR;
+}
+
+/*
+
+3 + 57 + 1 + 26 + 1 + 57 + 3 + 8.25
+
+search center = 3 + 57 + 1 + 5 (due to tsc 5+16+5 split)
+this is +-5 samples around (+5 beginning) of truncated t16 tsc
+
+*/
+int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int bcc)
+{
+ const int search_center = TRAIN_POS;
+ const int search_start_pos = (search_center - 5) * d_OSR + 1;
+ const int search_stop_pos = (search_center + 5 + d_chan_imp_length) * d_OSR;
+ const auto tseq = &d_norm_training_seq[bcc][TRAIN_BEGINNING];
+ const auto tseqlen = N_TRAIN_BITS - (2 * TRAIN_BEGINNING);
+ return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
+ search_center * d_OSR;
+}
+
+/*
+
+3 tail | 39 data | 64 tsc | 39 data | 3 tail | 8.25 guard
+start 3+39 - 10
+end 3+39 + SYNC_SEARCH_RANGE
+
+*/
+int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp)
+{
+ const int search_center = SYNC_POS + TRAIN_BEGINNING;
+ const int search_start_pos = (search_center - 10) * d_OSR;
+ const int search_stop_pos = (search_center + SYNC_SEARCH_RANGE) * d_OSR;
+ const auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
+ const auto tseqlen = N_SYNC_BITS - (2 * TRAIN_BEGINNING);
+
+ // strongest_window_nr + chan_imp_resp_center + SYNC_POS *d_OSR - 48 * d_OSR - 2 * d_OSR + 2 ;
+ float corr_max;
+ return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, &corr_max) -
+ search_center * d_OSR;
+ ;
+}
+
+int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max)
+{
+ const auto tseqlen = N_SYNC_BITS - (2 * TRAIN_BEGINNING);
+ const int search_center = SYNC_POS + TRAIN_BEGINNING;
+ const int search_start_pos = 0;
+ // FIXME: proper end offset
+ const int search_stop_pos = len - (N_SYNC_BITS * 8);
+ auto tseq = &d_sch_training_seq[TRAIN_BEGINNING];
+
+ return get_chan_imp_resp(input, chan_imp_resp, search_start_pos, search_stop_pos, tseq, tseqlen, corr_max) -
+ search_center * d_OSR;
+} \ No newline at end of file
diff --git a/Transceiver52M/grgsm_vitac/grgsm_vitac.h b/Transceiver52M/grgsm_vitac/grgsm_vitac.h
new file mode 100644
index 0000000..c1e470c
--- /dev/null
+++ b/Transceiver52M/grgsm_vitac/grgsm_vitac.h
@@ -0,0 +1,89 @@
+#pragma once
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author (C) 2009-2017 by Piotr Krysik <ptrkrysik@gmail.com>
+ * @section LICENSE
+ *
+ * Gr-gsm is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * Gr-gsm 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gr-gsm; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+#include <vector>
+#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 __has_attribute(target_clones) && defined(__x86_64) && true
+#define MULTI_VER_TARGET_ATTR __attribute__((target_clones("avx", "sse4.2", "sse3", "sse2", "sse", "default")))
+#else
+#define MULTI_VER_TARGET_ATTR
+#endif
+#endif
+
+/* ... but apparently clang disagrees... */
+#if defined(__clang__)
+#define MULTI_VER_TARGET_ATTR_CLANGONLY MULTI_VER_TARGET_ATTR
+#else
+#define MULTI_VER_TARGET_ATTR_CLANGONLY
+#endif
+
+/* ancient gcc < 8 has no attribute, clang always pretends to be gcc 4 */
+#if !defined(__clang__) && __GNUC__ < 8
+#define NO_UBSAN __attribute__((no_sanitize_undefined))
+#else
+#if defined(__has_attribute)
+#if __has_attribute(no_sanitize)
+#define NO_UBSAN __attribute__((no_sanitize("undefined")))
+#endif
+#else
+#define NO_UBSAN
+#endif
+#endif
+
+#define SYNC_SEARCH_RANGE 30
+const int d_OSR(4);
+
+void initvita();
+
+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);
+
+void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary, int ss);
+void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary, int ss);
+void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary);
+void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary);
+
+void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output, gr_complex start_point);
+gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_complex *input);
+inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems);
+inline void mafi(const gr_complex *input, int nitems, gr_complex *filter, int filter_length, gr_complex *output);
+int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp);
+int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int bcc);
+int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max);
+int get_access_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int max_delay);
+
+enum class btype { NB, SCH };
+struct fdata {
+ btype t;
+ unsigned int fn;
+ int tn;
+ int bcc;
+ std::string fpath;
+ std::vector<gr_complex> data;
+ unsigned int data_start_offset;
+}; \ No newline at end of file
diff --git a/Transceiver52M/grgsm_vitac/viterbi_detector.cc b/Transceiver52M/grgsm_vitac/viterbi_detector.cc
new file mode 100644
index 0000000..4ac4505
--- /dev/null
+++ b/Transceiver52M/grgsm_vitac/viterbi_detector.cc
@@ -0,0 +1,392 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author (C) 2009 by Piotr Krysik <ptrkrysik@gmail.com>
+ * @section LICENSE
+ *
+ * Gr-gsm is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * Gr-gsm 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gr-gsm; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * viterbi_detector:
+ * This part does the detection of received sequnece.
+ * Employed algorithm is viterbi Maximum Likehood Sequence Estimation.
+ * At this moment it gives hard decisions on the output, but
+ * it was designed with soft decisions in mind.
+ *
+ * SYNTAX: void viterbi_detector(
+ * const gr_complex * input,
+ * unsigned int samples_num,
+ * gr_complex * rhh,
+ * unsigned int start_state,
+ * const unsigned int * stop_states,
+ * unsigned int stops_num,
+ * float * output)
+ *
+ * INPUT: input: Complex received signal afted matched filtering.
+ * samples_num: Number of samples in the input table.
+ * rhh: The autocorrelation of the estimated channel
+ * impulse response.
+ * start_state: Number of the start point. In GSM each burst
+ * starts with sequence of three bits (0,0,0) which
+ * indicates start point of the algorithm.
+ * stop_states: Table with numbers of possible stop states.
+ * stops_num: Number of possible stop states
+ *
+ *
+ * OUTPUT: output: Differentially decoded hard output of the algorithm:
+ * -1 for logical "0" and 1 for logical "1"
+ *
+ * SUB_FUNC: none
+ *
+ * TEST(S): Tested with real world normal burst.
+ */
+
+#include "constants.h"
+#include <cmath>
+
+#define PATHS_NUM (1 << (CHAN_IMP_RESP_LENGTH-1))
+
+void viterbi_detector(const gr_complex * input, unsigned int samples_num, gr_complex * rhh, unsigned int start_state, const unsigned int * stop_states, unsigned int stops_num, float * output)
+{
+ float increment[8];
+ float path_metrics1[16];
+ float path_metrics2[16];
+ float paths_difference;
+ float * new_path_metrics;
+ float * old_path_metrics;
+ float * tmp;
+ float trans_table[BURST_SIZE][16];
+ float pm_candidate1, pm_candidate2;
+ bool real_imag;
+ float input_symbol_real, input_symbol_imag;
+ unsigned int i, sample_nr;
+
+/*
+* Setup first path metrics, so only state pointed by start_state is possible.
+* Start_state metric is equal to zero, the rest is written with some very low value,
+* which makes them practically impossible to occur.
+*/
+ for(i=0; i<PATHS_NUM; i++){
+ path_metrics1[i]=(-10e30);
+ }
+ path_metrics1[start_state]=0;
+
+/*
+* Compute Increment - a table of values which does not change for subsequent input samples.
+* Increment is table of reference levels for computation of branch metrics:
+* branch metric = (+/-)received_sample (+/-) reference_level
+*/
+ increment[0] = -rhh[1].imag() -rhh[2].real() -rhh[3].imag() +rhh[4].real();
+ increment[1] = rhh[1].imag() -rhh[2].real() -rhh[3].imag() +rhh[4].real();
+ increment[2] = -rhh[1].imag() +rhh[2].real() -rhh[3].imag() +rhh[4].real();
+ increment[3] = rhh[1].imag() +rhh[2].real() -rhh[3].imag() +rhh[4].real();
+ increment[4] = -rhh[1].imag() -rhh[2].real() +rhh[3].imag() +rhh[4].real();
+ increment[5] = rhh[1].imag() -rhh[2].real() +rhh[3].imag() +rhh[4].real();
+ increment[6] = -rhh[1].imag() +rhh[2].real() +rhh[3].imag() +rhh[4].real();
+ increment[7] = rhh[1].imag() +rhh[2].real() +rhh[3].imag() +rhh[4].real();
+
+
+/*
+* Computation of path metrics and decisions (Add-Compare-Select).
+* It's composed of two parts: one for odd input samples (imaginary numbers)
+* and one for even samples (real numbers).
+* Each part is composed of independent (parallelisable) statements like
+* this one:
+* pm_candidate1 = old_path_metrics[0] -input_symbol_imag +increment[2];
+* pm_candidate2 = old_path_metrics[8] -input_symbol_imag -increment[5];
+* paths_difference=pm_candidate2-pm_candidate1;
+* new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+* trans_table[sample_nr][1] = paths_difference;
+* This is very good point for optimisations (SIMD or OpenMP) as it's most time
+* consuming part of this function.
+*/
+ sample_nr=0;
+ old_path_metrics=path_metrics1;
+ new_path_metrics=path_metrics2;
+ while(sample_nr<samples_num){
+ //Processing imag states
+ real_imag=1;
+ input_symbol_imag = input[sample_nr].imag();
+
+ pm_candidate1 = old_path_metrics[0] +input_symbol_imag -increment[2];
+ pm_candidate2 = old_path_metrics[8] +input_symbol_imag +increment[5];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[0]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][0] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[0] -input_symbol_imag +increment[2];
+ pm_candidate2 = old_path_metrics[8] -input_symbol_imag -increment[5];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][1] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[1] +input_symbol_imag -increment[3];
+ pm_candidate2 = old_path_metrics[9] +input_symbol_imag +increment[4];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[2]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][2] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[1] -input_symbol_imag +increment[3];
+ pm_candidate2 = old_path_metrics[9] -input_symbol_imag -increment[4];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[3]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][3] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[2] +input_symbol_imag -increment[0];
+ pm_candidate2 = old_path_metrics[10] +input_symbol_imag +increment[7];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[4]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][4] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[2] -input_symbol_imag +increment[0];
+ pm_candidate2 = old_path_metrics[10] -input_symbol_imag -increment[7];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[5]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][5] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[3] +input_symbol_imag -increment[1];
+ pm_candidate2 = old_path_metrics[11] +input_symbol_imag +increment[6];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[6]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][6] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[3] -input_symbol_imag +increment[1];
+ pm_candidate2 = old_path_metrics[11] -input_symbol_imag -increment[6];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[7]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][7] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[4] +input_symbol_imag -increment[6];
+ pm_candidate2 = old_path_metrics[12] +input_symbol_imag +increment[1];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[8]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][8] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[4] -input_symbol_imag +increment[6];
+ pm_candidate2 = old_path_metrics[12] -input_symbol_imag -increment[1];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[9]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][9] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[5] +input_symbol_imag -increment[7];
+ pm_candidate2 = old_path_metrics[13] +input_symbol_imag +increment[0];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[10]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][10] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[5] -input_symbol_imag +increment[7];
+ pm_candidate2 = old_path_metrics[13] -input_symbol_imag -increment[0];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[11]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][11] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[6] +input_symbol_imag -increment[4];
+ pm_candidate2 = old_path_metrics[14] +input_symbol_imag +increment[3];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[12]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][12] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[6] -input_symbol_imag +increment[4];
+ pm_candidate2 = old_path_metrics[14] -input_symbol_imag -increment[3];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[13]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][13] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[7] +input_symbol_imag -increment[5];
+ pm_candidate2 = old_path_metrics[15] +input_symbol_imag +increment[2];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[14]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][14] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[7] -input_symbol_imag +increment[5];
+ pm_candidate2 = old_path_metrics[15] -input_symbol_imag -increment[2];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[15]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][15] = paths_difference;
+ tmp=old_path_metrics;
+ old_path_metrics=new_path_metrics;
+ new_path_metrics=tmp;
+
+ sample_nr++;
+ if(sample_nr==samples_num)
+ break;
+
+ //Processing real states
+ real_imag=0;
+ input_symbol_real = input[sample_nr].real();
+
+ pm_candidate1 = old_path_metrics[0] -input_symbol_real -increment[7];
+ pm_candidate2 = old_path_metrics[8] -input_symbol_real +increment[0];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[0]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][0] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[0] +input_symbol_real +increment[7];
+ pm_candidate2 = old_path_metrics[8] +input_symbol_real -increment[0];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][1] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[1] -input_symbol_real -increment[6];
+ pm_candidate2 = old_path_metrics[9] -input_symbol_real +increment[1];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[2]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][2] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[1] +input_symbol_real +increment[6];
+ pm_candidate2 = old_path_metrics[9] +input_symbol_real -increment[1];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[3]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][3] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[2] -input_symbol_real -increment[5];
+ pm_candidate2 = old_path_metrics[10] -input_symbol_real +increment[2];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[4]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][4] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[2] +input_symbol_real +increment[5];
+ pm_candidate2 = old_path_metrics[10] +input_symbol_real -increment[2];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[5]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][5] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[3] -input_symbol_real -increment[4];
+ pm_candidate2 = old_path_metrics[11] -input_symbol_real +increment[3];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[6]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][6] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[3] +input_symbol_real +increment[4];
+ pm_candidate2 = old_path_metrics[11] +input_symbol_real -increment[3];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[7]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][7] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[4] -input_symbol_real -increment[3];
+ pm_candidate2 = old_path_metrics[12] -input_symbol_real +increment[4];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[8]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][8] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[4] +input_symbol_real +increment[3];
+ pm_candidate2 = old_path_metrics[12] +input_symbol_real -increment[4];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[9]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][9] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[5] -input_symbol_real -increment[2];
+ pm_candidate2 = old_path_metrics[13] -input_symbol_real +increment[5];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[10]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][10] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[5] +input_symbol_real +increment[2];
+ pm_candidate2 = old_path_metrics[13] +input_symbol_real -increment[5];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[11]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][11] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[6] -input_symbol_real -increment[1];
+ pm_candidate2 = old_path_metrics[14] -input_symbol_real +increment[6];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[12]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][12] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[6] +input_symbol_real +increment[1];
+ pm_candidate2 = old_path_metrics[14] +input_symbol_real -increment[6];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[13]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][13] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[7] -input_symbol_real -increment[0];
+ pm_candidate2 = old_path_metrics[15] -input_symbol_real +increment[7];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[14]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][14] = paths_difference;
+
+ pm_candidate1 = old_path_metrics[7] +input_symbol_real +increment[0];
+ pm_candidate2 = old_path_metrics[15] +input_symbol_real -increment[7];
+ paths_difference=pm_candidate2-pm_candidate1;
+ new_path_metrics[15]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
+ trans_table[sample_nr][15] = paths_difference;
+
+ tmp=old_path_metrics;
+ old_path_metrics=new_path_metrics;
+ new_path_metrics=tmp;
+
+ sample_nr++;
+ }
+
+/*
+* Find the best from the stop states by comparing their path metrics.
+* Not every stop state is always possible, so we are searching in
+* a subset of them.
+*/
+ unsigned int best_stop_state;
+ float stop_state_metric, max_stop_state_metric;
+ best_stop_state = stop_states[0];
+ max_stop_state_metric = old_path_metrics[best_stop_state];
+ for(i=1; i< stops_num; i++){
+ stop_state_metric = old_path_metrics[stop_states[i]];
+ if(stop_state_metric > max_stop_state_metric){
+ max_stop_state_metric = stop_state_metric;
+ best_stop_state = stop_states[i];
+ }
+ }
+
+/*
+* This table was generated with hope that it gives a litle speedup during
+* traceback stage.
+* Received bit is related to the number of state in the trellis.
+* I've numbered states so their parity (number of ones) is related
+* to a received bit.
+*/
+ static const unsigned int parity_table[PATHS_NUM] = { 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, };
+
+/*
+* Table of previous states in the trellis diagram.
+* For GMSK modulation every state has two previous states.
+* Example:
+* previous_state_nr1 = prev_table[current_state_nr][0]
+* previous_state_nr2 = prev_table[current_state_nr][1]
+*/
+ static const unsigned int prev_table[PATHS_NUM][2] = { {0,8}, {0,8}, {1,9}, {1,9}, {2,10}, {2,10}, {3,11}, {3,11}, {4,12}, {4,12}, {5,13}, {5,13}, {6,14}, {6,14}, {7,15}, {7,15}, };
+
+/*
+* Traceback and differential decoding of received sequence.
+* Decisions stored in trans_table are used to restore best path in the trellis.
+*/
+ sample_nr=samples_num;
+ unsigned int state_nr=best_stop_state;
+ unsigned int decision;
+ bool out_bit=0;
+
+ while(sample_nr>0){
+ sample_nr--;
+ decision = (trans_table[sample_nr][state_nr]>0);
+
+ if(decision != out_bit)
+ output[sample_nr]=-trans_table[sample_nr][state_nr];
+ else
+ output[sample_nr]=trans_table[sample_nr][state_nr];
+
+ out_bit = out_bit ^ real_imag ^ parity_table[state_nr];
+ state_nr = prev_table[state_nr][decision];
+ real_imag = !real_imag;
+ }
+}
diff --git a/Transceiver52M/grgsm_vitac/viterbi_detector.h b/Transceiver52M/grgsm_vitac/viterbi_detector.h
new file mode 100644
index 0000000..92bc2a8
--- /dev/null
+++ b/Transceiver52M/grgsm_vitac/viterbi_detector.h
@@ -0,0 +1,64 @@
+/* -*- c++ -*- */
+/*
+ * @file
+ * @author (C) 2009 Piotr Krysik <ptrkrysik@gmail.com>
+ * @section LICENSE
+ *
+ * Gr-gsm is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3, or (at your option)
+ * any later version.
+ *
+ * Gr-gsm 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with gr-gsm; see the file COPYING. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street,
+ * Boston, MA 02110-1301, USA.
+ */
+
+/*
+ * viterbi_detector:
+ * This part does the detection of received sequnece.
+ * Employed algorithm is viterbi Maximum Likehood Sequence Estimation.
+ * At this moment it gives hard decisions on the output, but
+ * it was designed with soft decisions in mind.
+ *
+ * SYNTAX: void viterbi_detector(
+ * const gr_complex * input,
+ * unsigned int samples_num,
+ * gr_complex * rhh,
+ * unsigned int start_state,
+ * const unsigned int * stop_states,
+ * unsigned int stops_num,
+ * float * output)
+ *
+ * INPUT: input: Complex received signal afted matched filtering.
+ * samples_num: Number of samples in the input table.
+ * rhh: The autocorrelation of the estimated channel
+ * impulse response.
+ * start_state: Number of the start point. In GSM each burst
+ * starts with sequence of three bits (0,0,0) which
+ * indicates start point of the algorithm.
+ * stop_states: Table with numbers of possible stop states.
+ * stops_num: Number of possible stop states
+ *
+ *
+ * OUTPUT: output: Differentially decoded hard output of the algorithm:
+ * -1 for logical "0" and 1 for logical "1"
+ *
+ * SUB_FUNC: none
+ *
+ * TEST(S): Tested with real world normal burst.
+ */
+
+#ifndef INCLUDED_VITERBI_DETECTOR_H
+#define INCLUDED_VITERBI_DETECTOR_H
+#include "constants.h"
+
+void viterbi_detector(const gr_complex * input, unsigned int samples_num, gr_complex * rhh, unsigned int start_state, const unsigned int * stop_states, unsigned int stops_num, float * output);
+
+#endif /* INCLUDED_VITERBI_DETECTOR_H */
diff --git a/Transceiver52M/ms/bladerf_specific.h b/Transceiver52M/ms/bladerf_specific.h
new file mode 100644
index 0000000..9db8bf0
--- /dev/null
+++ b/Transceiver52M/ms/bladerf_specific.h
@@ -0,0 +1,479 @@
+#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 "itrq.h"
+#include <atomic>
+#include <complex>
+#include <cstdint>
+#include <functional>
+#include <iostream>
+#include <cassert>
+#include <cstring>
+
+#include <libbladeRF.h>
+#include <Timeval.h>
+#include <unistd.h>
+
+const size_t BLADE_BUFFER_SIZE = 1024 * 1;
+const size_t BLADE_NUM_BUFFERS = 32 * 1;
+const size_t NUM_TRANSFERS = 16 * 2;
+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)
+{
+ out << '(' << std::forward<Arg>(arg);
+ (void)(int[]){ 0, (void((out << "," << std::forward<Args>(args))), 0)... };
+ out << ')' << std::endl;
+}
+
+template <class R, class... Args>
+using RvalFunc = R (*)(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,
+ Args... args)
+{
+ R rval = func(std::forward<Args>(args)...);
+ if (rval != 0) {
+ std::cerr << ((rval >= 0) ? "OK:" : bladerf_strerror(rval)) << ':' << finame << ':' << line << ':'
+ << funcname << ':' << fname;
+ expand_args(std::cerr, args...);
+ }
+ return rval;
+}
+
+// only macros can pass a func name string
+#define blade_check(func, ...) exec_and_check(func, #func, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__)
+
+#pragma pack(push, 1)
+using blade_sample_type = std::complex<int16_t>;
+enum class blade_speed_buffer_type { HS, SS };
+template <blade_speed_buffer_type T>
+struct blade_usb_message {
+ uint32_t reserved;
+ uint64_t ts;
+ uint32_t meta_flags;
+ blade_sample_type d[(T == blade_speed_buffer_type::SS ? 512 : 256) - 4];
+};
+
+static_assert(sizeof(blade_usb_message<blade_speed_buffer_type::SS>) == 2048, "blade buffer mismatch!");
+static_assert(sizeof(blade_usb_message<blade_speed_buffer_type::HS>) == 1024, "blade buffer mismatch!");
+template <unsigned int SZ, blade_speed_buffer_type T>
+struct blade_otw_buffer {
+ static_assert((SZ >= 2 && !(SZ % 2)), "min size is 2x usb buffer!");
+ blade_usb_message<T> m[SZ];
+ int actual_samples_per_msg()
+ {
+ return sizeof(blade_usb_message<T>::d) / sizeof(typeof(blade_usb_message<T>::d[0]));
+ }
+ int actual_samples_per_buffer()
+ {
+ return SZ * actual_samples_per_msg();
+ }
+ int samples_per_buffer()
+ {
+ return SZ * sizeof(blade_usb_message<T>) / sizeof(typeof(blade_usb_message<T>::d[0]));
+ }
+ int num_msgs_per_buffer()
+ {
+ return SZ;
+ }
+ auto get_first_ts()
+ {
+ return m[0].ts;
+ }
+ constexpr auto *getsampleoffset(int ofs)
+ {
+ auto full = ofs / actual_samples_per_msg();
+ auto rem = ofs % actual_samples_per_msg();
+ return &m[full].d[rem];
+ }
+ int readall(blade_sample_type *outaddr)
+ {
+ blade_sample_type *addr = outaddr;
+ for (unsigned int i = 0; i < SZ; i++) {
+ memcpy(addr, &m[i].d[0], actual_samples_per_msg() * sizeof(blade_sample_type));
+ addr += actual_samples_per_msg();
+ }
+ return actual_samples_per_buffer();
+ }
+ int read_n(blade_sample_type *outaddr, int start, int num)
+ {
+ assert((start + num) <= actual_samples_per_buffer());
+ assert(start >= 0);
+
+ if (!num)
+ return 0;
+
+ // which buffer?
+ int start_buf_idx = (start > 0) ? start / actual_samples_per_msg() : 0;
+ // offset from actual buffer start
+ auto start_offset_in_buf = (start - (start_buf_idx * actual_samples_per_msg()));
+ auto samp_rem_in_first_buf = actual_samples_per_msg() - start_offset_in_buf;
+ auto remaining_first_buf = num > samp_rem_in_first_buf ? samp_rem_in_first_buf : num;
+
+ memcpy(outaddr, &m[start_buf_idx].d[start_offset_in_buf],
+ remaining_first_buf * sizeof(blade_sample_type));
+ outaddr += remaining_first_buf;
+
+ auto remaining = num - remaining_first_buf;
+
+ if (!remaining)
+ return num;
+
+ start_buf_idx++;
+
+ auto rem_full_bufs = remaining / actual_samples_per_msg();
+ remaining -= rem_full_bufs * actual_samples_per_msg();
+
+ for (int i = 0; i < rem_full_bufs; i++) {
+ memcpy(outaddr, &m[start_buf_idx++].d[0], actual_samples_per_msg() * sizeof(blade_sample_type));
+ outaddr += actual_samples_per_msg();
+ }
+
+ if (remaining)
+ memcpy(outaddr, &m[start_buf_idx].d[0], remaining * sizeof(blade_sample_type));
+ return num;
+ }
+ int write_n_burst(blade_sample_type *in, int num, uint64_t first_ts)
+ {
+ assert(num <= actual_samples_per_buffer());
+ int len_rem = num;
+ for (unsigned int i = 0; i < SZ; i++) {
+ m[i] = {};
+ m[i].ts = first_ts + i * actual_samples_per_msg();
+ if (len_rem) {
+ int max_to_copy =
+ len_rem > actual_samples_per_msg() ? actual_samples_per_msg() : len_rem;
+ memcpy(&m[i].d[0], in, max_to_copy * sizeof(blade_sample_type));
+ len_rem -= max_to_copy;
+ in += actual_samples_per_msg();
+ }
+ }
+ return num;
+ }
+};
+#pragma pack(pop)
+
+template <unsigned int SZ, blade_speed_buffer_type T>
+struct blade_otw_buffer_helper {
+ static_assert((SZ >= 1024 && ((SZ & (SZ - 1)) == 0)), "only buffer size multiples of 1024 allowed!");
+ static blade_otw_buffer<SZ / 512, T> x;
+};
+
+using dev_buf_t = typeof(blade_otw_buffer_helper<BLADE_BUFFER_SIZE, blade_speed_buffer_type::SS>::x);
+// using buf_in_use = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
+using bh_fn_t = std::function<int(dev_buf_t *)>;
+
+template <typename T>
+struct blade_hw {
+ struct bladerf *dev;
+ struct bladerf_stream *rx_stream;
+ struct bladerf_stream *tx_stream;
+ // using pkt2buf = blade_otw_buffer<2, blade_speed_buffer_type::SS>;
+ using tx_buf_q_type = spsc_cond_timeout<BLADE_NUM_BUFFERS, dev_buf_t *, true, false>;
+ const unsigned int rxFullScale, txFullScale;
+ const int rxtxdelay;
+
+ float rxgain, txgain;
+ static std::atomic<bool> stop_lower_threads_flag;
+ double rxfreq_cache, txfreq_cache;
+
+ struct ms_trx_config {
+ int tx_freq;
+ int rx_freq;
+ int sample_rate;
+ int bandwidth;
+
+ public:
+ ms_trx_config() : tx_freq(881e6), rx_freq(926e6), sample_rate(((1625e3 / 6) * 4)), bandwidth(1e6)
+ {
+ }
+ } cfg;
+
+ struct buf_mgmt {
+ void **rx_samples;
+ void **tx_samples;
+ tx_buf_q_type bufptrqueue;
+
+ } buf_mgmt;
+
+ virtual ~blade_hw()
+ {
+ close_device();
+ }
+ blade_hw()
+ : rxFullScale(2047), txFullScale(2047), rxtxdelay(-60), rxgain(30), txgain(30), rxfreq_cache(0),
+ txfreq_cache(0)
+ {
+ }
+
+ void close_device()
+ {
+ if (dev) {
+ if (tx_stream) {
+ bladerf_deinit_stream(tx_stream);
+ }
+
+ if (rx_stream) {
+ bladerf_deinit_stream(rx_stream);
+ }
+
+ bladerf_enable_module(dev, BLADERF_MODULE_RX, false);
+ bladerf_enable_module(dev, BLADERF_MODULE_TX, false);
+
+ bladerf_close(dev);
+ dev = NULL;
+ }
+ }
+
+ int init_device(bh_fn_t rxh, bh_fn_t txh)
+ {
+ struct bladerf_rational_rate rate = { 0, static_cast<uint64_t>((1625e3 * 4)) * 64, 6 * 64 }, actual;
+
+ bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_DEBUG);
+ bladerf_set_usb_reset_on_open(true);
+
+ blade_check(bladerf_open, &dev, "");
+ if (!dev) {
+ std::cerr << "open failed, device missing?" << std::endl;
+ exit(0);
+ }
+ if (bladerf_device_speed(dev) != bladerf_dev_speed::BLADERF_DEVICE_SPEED_SUPER) {
+ std::cerr << "open failed, only superspeed (usb3) supported!" << std::endl;
+ return -1;
+ }
+
+ blade_check(bladerf_set_tuning_mode, dev, bladerf_tuning_mode::BLADERF_TUNING_MODE_FPGA);
+
+ bool is_locked;
+ blade_check(bladerf_set_pll_enable, dev, true);
+ uint64_t refclock = 10000000UL;
+ blade_check(bladerf_set_pll_refclk, dev, refclock);
+ for (int i = 0; i < 20; i++) {
+ usleep(50 * 1000);
+ bladerf_get_pll_lock_state(dev, &is_locked);
+
+ if (is_locked)
+ break;
+ }
+ if (!is_locked) {
+ std::cerr << "unable to lock refclk!" << std::endl;
+ return -1;
+ }
+
+ blade_check(bladerf_set_rational_sample_rate, dev, BLADERF_CHANNEL_RX(0), &rate, &actual);
+ blade_check(bladerf_set_rational_sample_rate, dev, BLADERF_CHANNEL_TX(0), &rate, &actual);
+
+ blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_RX(0), (bladerf_frequency)cfg.rx_freq);
+ blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_TX(0), (bladerf_frequency)cfg.tx_freq);
+
+ blade_check(bladerf_set_bandwidth, dev, BLADERF_CHANNEL_RX(0), (bladerf_bandwidth)cfg.bandwidth,
+ (bladerf_bandwidth *)NULL);
+ blade_check(bladerf_set_bandwidth, dev, BLADERF_CHANNEL_TX(0), (bladerf_bandwidth)cfg.bandwidth,
+ (bladerf_bandwidth *)NULL);
+
+ blade_check(bladerf_set_gain_mode, dev, BLADERF_CHANNEL_RX(0), BLADERF_GAIN_MGC);
+ setRxGain(rxgain, 0);
+ setTxGain(txgain, 0);
+ usleep(1000);
+
+ bladerf_set_stream_timeout(dev, BLADERF_TX, 10);
+ bladerf_set_stream_timeout(dev, BLADERF_RX, 10);
+
+ blade_check(bladerf_init_stream, &rx_stream, dev, getrxcb(rxh), &buf_mgmt.rx_samples, BLADE_NUM_BUFFERS,
+ BLADERF_FORMAT_SC16_Q11_META, BLADE_BUFFER_SIZE, NUM_TRANSFERS, (void *)this);
+
+ blade_check(bladerf_init_stream, &tx_stream, dev, gettxcb(txh), &buf_mgmt.tx_samples, BLADE_NUM_BUFFERS,
+ BLADERF_FORMAT_SC16_Q11_META, BLADE_BUFFER_SIZE, NUM_TRANSFERS, (void *)this);
+
+ for (unsigned int i = 0; i < BLADE_NUM_BUFFERS; i++) {
+ auto cur_buffer = reinterpret_cast<tx_buf_q_type::elem_t *>(buf_mgmt.tx_samples);
+ buf_mgmt.bufptrqueue.spsc_push(&cur_buffer[i]);
+ }
+
+ return 0;
+ }
+
+ void actually_enable_streams()
+ {
+ blade_check(bladerf_enable_module, dev, BLADERF_MODULE_RX, true);
+ usleep(1000);
+ blade_check(bladerf_enable_module, dev, BLADERF_MODULE_TX, true);
+ }
+
+ bool tuneTx(double freq, size_t chan = 0)
+ {
+ if (txfreq_cache == freq)
+ return true;
+ msleep(15);
+ blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_TX(0), (bladerf_frequency)freq);
+ txfreq_cache = freq;
+ msleep(15);
+ return true;
+ };
+ bool tuneRx(double freq, size_t chan = 0)
+ {
+ if (rxfreq_cache == freq)
+ return true;
+ msleep(15);
+ blade_check(bladerf_set_frequency, dev, BLADERF_CHANNEL_RX(0), (bladerf_frequency)freq);
+ rxfreq_cache = freq;
+ msleep(15);
+ return true;
+ };
+ bool tuneRxOffset(double offset, size_t chan = 0)
+ {
+ return true;
+ };
+
+ double setRxGain(double dB, size_t chan = 0)
+ {
+ rxgain = dB;
+ msleep(15);
+ blade_check(bladerf_set_gain, dev, BLADERF_CHANNEL_RX(0), (bladerf_gain)dB);
+ msleep(15);
+ return dB;
+ };
+ double setTxGain(double dB, size_t chan = 0)
+ {
+ txgain = dB;
+ msleep(15);
+ blade_check(bladerf_set_gain, dev, BLADERF_CHANNEL_TX(0), (bladerf_gain)dB);
+ msleep(15);
+ return dB;
+ };
+ int setPowerAttenuation(int atten, size_t chan = 0)
+ {
+ return atten;
+ };
+
+ static void check_timestamp(dev_buf_t *rcd)
+ {
+ static bool first = true;
+ static uint64_t last_ts;
+ if (first) {
+ first = false;
+ last_ts = rcd->m[0].ts;
+ } else if (last_ts + rcd->actual_samples_per_buffer() != rcd->m[0].ts) {
+ std::cerr << "RX Overrun!" << last_ts << " " << rcd->actual_samples_per_buffer() << " "
+ << last_ts + rcd->actual_samples_per_buffer() << " " << rcd->m[0].ts << std::endl;
+ last_ts = rcd->m[0].ts;
+ } else {
+ last_ts = rcd->m[0].ts;
+ }
+ }
+
+ bladerf_stream_cb getrxcb(bh_fn_t rxbh)
+ {
+ // C cb -> no capture!
+ static auto rxbhfn = rxbh;
+ return [](struct bladerf *dev, struct bladerf_stream *stream, struct bladerf_metadata *meta,
+ void *samples, size_t num_samples, void *user_data) -> void * {
+ // struct blade_hw *trx = (struct blade_hw *)user_data;
+ static int to_skip = 0;
+ dev_buf_t *rcd = (dev_buf_t *)samples;
+
+ if (stop_lower_threads_flag)
+ return BLADERF_STREAM_SHUTDOWN;
+
+ if (to_skip < 120) // prevents weird overflows on startup
+ to_skip++;
+ else {
+ check_timestamp(rcd);
+ rxbhfn(rcd);
+ }
+
+ return samples;
+ };
+ }
+ bladerf_stream_cb gettxcb(bh_fn_t txbh)
+ {
+ // C cb -> no capture!
+ static auto txbhfn = txbh;
+ return [](struct bladerf *dev, struct bladerf_stream *stream, struct bladerf_metadata *meta,
+ void *samples, size_t num_samples, void *user_data) -> void * {
+ struct blade_hw *trx = (struct blade_hw *)user_data;
+ auto ptr = reinterpret_cast<tx_buf_q_type::elem_t>(samples);
+
+ if (samples) // put buffer address back into queue, ready to be reused
+ trx->buf_mgmt.bufptrqueue.spsc_push(&ptr);
+
+ if (stop_lower_threads_flag)
+ return BLADERF_STREAM_SHUTDOWN;
+
+ return BLADERF_STREAM_NO_DATA;
+ };
+ }
+
+ auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
+ {
+ using thist = decltype(this);
+ auto fn = [](void *args) -> void * {
+ thist t = reinterpret_cast<thist>(args);
+ int status = 0;
+ if (!stop_lower_threads_flag)
+ status = bladerf_stream(t->rx_stream, BLADERF_RX_X1);
+ if (status < 0)
+ std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
+
+ return 0;
+ };
+ return fn;
+ }
+ auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
+ {
+ using thist = decltype(this);
+ auto fn = [](void *args) -> void * {
+ thist t = reinterpret_cast<thist>(args);
+ int status = 0;
+ if (!stop_lower_threads_flag)
+ status = bladerf_stream(t->tx_stream, BLADERF_TX_X1);
+ if (status < 0)
+ std::cerr << "rx stream error! " << bladerf_strerror(status) << std::endl;
+
+ return 0;
+ };
+ return fn;
+ }
+
+ void submit_burst_ts(blade_sample_type *buffer, int len, uint64_t ts)
+ {
+ tx_buf_q_type::elem_t rcd;
+
+ // exit by submitting a dummy buffer to assure the libbladerf stream mutex is happy (thread!)
+ if (!buffer) {
+ bladerf_submit_stream_buffer(tx_stream, (void *)BLADERF_STREAM_SHUTDOWN, 1000);
+ return;
+ }
+
+ //get empty bufer from list
+ while (!buf_mgmt.bufptrqueue.spsc_pop(&rcd))
+ buf_mgmt.bufptrqueue.spsc_prep_pop();
+ assert(rcd != nullptr);
+
+ rcd->write_n_burst(buffer, len, ts + rxtxdelay); // blade xa4 specific delay!
+ blade_check(bladerf_submit_stream_buffer_nb, tx_stream, (void *)rcd);
+ }
+};
diff --git a/Transceiver52M/ms/itrq.h b/Transceiver52M/ms/itrq.h
new file mode 100644
index 0000000..69ff515
--- /dev/null
+++ b/Transceiver52M/ms/itrq.h
@@ -0,0 +1,249 @@
+#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 <atomic>
+
+#include <condition_variable>
+#include <mutex>
+#include <sys/eventfd.h>
+#include <unistd.h>
+
+namespace spsc_detail
+{
+template <bool block_read, bool block_write>
+class spsc_cond_timeout_detail {
+ std::condition_variable cond_r, cond_w;
+ std::mutex lr, lw;
+ std::atomic_int r_flag, w_flag;
+ const int timeout_ms = 200;
+
+ public:
+ explicit spsc_cond_timeout_detail() : r_flag(0), w_flag(0)
+ {
+ }
+
+ ~spsc_cond_timeout_detail()
+ {
+ }
+
+ ssize_t spsc_check_r()
+ {
+ std::unique_lock<std::mutex> lk(lr);
+ if (cond_r.wait_for(lk, std::chrono::milliseconds(timeout_ms), [&] { return r_flag != 0; })) {
+ r_flag--;
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ ssize_t spsc_check_w()
+ {
+ std::unique_lock<std::mutex> lk(lw);
+ if (cond_w.wait_for(lk, std::chrono::milliseconds(timeout_ms), [&] { return w_flag != 0; })) {
+ w_flag--;
+ return 1;
+ } else {
+ return 0;
+ }
+ }
+ void spsc_notify_r()
+ {
+ std::unique_lock<std::mutex> lk(lr);
+ r_flag++;
+ cond_r.notify_one();
+ }
+ void spsc_notify_w()
+ {
+ std::unique_lock<std::mutex> lk(lw);
+ w_flag++;
+ cond_w.notify_one();
+ }
+};
+
+template <bool block_read, bool block_write>
+class spsc_cond_detail {
+ std::condition_variable cond_r, cond_w;
+ std::mutex lr, lw;
+ std::atomic_int r_flag, w_flag;
+
+ public:
+ explicit spsc_cond_detail() : r_flag(0), w_flag(0)
+ {
+ }
+
+ ~spsc_cond_detail()
+ {
+ }
+
+ ssize_t spsc_check_r()
+ {
+ std::unique_lock<std::mutex> lk(lr);
+ while (r_flag == 0)
+ cond_r.wait(lk);
+ r_flag--;
+ return 1;
+ }
+ ssize_t spsc_check_w()
+ {
+ std::unique_lock<std::mutex> lk(lw);
+ while (w_flag == 0)
+ cond_w.wait(lk);
+ w_flag--;
+ return 1;
+ }
+ void spsc_notify_r()
+ {
+ std::unique_lock<std::mutex> lk(lr);
+ r_flag++;
+ cond_r.notify_one();
+ }
+ void spsc_notify_w()
+ {
+ std::unique_lock<std::mutex> lk(lw);
+ w_flag++;
+ cond_w.notify_one();
+ }
+};
+
+// originally designed for select loop integration
+template <bool block_read, bool block_write>
+class spsc_efd_detail {
+ int efd_r, efd_w; /* eventfds used to block/notify readers/writers */
+
+ public:
+ explicit spsc_efd_detail()
+ : efd_r(eventfd(0, block_read ? 0 : EFD_NONBLOCK)), efd_w(eventfd(1, block_write ? 0 : EFD_NONBLOCK))
+ {
+ }
+
+ ~spsc_efd_detail()
+ {
+ close(efd_r);
+ close(efd_w);
+ }
+
+ ssize_t spsc_check_r()
+ {
+ uint64_t efdr;
+ return read(efd_r, &efdr, sizeof(uint64_t));
+ }
+ ssize_t spsc_check_w()
+ {
+ uint64_t efdr;
+ return read(efd_w, &efdr, sizeof(uint64_t));
+ }
+ void spsc_notify_r()
+ {
+ uint64_t efdu = 1;
+ write(efd_r, &efdu, sizeof(uint64_t));
+ }
+ void spsc_notify_w()
+ {
+ uint64_t efdu = 1;
+ write(efd_w, &efdu, sizeof(uint64_t));
+ }
+ int get_r_efd()
+ {
+ return efd_r;
+ }
+ int get_w_efd()
+ {
+ return efd_w;
+ }
+};
+
+template <unsigned int SZ, typename ELEM, bool block_read, bool block_write, template <bool, bool> class T>
+class spsc : public T<block_read, block_write> {
+ static_assert(SZ > 0, "queues need a size...");
+ std::atomic<unsigned int> readptr;
+ std::atomic<unsigned int> writeptr;
+
+ ELEM buf[SZ];
+
+ public:
+ using base_t = T<block_read, block_write>;
+ using elem_t = ELEM;
+ explicit spsc() : readptr(0), writeptr(0)
+ {
+ }
+
+ ~spsc()
+ {
+ }
+
+ /*! Adds element to the queue by copying the data.
+ * \param[in] elem input buffer, must match the originally configured queue buffer size!.
+ * \returns true if queue was not full and element was successfully pushed */
+ bool spsc_push(const ELEM *elem)
+ {
+ size_t cur_wp, cur_rp;
+ cur_wp = writeptr.load(std::memory_order_relaxed);
+ cur_rp = readptr.load(std::memory_order_acquire);
+ if ((cur_wp + 1) % SZ == cur_rp) {
+ if (block_write)
+ base_t::spsc_check_w(); /* blocks, ensures next (!) call succeeds */
+ return false;
+ }
+ buf[cur_wp] = *elem;
+ writeptr.store((cur_wp + 1) % SZ, std::memory_order_release);
+ if (block_read)
+ base_t::spsc_notify_r(); /* fine after release */
+ return true;
+ }
+
+ /*! Removes element from the queue by copying the data.
+ * \param[in] elem output buffer, must match the originally configured queue buffer size!.
+ * \returns true if queue was not empty and element was successfully removed */
+ bool spsc_pop(ELEM *elem)
+ {
+ size_t cur_wp, cur_rp;
+ cur_wp = writeptr.load(std::memory_order_acquire);
+ cur_rp = readptr.load(std::memory_order_relaxed);
+
+ if (cur_wp == cur_rp) /* blocks via prep_pop */
+ return false;
+
+ *elem = buf[cur_rp];
+ readptr.store((cur_rp + 1) % SZ, std::memory_order_release);
+ if (block_write)
+ base_t::spsc_notify_w();
+ return true;
+ }
+
+ /*! Reads the read-fd of the queue, which, depending on settings passed on queue creation, blocks.
+ * This function can be used to deliberately wait for a non-empty queue on the read side.
+ * \returns result of reading the fd. */
+ ssize_t spsc_prep_pop()
+ {
+ return base_t::spsc_check_r();
+ }
+};
+
+} // namespace spsc_detail
+
+template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
+class spsc_evfd : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_efd_detail> {};
+template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
+class spsc_cond : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_detail> {};
+template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
+class spsc_cond_timeout
+ : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_timeout_detail> {}; \ No newline at end of file
diff --git a/Transceiver52M/ms/l1ctl_server.c b/Transceiver52M/ms/l1ctl_server.c
new file mode 100644
index 0000000..9e9083f
--- /dev/null
+++ b/Transceiver52M/ms/l1ctl_server.c
@@ -0,0 +1,275 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ * UNIX socket server for L1CTL
+ *
+ * (C) 2013 by Sylvain Munaut <tnt@246tNt.com>
+ * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ * (C) 2022 by by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * 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
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <sys/un.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include <osmocom/core/talloc.h>
+#include <osmocom/core/select.h>
+#include <osmocom/core/socket.h>
+#include <osmocom/core/write_queue.h>
+
+#include <osmocom/bb/trxcon/logging.h>
+#include <osmocom/bb/trxcon/l1ctl_server.h>
+
+#define LOGP_CLI(cli, cat, level, fmt, args...) LOGP(cat, level, "%s" fmt, (cli)->log_prefix, ##args)
+
+static int l1ctl_client_read_cb(struct osmo_fd *ofd)
+{
+ struct l1ctl_client *client = (struct l1ctl_client *)ofd->data;
+ struct msgb *msg;
+ uint16_t len;
+ int rc;
+
+ /* Attempt to read from socket */
+ rc = read(ofd->fd, &len, L1CTL_MSG_LEN_FIELD);
+ if (rc != L1CTL_MSG_LEN_FIELD) {
+ if (rc <= 0) {
+ LOGP_CLI(client, DL1D, LOGL_NOTICE, "L1CTL connection error: read() failed (rc=%d): %s\n", rc,
+ strerror(errno));
+ } else {
+ LOGP_CLI(client, DL1D, LOGL_NOTICE, "L1CTL connection error: short read\n");
+ rc = -EIO;
+ }
+ l1ctl_client_conn_close(client);
+ return rc;
+ }
+
+ /* Check message length */
+ len = ntohs(len);
+ if (len > L1CTL_LENGTH) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR, "Length is too big: %u\n", len);
+ return -EINVAL;
+ }
+
+ /* Allocate a new msg */
+ msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM, L1CTL_HEADROOM, "l1ctl_rx_msg");
+ if (!msg) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to allocate msg\n");
+ return -ENOMEM;
+ }
+
+ msg->l1h = msgb_put(msg, len);
+ rc = read(ofd->fd, msg->l1h, msgb_l1len(msg));
+ if (rc != len) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR, "Can not read data: len=%d < rc=%d: %s\n", len, rc, strerror(errno));
+ msgb_free(msg);
+ return rc;
+ }
+
+ /* Debug print */
+ LOGP_CLI(client, DL1D, LOGL_DEBUG, "RX: '%s'\n", osmo_hexdump(msg->data, msg->len));
+
+ /* Call L1CTL handler */
+ client->server->cfg->conn_read_cb(client, msg);
+
+ return 0;
+}
+
+static int l1ctl_client_write_cb(struct osmo_fd *ofd, struct msgb *msg)
+{
+ struct l1ctl_client *client = (struct l1ctl_client *)ofd->data;
+ int len;
+
+ if (ofd->fd <= 0)
+ return -EINVAL;
+
+ len = write(ofd->fd, msg->data, msg->len);
+ if (len != msg->len) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to write data: written (%d) < msg_len (%d)\n", len,
+ msg->len);
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Connection handler */
+static int l1ctl_server_conn_cb(struct osmo_fd *sfd, unsigned int flags)
+{
+ struct l1ctl_server *server = (struct l1ctl_server *)sfd->data;
+ struct l1ctl_client *client;
+ int rc, client_fd;
+
+ client_fd = accept(sfd->fd, NULL, NULL);
+ if (client_fd < 0) {
+ LOGP(DL1C, LOGL_ERROR,
+ "Failed to accept() a new connection: "
+ "%s\n",
+ strerror(errno));
+ return client_fd;
+ }
+
+ if (server->cfg->num_clients_max > 0 /* 0 means unlimited */ &&
+ server->num_clients >= server->cfg->num_clients_max) {
+ LOGP(DL1C, LOGL_NOTICE,
+ "L1CTL server cannot accept more "
+ "than %u connection(s)\n",
+ server->cfg->num_clients_max);
+ close(client_fd);
+ return -ENOMEM;
+ }
+
+ client = talloc_zero(server, struct l1ctl_client);
+ if (client == NULL) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to allocate an L1CTL client\n");
+ close(client_fd);
+ return -ENOMEM;
+ }
+
+ /* Init the client's write queue */
+ osmo_wqueue_init(&client->wq, 100);
+ INIT_LLIST_HEAD(&client->wq.bfd.list);
+
+ client->wq.write_cb = &l1ctl_client_write_cb;
+ client->wq.read_cb = &l1ctl_client_read_cb;
+ osmo_fd_setup(&client->wq.bfd, client_fd, OSMO_FD_READ, &osmo_wqueue_bfd_cb, client, 0);
+
+ /* Register the client's write queue */
+ rc = osmo_fd_register(&client->wq.bfd);
+ if (rc != 0) {
+ LOGP(DL1C, LOGL_ERROR, "Failed to register a new connection fd\n");
+ close(client->wq.bfd.fd);
+ talloc_free(client);
+ return rc;
+ }
+
+ llist_add_tail(&client->list, &server->clients);
+ client->id = server->next_client_id++;
+ client->server = server;
+ server->num_clients++;
+
+ LOGP(DL1C, LOGL_NOTICE, "L1CTL server got a new connection (id=%u)\n", client->id);
+
+ if (client->server->cfg->conn_accept_cb != NULL)
+ client->server->cfg->conn_accept_cb(client);
+
+ return 0;
+}
+
+int l1ctl_client_send(struct l1ctl_client *client, struct msgb *msg)
+{
+ uint8_t *len;
+
+ /* Debug print */
+ LOGP_CLI(client, DL1D, LOGL_DEBUG, "TX: '%s'\n", osmo_hexdump(msg->data, msg->len));
+
+ if (msg->l1h != msg->data)
+ LOGP_CLI(client, DL1D, LOGL_INFO, "Message L1 header != Message Data\n");
+
+ /* Prepend 16-bit length before sending */
+ len = msgb_push(msg, L1CTL_MSG_LEN_FIELD);
+ osmo_store16be(msg->len - L1CTL_MSG_LEN_FIELD, len);
+
+ if (osmo_wqueue_enqueue(&client->wq, msg) != 0) {
+ LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to enqueue msg!\n");
+ msgb_free(msg);
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void l1ctl_client_conn_close(struct l1ctl_client *client)
+{
+ struct l1ctl_server *server = client->server;
+
+ LOGP_CLI(client, DL1C, LOGL_NOTICE, "Closing L1CTL connection\n");
+
+ if (server->cfg->conn_close_cb != NULL)
+ server->cfg->conn_close_cb(client);
+
+ /* Close connection socket */
+ osmo_fd_unregister(&client->wq.bfd);
+ close(client->wq.bfd.fd);
+ client->wq.bfd.fd = -1;
+
+ /* Clear pending messages */
+ osmo_wqueue_clear(&client->wq);
+
+ client->server->num_clients--;
+ llist_del(&client->list);
+ talloc_free(client);
+
+ /* If this was the last client, reset the client IDs generator to 0.
+ * This way avoid assigning huge unreadable client IDs like 26545. */
+ if (llist_empty(&server->clients))
+ server->next_client_id = 0;
+}
+
+struct l1ctl_server *l1ctl_server_alloc(void *ctx, const struct l1ctl_server_cfg *cfg)
+{
+ struct l1ctl_server *server;
+ int rc;
+
+ LOGP(DL1C, LOGL_NOTICE, "Init L1CTL server (sock_path=%s)\n", cfg->sock_path);
+
+ server = talloc(ctx, struct l1ctl_server);
+ OSMO_ASSERT(server != NULL);
+
+ *server = (struct l1ctl_server){
+ .clients = LLIST_HEAD_INIT(server->clients),
+ .cfg = cfg,
+ };
+
+ /* conn_read_cb shall not be NULL */
+ OSMO_ASSERT(cfg->conn_read_cb != NULL);
+
+ /* Bind connection handler */
+ osmo_fd_setup(&server->ofd, -1, OSMO_FD_READ, &l1ctl_server_conn_cb, server, 0);
+
+ rc = osmo_sock_unix_init_ofd(&server->ofd, SOCK_STREAM, 0, cfg->sock_path, OSMO_SOCK_F_BIND);
+ if (rc < 0) {
+ LOGP(DL1C, LOGL_ERROR, "Could not create UNIX socket: %s\n", strerror(errno));
+ talloc_free(server);
+ return NULL;
+ }
+
+ return server;
+}
+
+void l1ctl_server_free(struct l1ctl_server *server)
+{
+ LOGP(DL1C, LOGL_NOTICE, "Shutdown L1CTL server\n");
+
+ /* Close all client connections */
+ while (!llist_empty(&server->clients)) {
+ struct l1ctl_client *client = llist_entry(server->clients.next, struct l1ctl_client, list);
+ l1ctl_client_conn_close(client);
+ }
+
+ /* Unbind listening socket */
+ if (server->ofd.fd != -1) {
+ osmo_fd_unregister(&server->ofd);
+ close(server->ofd.fd);
+ server->ofd.fd = -1;
+ }
+
+ talloc_free(server);
+}
diff --git a/Transceiver52M/ms/l1ctl_server_cb.cpp b/Transceiver52M/ms/l1ctl_server_cb.cpp
new file mode 100644
index 0000000..42f64ac
--- /dev/null
+++ b/Transceiver52M/ms/l1ctl_server_cb.cpp
@@ -0,0 +1,71 @@
+/*
+ * (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/>.
+ *
+ */
+
+extern "C" {
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/trxcon_fsm.h>
+#include <osmocom/bb/trxcon/l1ctl_server.h>
+}
+#include "ms_trxcon_if.h"
+
+static struct l1ctl_server_cfg server_cfg;
+static struct l1ctl_server *server = NULL;
+
+static int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg)
+{
+ struct trxcon_inst *trxcon = (struct trxcon_inst *)l1c->priv;
+
+ return trxcon_l1ctl_receive(trxcon, msg);
+}
+
+static void l1ctl_conn_accept_cb(struct l1ctl_client *l1c)
+{
+ l1c->log_prefix = talloc_strdup(l1c, g_trxcon->log_prefix);
+ l1c->priv = g_trxcon;
+ g_trxcon->l2if = l1c;
+}
+
+static void l1ctl_conn_close_cb(struct l1ctl_client *l1c)
+{
+ struct trxcon_inst *trxcon = (struct trxcon_inst *)l1c->priv;
+
+ if (trxcon == NULL || trxcon->fi == NULL)
+ return;
+
+ osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_L2IF_FAILURE, NULL);
+}
+
+bool trxc_l1ctl_init(void *tallctx)
+{
+ /* Start the L1CTL server */
+ server_cfg = (struct l1ctl_server_cfg){
+ /* TODO: make path configurable */
+ .sock_path = "/tmp/osmocom_l2", .num_clients_max = 1,
+ .conn_read_cb = &l1ctl_rx_cb, .conn_accept_cb = &l1ctl_conn_accept_cb,
+ .conn_close_cb = &l1ctl_conn_close_cb,
+ };
+
+ server = l1ctl_server_alloc(tallctx, &server_cfg);
+ if (server == NULL) {
+ return false;
+ }
+ return true;
+}
diff --git a/Transceiver52M/ms/logging.c b/Transceiver52M/ms/logging.c
new file mode 100644
index 0000000..af397e0
--- /dev/null
+++ b/Transceiver52M/ms/logging.c
@@ -0,0 +1,98 @@
+/*
+ * OsmocomBB <-> SDR connection bridge
+ *
+ * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
+ *
+ * All Rights Reserved
+ *
+ * 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
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ *
+ */
+
+#include <osmocom/core/application.h>
+#include <osmocom/core/logging.h>
+#include <osmocom/core/utils.h>
+
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/logging.h>
+
+static struct log_info_cat trxcon_log_info_cat[] = {
+ [DAPP] = {
+ .name = "DAPP",
+ .description = "Application",
+ .color = "\033[1;35m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DL1C] = {
+ .name = "DL1C",
+ .description = "Layer 1 control interface",
+ .color = "\033[1;31m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DL1D] = {
+ .name = "DL1D",
+ .description = "Layer 1 data",
+ .color = "\033[1;31m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DTRXC] = {
+ .name = "DTRXC",
+ .description = "Transceiver control interface",
+ .color = "\033[1;33m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DTRXD] = {
+ .name = "DTRXD",
+ .description = "Transceiver data interface",
+ .color = "\033[1;33m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DSCH] = {
+ .name = "DSCH",
+ .description = "Scheduler management",
+ .color = "\033[1;36m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DSCHD] = {
+ .name = "DSCHD",
+ .description = "Scheduler data",
+ .color = "\033[1;36m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+ [DGPRS] = {
+ .name = "DGPRS",
+ .description = "L1 GPRS (MAC layer)",
+ .color = "\033[1;36m",
+ .enabled = 1, .loglevel = LOGL_NOTICE,
+ },
+};
+
+static const struct log_info trxcon_log_info = {
+ .cat = trxcon_log_info_cat,
+ .num_cat = ARRAY_SIZE(trxcon_log_info_cat),
+};
+
+static const int trxcon_log_cfg[] = {
+ [TRXCON_LOGC_FSM] = DAPP,
+ [TRXCON_LOGC_L1C] = DL1C,
+ [TRXCON_LOGC_L1D] = DL1D,
+ [TRXCON_LOGC_SCHC] = DSCH,
+ [TRXCON_LOGC_SCHD] = DSCHD,
+ [TRXCON_LOGC_GPRS] = DGPRS,
+};
+
+void trxc_log_init(void *tallctx)
+{
+ osmo_init_logging2(tallctx, &trxcon_log_info);
+ log_target_file_switch_to_wqueue(osmo_stderr_target);
+
+ trxcon_set_log_cfg(&trxcon_log_cfg[0], ARRAY_SIZE(trxcon_log_cfg));
+}
diff --git a/Transceiver52M/ms/ms.cpp b/Transceiver52M/ms/ms.cpp
new file mode 100644
index 0000000..4ae8668
--- /dev/null
+++ b/Transceiver52M/ms/ms.cpp
@@ -0,0 +1,160 @@
+
+/*
+ * (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 "GSMCommon.h"
+#include <atomic>
+#include <cassert>
+#include <complex>
+#include <iostream>
+#include <cstdlib>
+#include <cstdio>
+#include <thread>
+#include <fstream>
+
+#include "ms.h"
+
+extern "C" {
+#include "sch.h"
+}
+
+#include "threadsched.h"
+
+dummylog ms_trx::dummy_log;
+
+#ifdef DBGXX
+const int offsetrange = 200;
+const int offset_start = -15;
+static int offset_ctr = 0;
+#endif
+
+template <>
+std::atomic<bool> ms_trx::base::stop_lower_threads_flag(false);
+
+int ms_trx::init_dev_and_streams()
+{
+ int status = 0;
+ status = init_device(rx_bh(), tx_bh());
+ if (status < 0) {
+ std::cerr << "failed to init dev!" << std::endl;
+ return -1;
+ }
+ return status;
+}
+
+bh_fn_t ms_trx::rx_bh()
+{
+ return [this](dev_buf_t *rcd) -> int {
+ if (this->search_for_sch(rcd) == SCH_STATE::FOUND)
+ this->grab_bursts(rcd);
+ return 0;
+ };
+}
+
+bh_fn_t ms_trx::tx_bh()
+{
+ return [this](dev_buf_t *rcd) -> int {
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-variable"
+ auto y = this;
+#pragma GCC diagnostic pop
+ /* nothing to do here */
+ return 0;
+ };
+}
+
+void ms_trx::start_lower_ms()
+{
+ if (stop_lower_threads_flag)
+ return;
+ auto fn = get_rx_burst_handler_fn(rx_bh());
+ lower_rx_task = spawn_worker_thread(sched_params::thread_names::RXRUN, fn, this);
+
+ usleep(1000);
+ auto fn2 = get_tx_burst_handler_fn(tx_bh());
+ lower_tx_task = spawn_worker_thread(sched_params::thread_names::TXRUN, fn2, this);
+
+ actually_enable_streams();
+}
+
+void ms_trx::set_upper_ready(bool is_ready)
+{
+ upper_is_ready = is_ready;
+}
+
+void ms_trx::stop_threads()
+{
+ std::cerr << "killing threads..." << std::endl;
+ stop_lower_threads_flag = true;
+ close_device();
+ std::cerr << "dev closed..." << std::endl;
+ pthread_join(lower_rx_task, nullptr);
+ std::cerr << "L rx dead..." << std::endl;
+ pthread_join(lower_tx_task, nullptr);
+ std::cerr << "L tx dead..." << std::endl;
+}
+
+void ms_trx::submit_burst(blade_sample_type *buffer, int len, GSM::Time target)
+{
+ int64_t now_ts;
+ GSM::Time now_time;
+ target.incTN(3); // ul dl offset
+ int target_fn = target.FN();
+ int target_tn = target.TN();
+ timekeeper.get_both(&now_time, &now_ts);
+
+ auto diff_fn = GSM::FNDelta(target_fn, now_time.FN());
+ int diff_tn = (target_tn - (int)now_time.TN()) % 8;
+ auto tosend = GSM::Time(diff_fn, 0);
+
+ if (diff_tn > 0)
+ tosend.incTN(diff_tn);
+ else
+ tosend.decTN(-diff_tn);
+
+ // in theory fn equal and tn+3 equal is also a problem...
+ if (diff_fn < 0 || (diff_fn == 0 && (target_tn-now_time.TN() < 3))) {
+ std::cerr << "## TX too late?! fn DIFF:" << diff_fn << " tn LOCAL: " << now_time.TN()
+ << " tn OTHER: " << target_tn << std::endl;
+ return;
+ }
+
+ int64_t send_ts = now_ts + tosend.FN() * 8 * ONE_TS_BURST_LEN + tosend.TN() * ONE_TS_BURST_LEN - timing_advance;
+#ifdef DBGXX
+ auto check = now_time + tosend;
+ std::cerr << "## fn DIFF: " << diff_fn << " ## tn DIFF: " << diff_tn << " tn LOCAL/OTHER: " << now_time.TN()
+ << "/" << target_tn << " tndiff" << diff_tn << " tosend:" << tosend.FN() << ":" << tosend.TN()
+ << " check: " << check.FN() << ":" << check.TN() << " target: " << target.FN() << ":" << target.TN()
+ << " ts now: " << now_ts << " target ts:" << send_ts << std::endl;
+#endif
+#if 0
+ auto check = now_time + tosend;
+ unsigned int pad = 4 * 4;
+ blade_sample_type buf2[len + pad];
+ std::fill(buf2, buf2 + pad, 0);
+ memcpy(&buf2[pad], buffer, len * sizeof(blade_sample_type));
+
+ assert(target.FN() == check.FN());
+ assert(target.TN() == check.TN());
+ submit_burst_ts(buf2, len + pad, send_ts - pad);
+#else
+ submit_burst_ts(buffer, len, send_ts);
+#endif
+}
diff --git a/Transceiver52M/ms/ms.h b/Transceiver52M/ms/ms.h
new file mode 100644
index 0000000..2ad78de
--- /dev/null
+++ b/Transceiver52M/ms/ms.h
@@ -0,0 +1,301 @@
+#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 <atomic>
+#include <cassert>
+#include <complex>
+#include <cstdint>
+#include <mutex>
+#include <iostream>
+// #include <thread>
+
+#if defined(BUILDBLADE)
+#include "bladerf_specific.h"
+#define BASET blade_hw<ms_trx>
+#elif defined(BUILDUHD)
+#include "uhd_specific.h"
+#define BASET uhd_hw<ms_trx>
+#else
+#error wat? no device..
+#endif
+
+#include "Complex.h"
+#include "GSMCommon.h"
+#include "itrq.h"
+#include "threadpool.h"
+#include "threadsched.h"
+
+const unsigned int ONE_TS_BURST_LEN = (3 + 58 + 26 + 58 + 3 + 8.25) * 4 /*sps*/;
+const unsigned int SCH_LEN_SPS = (ONE_TS_BURST_LEN * 8 /*ts*/ * 12 /*frames*/);
+
+template <typename T>
+void clamp_array(T *start2, unsigned int len, T max)
+{
+ for (unsigned int i = 0; i < len; i++) {
+ const T t1 = start2[i] < -max ? -max : start2[i];
+ const T t2 = t1 > max ? max : t1;
+ start2[i] = t2;
+ }
+}
+
+namespace cvt_internal
+{
+
+template <typename SRC_T, typename ST>
+void convert_and_scale_i(float *dst, const SRC_T *src, unsigned int src_len, ST scale)
+{
+ for (unsigned int i = 0; i < src_len; i++)
+ dst[i] = static_cast<float>(src[i]) * scale;
+}
+
+template <typename DST_T, typename ST>
+void convert_and_scale_i(DST_T *dst, const float *src, unsigned int src_len, ST scale)
+{
+ for (unsigned int i = 0; i < src_len; i++)
+ dst[i] = static_cast<DST_T>(src[i] * scale);
+}
+
+template <typename ST>
+void convert_and_scale_i(float *dst, const float *src, unsigned int src_len, ST scale)
+{
+ for (unsigned int i = 0; i < src_len; i++)
+ dst[i] = src[i] * scale;
+}
+
+template <typename T>
+struct is_complex : std::false_type {
+ using baset = T;
+ static const unsigned int len_mul = 1;
+};
+
+template <typename T>
+struct is_complex<std::complex<T>> : std::true_type {
+ using baset = typename std::complex<T>::value_type;
+ static const unsigned int len_mul = 2;
+};
+
+template <typename T>
+struct is_complex<Complex<T>> : std::true_type {
+ using baset = typename Complex<T>::value_type;
+ static const unsigned int len_mul = 2;
+};
+
+} // namespace cvt_internal
+
+template <typename DST_T, typename SRC_T, typename ST>
+void convert_and_scale(DST_T *dst, const SRC_T *src, unsigned int src_len, ST scale)
+{
+ using vd = typename cvt_internal::is_complex<DST_T>::baset;
+ using vs = typename cvt_internal::is_complex<SRC_T>::baset;
+ return cvt_internal::convert_and_scale_i((vd *)dst, (vs *)src, src_len, scale);
+}
+
+template <typename array_t>
+float normed_abs_sum(array_t *src, int len)
+{
+ using vd = typename cvt_internal::is_complex<array_t>::baset;
+ auto len_mul = cvt_internal::is_complex<array_t>::len_mul;
+ auto ptr = reinterpret_cast<const vd *>(src);
+ float sum = 0;
+ for (unsigned int i = 0; i < len * len_mul; i++)
+ sum += std::abs(ptr[i]);
+ sum /= len * len_mul;
+ return sum;
+}
+
+struct one_burst {
+ one_burst()
+ {
+ }
+ GSM::Time gsmts;
+ union {
+ blade_sample_type burst[ONE_TS_BURST_LEN];
+ char sch_bits[148];
+ };
+};
+
+using rx_queue_t = spsc_cond_timeout<4, one_burst, true, false>;
+
+enum class SCH_STATE { SEARCHING, FOUND };
+
+class dummylog : private std::streambuf {
+ std::ostream null_stream;
+
+ public:
+ dummylog() : null_stream(this){};
+ ~dummylog() override{};
+ std::ostream &operator()()
+ {
+ return null_stream;
+ }
+ int overflow(int c) override
+ {
+ return c;
+ }
+};
+
+// keeps relationship between gsm time and (continuously adjusted) ts
+class time_keeper {
+ GSM::Time global_time_keeper;
+ int64_t global_ts_keeper;
+ std::mutex m;
+
+ public:
+ time_keeper() : global_time_keeper(0), global_ts_keeper(0)
+ {
+ }
+
+ void set(GSM::Time t, int64_t ts)
+ {
+ std::lock_guard<std::mutex> g(m);
+ global_time_keeper = t;
+ global_ts_keeper = ts;
+ }
+ void inc_both()
+ {
+ std::lock_guard<std::mutex> g(m);
+ global_time_keeper.incTN(1);
+ global_ts_keeper += ONE_TS_BURST_LEN;
+ }
+ void inc_and_update(int64_t new_ts)
+ {
+ std::lock_guard<std::mutex> g(m);
+ global_time_keeper.incTN(1);
+ global_ts_keeper = new_ts;
+ // std::cerr << "u " << new_ts << std::endl;
+ }
+ void inc_and_update_safe(int64_t new_ts)
+ {
+ std::lock_guard<std::mutex> g(m);
+ auto diff = new_ts - global_ts_keeper;
+ assert(diff < 1.5 * ONE_TS_BURST_LEN);
+ assert(diff > 0.5 * ONE_TS_BURST_LEN);
+ global_time_keeper.incTN(1);
+ global_ts_keeper = new_ts;
+ // std::cerr << "s " << new_ts << std::endl;
+ }
+ void dec_by_one()
+ {
+ std::lock_guard<std::mutex> g(m);
+ global_time_keeper.decTN(1);
+ global_ts_keeper -= ONE_TS_BURST_LEN;
+ }
+ auto get_ts()
+ {
+ std::lock_guard<std::mutex> g(m);
+ return global_ts_keeper;
+ }
+ auto gsmtime()
+ {
+ std::lock_guard<std::mutex> g(m);
+ return global_time_keeper;
+ }
+ void get_both(GSM::Time *t, int64_t *ts)
+ {
+ std::lock_guard<std::mutex> g(m);
+ *t = global_time_keeper;
+ *ts = global_ts_keeper;
+ }
+};
+
+using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>;
+
+// used to globally initialize the sched/hw information
+struct sched_hw_info {
+ int hw_cpus;
+ sched_params::target hw_target;
+
+ sched_hw_info()
+ {
+ hw_cpus = std::thread::hardware_concurrency();
+ hw_target = hw_cpus > 4 ? sched_params::target::ODROID : sched_params::target::PI4;
+ set_sched_target(hw_target);
+ std::cerr << "scheduling for: " << (hw_cpus > 4 ? "odroid" : "pi4") << std::endl;
+ }
+};
+
+struct ms_trx : public BASET, public sched_hw_info {
+ using base = BASET;
+ static dummylog dummy_log;
+ unsigned int mTSC;
+ unsigned int mBSIC;
+ int timing_advance;
+ bool do_auto_gain;
+
+ pthread_t lower_rx_task;
+ pthread_t lower_tx_task;
+
+ // provides bursts to upper rx thread
+ rx_queue_t rxqueue;
+
+ blade_sample_type *first_sch_buf;
+ blade_sample_type *burst_copy_buffer;
+
+ uint64_t first_sch_buf_rcv_ts;
+ std::atomic<bool> rcv_done;
+ std::atomic<bool> sch_thread_done;
+
+ int64_t temp_ts_corr_offset = 0;
+ int64_t first_sch_ts_start = -1;
+
+ time_keeper timekeeper;
+ single_thread_pool worker_thread; // uses base class sched target hw info
+
+ void start_lower_ms();
+ std::atomic<bool> upper_is_ready;
+ void set_upper_ready(bool is_ready);
+
+ bool handle_sch_or_nb();
+ bool handle_sch(bool first = false);
+ bool decode_sch(char *bits, bool update_global_clock);
+ SCH_STATE search_for_sch(dev_buf_t *rcd);
+ void grab_bursts(dev_buf_t *rcd);
+
+ int init_dev_and_streams();
+ void stop_threads();
+ void *rx_cb(ms_trx *t);
+ void *tx_cb();
+ void maybe_update_gain(one_burst &brst);
+
+ ms_trx()
+ : mTSC(0), mBSIC(0), timing_advance(0), do_auto_gain(false), rxqueue(),
+ first_sch_buf(new blade_sample_type[SCH_LEN_SPS]),
+ burst_copy_buffer(new blade_sample_type[ONE_TS_BURST_LEN]), first_sch_buf_rcv_ts(0),
+ rcv_done{ false }, sch_thread_done{ false }, upper_is_ready(false)
+ {
+ }
+
+ virtual ~ms_trx()
+ {
+ delete[] burst_copy_buffer;
+ delete[] first_sch_buf;
+ }
+ bh_fn_t rx_bh();
+ bh_fn_t tx_bh();
+
+ void submit_burst(blade_sample_type *buffer, int len, GSM::Time);
+ void set_ta(int val)
+ {
+ assert(val > -127 && val < 128);
+ timing_advance = val * 4;
+ }
+};
diff --git a/Transceiver52M/ms/ms_rx_lower.cpp b/Transceiver52M/ms/ms_rx_lower.cpp
new file mode 100644
index 0000000..d894e96
--- /dev/null
+++ b/Transceiver52M/ms/ms_rx_lower.cpp
@@ -0,0 +1,420 @@
+/*
+ * (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 "sigProcLib.h"
+#include "signalVector.h"
+#include <atomic>
+#include <cassert>
+#include <complex>
+#include <iostream>
+#include <future>
+
+#include "ms.h"
+#include "grgsm_vitac/grgsm_vitac.h"
+
+#include "threadpool.h"
+
+extern "C" {
+#include "sch.h"
+}
+
+#ifdef LOG
+#undef LOG
+#endif
+
+#if !defined(NODAMNLOG)
+#define DBGLG(...) ms_trx::dummy_log()
+#else
+#define DBGLG(...) std::cerr
+#endif
+
+#if !defined(NODAMNLOG)
+#define DBGLG2(...) ms_trx::dummy_log()
+#else
+#define DBGLG2(...) std::cerr
+#endif
+
+#define PRINT_Q_OVERFLOW
+
+bool ms_trx::decode_sch(char *bits, bool update_global_clock)
+{
+ int fn;
+ struct sch_info sch;
+ ubit_t info[GSM_SCH_INFO_LEN];
+ sbit_t data[GSM_SCH_CODED_LEN];
+
+ memcpy(&data[0], &bits[3], 39);
+ memcpy(&data[39], &bits[106], 39);
+
+ if (!gsm_sch_decode(info, data)) {
+ gsm_sch_parse(info, &sch);
+
+ if (update_global_clock) {
+ DBGLG() << "SCH : Decoded values" << std::endl;
+ DBGLG() << " BSIC: " << sch.bsic << std::endl;
+ DBGLG() << " TSC: " << (sch.bsic & 0x7) << std::endl;
+ DBGLG() << " T1 : " << sch.t1 << std::endl;
+ DBGLG() << " T2 : " << sch.t2 << std::endl;
+ DBGLG() << " T3p : " << sch.t3p << std::endl;
+ DBGLG() << " FN : " << gsm_sch_to_fn(&sch) << std::endl;
+ }
+
+ fn = gsm_sch_to_fn(&sch);
+ if (fn < 0) { // how? wh?
+ DBGLG() << "SCH : Failed to convert FN " << std::endl;
+ return false;
+ }
+
+ if (update_global_clock) {
+ mBSIC = sch.bsic;
+ mTSC = sch.bsic & 0x7;
+ timekeeper.set(fn, 0);
+ // global_time_keeper.FN(fn);
+ // global_time_keeper.TN(0);
+ }
+
+ return true;
+ }
+ return false;
+}
+
+void ms_trx::maybe_update_gain(one_burst &brst)
+{
+ static_assert((sizeof(brst.burst) / sizeof(brst.burst[0])) == ONE_TS_BURST_LEN, "wtf, buffer size mismatch?");
+ const int avgburst_num = 8 * 20; // ~ 50*4.5ms = 90ms?
+ static_assert(avgburst_num * 577 > (50 * 1000), "can't update faster then blade wait time?");
+ const unsigned int rx_max_cutoff = (rxFullScale * 2) / 3;
+ static int gain_check = 0;
+ static float runmean = 0;
+ float sum = normed_abs_sum(&brst.burst[0], ONE_TS_BURST_LEN);
+ runmean = gain_check ? (runmean * (gain_check + 2) - 1 + sum) / (gain_check + 2) : sum;
+
+ if (gain_check == avgburst_num - 1) {
+ DBGLG2() << "\x1B[32m #RXG \033[0m" << rxgain << " " << runmean << " " << sum << std::endl;
+ auto gainoffset = runmean < (rxFullScale / 4 ? 4 : 2);
+ gainoffset = runmean < (rxFullScale / 2 ? 2 : 1);
+ float newgain = runmean < rx_max_cutoff ? rxgain + gainoffset : rxgain - gainoffset;
+ // FIXME: gian cutoff
+ if (newgain != rxgain && newgain <= 60) {
+ auto gain_fun = [this, newgain] { setRxGain(newgain); };
+ worker_thread.add_task(gain_fun);
+ }
+
+ runmean = 0;
+ }
+ gain_check = (gain_check + 1) % avgburst_num;
+}
+
+static char sch_demod_bits[148];
+
+bool ms_trx::handle_sch_or_nb()
+{
+ one_burst brst;
+ const auto current_gsm_time = timekeeper.gsmtime();
+ const auto is_sch = gsm_sch_check_ts(current_gsm_time.TN(), current_gsm_time.FN());
+
+ //either pass burst to upper layer for demod, OR pass demodded SCH to upper layer so we don't waste time processing it twice
+ brst.gsmts = current_gsm_time;
+
+ if (!is_sch) {
+ memcpy(brst.burst, burst_copy_buffer, sizeof(blade_sample_type) * ONE_TS_BURST_LEN);
+ } else {
+ handle_sch(false);
+ memcpy(brst.sch_bits, sch_demod_bits, sizeof(sch_demod_bits));
+ }
+
+ while (upper_is_ready && !rxqueue.spsc_push(&brst))
+ ;
+
+ if (do_auto_gain)
+ maybe_update_gain(brst);
+
+ return false;
+}
+
+static float sch_acq_buffer[SCH_LEN_SPS * 2];
+
+bool ms_trx::handle_sch(bool is_first_sch_acq)
+{
+ auto current_gsm_time = timekeeper.gsmtime();
+ const auto buf_len = is_first_sch_acq ? SCH_LEN_SPS : ONE_TS_BURST_LEN;
+ const auto which_in_buffer = is_first_sch_acq ? first_sch_buf : burst_copy_buffer;
+ memset((void *)&sch_acq_buffer[0], 0, sizeof(sch_acq_buffer));
+#if 1
+ const auto which_out_buffer = is_first_sch_acq ? sch_acq_buffer : &sch_acq_buffer[40 * 2];
+ const auto ss = reinterpret_cast<std::complex<float> *>(which_out_buffer);
+ std::complex<float> channel_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
+ int start;
+ convert_and_scale(which_out_buffer, which_in_buffer, buf_len * 2, 1.f / float(rxFullScale));
+ if (is_first_sch_acq) {
+ float max_corr = 0;
+ start = get_sch_buffer_chan_imp_resp(ss, &channel_imp_resp[0], buf_len, &max_corr);
+ } else {
+ start = get_sch_chan_imp_resp(ss, &channel_imp_resp[0]);
+ start = start < 39 ? start : 39;
+ start = start > -39 ? start : -39;
+ }
+ detect_burst_nb(&ss[start], &channel_imp_resp[0], 0, sch_demod_bits);
+
+ auto sch_decode_success = decode_sch(sch_demod_bits, is_first_sch_acq);
+#if 0
+ auto burst = new signalVector(buf_len, 50);
+ const auto corr_type = is_first_sch_acq ? sch_detect_type::SCH_DETECT_BUFFER : sch_detect_type::SCH_DETECT_FULL;
+ struct estim_burst_params ebp;
+
+ // scale like uhd, +-2k -> +-32k
+ convert_and_scale(burst->begin(), which_in_buffer, buf_len * 2, SAMPLE_SCALE_FACTOR);
+
+ auto rv = detectSCHBurst(*burst, 4, 4, corr_type, &ebp);
+
+ int howmuchdelay = ebp.toa * 4;
+ std::cerr << "ooffs: " << howmuchdelay << " " << std::endl;
+ std::cerr << "voffs: " << start << " " << sch_decode_success << std::endl;
+#endif
+ if (sch_decode_success) {
+ const auto ts_offset_symb = 4;
+ if (is_first_sch_acq) {
+ // update ts to first sample in sch buffer, to allow delay calc for current ts
+ first_sch_ts_start = first_sch_buf_rcv_ts + start - (ts_offset_symb * 4) - 1;
+ } else if (abs(start) > 1) {
+ // continuous sch tracking, only update if off too much
+ temp_ts_corr_offset += -start;
+ std::cerr << "offs: " << start << " " << temp_ts_corr_offset << std::endl;
+ }
+
+ return true;
+ } else {
+ DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << start << " " << current_gsm_time.FN()
+ << ":" << current_gsm_time.TN() << std::endl;
+ }
+#else
+ const auto ts_offset_symb = 4;
+ auto burst = new signalVector(buf_len, 50);
+ const auto corr_type = is_first_sch_acq ? sch_detect_type::SCH_DETECT_BUFFER : sch_detect_type::SCH_DETECT_FULL;
+ struct estim_burst_params ebp;
+
+ // scale like uhd, +-2k -> +-32k
+ convert_and_scale(burst->begin(), which_in_buffer, buf_len * 2, SAMPLE_SCALE_FACTOR);
+
+ auto rv = detectSCHBurst(*burst, 4, 4, corr_type, &ebp);
+
+ int howmuchdelay = ebp.toa * 4;
+
+ if (!rv) {
+ delete burst;
+ DBGLG() << "SCH : \x1B[31m detect fail \033[0m NOOOOOOOOOOOOOOOOOO toa:" << ebp.toa << " "
+ << current_gsm_time.FN() << ":" << current_gsm_time.TN() << std::endl;
+ return false;
+ }
+
+ SoftVector *bits;
+ if (is_first_sch_acq) {
+ // can't be legit with a buf size spanning _at least_ one SCH but delay that implies partial sch burst
+ if (howmuchdelay < 0 || (buf_len - howmuchdelay) < ONE_TS_BURST_LEN) {
+ delete burst;
+ return false;
+ }
+
+ struct estim_burst_params ebp2;
+ // auto sch_chunk = new signalVector(ONE_TS_BURST_LEN, 50);
+ // auto sch_chunk_start = sch_chunk->begin();
+ // memcpy(sch_chunk_start, sch_buf_f.data() + howmuchdelay, sizeof(std::complex<float>) * ONE_TS_BURST_LEN);
+
+ auto delay = delayVector(burst, NULL, -howmuchdelay);
+
+ scaleVector(*delay, (complex)1.0 / ebp.amp);
+
+ auto rv2 = detectSCHBurst(*delay, 4, 4, sch_detect_type::SCH_DETECT_FULL, &ebp2);
+ DBGLG() << "FIRST SCH : " << (rv2 ? "yes " : " ") << "Timing offset " << ebp2.toa << " symbols"
+ << std::endl;
+
+ bits = demodAnyBurst(*delay, SCH, 4, &ebp2);
+ delete delay;
+ } else {
+ bits = demodAnyBurst(*burst, SCH, 4, &ebp);
+ }
+
+ delete burst;
+
+ // clamp to +-1.5 because +-127 softbits scaled by 64 after -0.5 can be at most +-1.5
+ clamp_array(bits->begin(), 148, 1.5f);
+
+ float_to_sbit(&bits->begin()[0], (signed char *)&sch_demod_bits[0], 62, 148);
+ // float_to_sbit(&bits->begin()[106], &data[39], 62, 39);
+
+ if (decode_sch((char *)sch_demod_bits, is_first_sch_acq)) {
+ auto current_gsm_time_updated = timekeeper.gsmtime();
+ if (is_first_sch_acq) {
+ // update ts to first sample in sch buffer, to allow delay calc for current ts
+ first_sch_ts_start = first_sch_buf_rcv_ts + howmuchdelay - (ts_offset_symb * 4);
+ } else {
+ // continuous sch tracking, only update if off too much
+ auto diff = [](float x, float y) { return x > y ? x - y : y - x; };
+
+ auto d = diff(ebp.toa, ts_offset_symb);
+ if (abs(d) > 0.3) {
+ if (ebp.toa < ts_offset_symb)
+ ebp.toa = d;
+ else
+ ebp.toa = -d;
+ temp_ts_corr_offset += ebp.toa * 4;
+
+ DBGLG() << "offs: " << ebp.toa << " " << temp_ts_corr_offset << std::endl;
+ }
+ }
+
+ auto a = gsm_sch_check_fn(current_gsm_time_updated.FN() - 1);
+ auto b = gsm_sch_check_fn(current_gsm_time_updated.FN());
+ auto c = gsm_sch_check_fn(current_gsm_time_updated.FN() + 1);
+ DBGLG() << "L SCH : Timing offset " << rv << " " << ebp.toa << " " << a << b << c << "fn "
+ << current_gsm_time_updated.FN() << ":" << current_gsm_time_updated.TN() << std::endl;
+
+ delete bits;
+ return true;
+ } else {
+ DBGLG2() << "L SCH : \x1B[31m decode fail \033[0m @ toa:" << ebp.toa << " " << current_gsm_time.FN()
+ << ":" << current_gsm_time.TN() << std::endl;
+ }
+
+ delete bits;
+#endif
+ return false;
+}
+
+/*
+accumulates a full big buffer consisting of 8*12 timeslots, then:
+either
+1) adjusts gain if necessary and starts over
+2) searches and finds SCH and is done
+*/
+SCH_STATE ms_trx::search_for_sch(dev_buf_t *rcd)
+{
+ static unsigned int sch_pos = 0;
+ auto to_copy = SCH_LEN_SPS - sch_pos;
+
+ if (sch_thread_done)
+ return SCH_STATE::FOUND;
+
+ if (rcv_done)
+ return SCH_STATE::SEARCHING;
+
+ if (sch_pos == 0) // keep first ts for time delta calc
+ first_sch_buf_rcv_ts = rcd->get_first_ts();
+
+ if (to_copy) {
+ auto spsmax = rcd->actual_samples_per_buffer();
+ if (to_copy > (unsigned int)spsmax)
+ sch_pos += rcd->readall(first_sch_buf + sch_pos);
+ else
+ sch_pos += rcd->read_n(first_sch_buf + sch_pos, 0, to_copy);
+ } else { // (!to_copy)
+ sch_pos = 0;
+ rcv_done = true;
+ auto sch_search_fun = [this] {
+ const auto target_val = rxFullScale / 8;
+ float sum = normed_abs_sum(first_sch_buf, SCH_LEN_SPS);
+
+ //FIXME: arbitrary value, gain cutoff
+ if (sum > target_val || rxgain >= 60) // enough ?
+ sch_thread_done = this->handle_sch(true);
+ else {
+ std::cerr << "\x1B[32m #RXG \033[0m gain " << rxgain << " -> " << rxgain + 4
+ << " sample avg:" << sum << " target: >=" << target_val << std::endl;
+ setRxGain(rxgain + 4);
+ }
+
+ if (!sch_thread_done)
+ rcv_done = false; // retry!
+ };
+ worker_thread.add_task(sch_search_fun);
+ }
+ return SCH_STATE::SEARCHING;
+}
+
+void ms_trx::grab_bursts(dev_buf_t *rcd)
+{
+ // partial burst samples read from the last buffer
+ static int partial_rdofs = 0;
+ static bool first_call = true;
+ int to_skip = 0;
+
+ // round up to next burst by calculating the time between sch detection and now
+ if (first_call) {
+ const auto next_burst_start = rcd->get_first_ts() - first_sch_ts_start;
+ const auto fullts = next_burst_start / ONE_TS_BURST_LEN;
+ const auto fracts = next_burst_start % ONE_TS_BURST_LEN;
+ to_skip = ONE_TS_BURST_LEN - fracts;
+
+ for (unsigned int i = 0; i < fullts; i++)
+ timekeeper.inc_and_update(first_sch_ts_start + i * ONE_TS_BURST_LEN);
+
+ if (fracts)
+ timekeeper.inc_both();
+ // timekeeper.inc_and_update(first_sch_ts_start + 1 * ONE_TS_BURST_LEN);
+
+ timekeeper.dec_by_one(); // oops, off by one?
+
+ timekeeper.set(timekeeper.gsmtime(), rcd->get_first_ts() - ONE_TS_BURST_LEN + to_skip);
+
+ DBGLG() << "this ts: " << rcd->get_first_ts() << " diff full TN: " << fullts << " frac TN: " << fracts
+ << " GSM now: " << timekeeper.gsmtime().FN() << ":" << timekeeper.gsmtime().TN() << " is sch? "
+ << gsm_sch_check_fn(timekeeper.gsmtime().FN()) << std::endl;
+ first_call = false;
+ }
+
+ if (partial_rdofs) {
+ auto first_remaining = ONE_TS_BURST_LEN - partial_rdofs;
+ auto rd = rcd->read_n(burst_copy_buffer + partial_rdofs, 0, first_remaining);
+ if (rd != (int)first_remaining) {
+ partial_rdofs += rd;
+ return;
+ }
+
+ timekeeper.inc_and_update_safe(rcd->get_first_ts() - partial_rdofs);
+ handle_sch_or_nb();
+ to_skip = first_remaining;
+ }
+
+ // apply sample rate slippage compensation
+ to_skip -= temp_ts_corr_offset;
+
+ // FIXME: happens rarely, read_n start -1 blows up
+ // this is fine: will just be corrected one buffer later
+ if (to_skip < 0)
+ to_skip = 0;
+ else
+ temp_ts_corr_offset = 0;
+
+ const auto left_after_burst = rcd->actual_samples_per_buffer() - to_skip;
+
+ const int full = left_after_burst / ONE_TS_BURST_LEN;
+ const int frac = left_after_burst % ONE_TS_BURST_LEN;
+
+ for (int i = 0; i < full; i++) {
+ rcd->read_n(burst_copy_buffer, to_skip + i * ONE_TS_BURST_LEN, ONE_TS_BURST_LEN);
+ timekeeper.inc_and_update_safe(rcd->get_first_ts() + to_skip + i * ONE_TS_BURST_LEN);
+ handle_sch_or_nb();
+ }
+
+ if (frac)
+ rcd->read_n(burst_copy_buffer, to_skip + full * ONE_TS_BURST_LEN, frac);
+ partial_rdofs = frac;
+}
diff --git a/Transceiver52M/ms/ms_trxcon_if.cpp b/Transceiver52M/ms/ms_trxcon_if.cpp
new file mode 100644
index 0000000..7de9710
--- /dev/null
+++ b/Transceiver52M/ms/ms_trxcon_if.cpp
@@ -0,0 +1,78 @@
+/*
+ * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Eric Wild <ewild@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <atomic>
+#include "ms_trxcon_if.h"
+extern "C" {
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/l1ctl_server.h>
+#include <osmocom/core/panic.h>
+}
+
+extern tx_queue_t txq;
+extern cmd_queue_t cmdq_to_phy;
+extern cmdr_queue_t cmdq_from_phy;
+extern std::atomic<bool> g_exit_flag;
+// trxcon C call(back) if
+extern "C" {
+int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon_phyif_burst_req *br)
+{
+ if (br->burst_len == 0) // dummy/nope
+ return 0;
+ OSMO_ASSERT(br->burst != 0);
+
+ internal_q_tx_buf b(br);
+ if (!g_exit_flag)
+ txq.spsc_push(&b);
+ return 0;
+}
+
+int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon_phyif_cmd *cmd)
+{
+#ifdef TXDEBUG
+ DBGLG() << "TOP C: " << cmd2str(cmd->type) << std::endl;
+#endif
+ if (!g_exit_flag)
+ cmdq_to_phy.spsc_push(cmd);
+ // q for resp polling happens in main loop
+ return 0;
+}
+
+void trxcon_phyif_close(void *phyif)
+{
+}
+
+void trxcon_l1ctl_close(struct trxcon_inst *trxcon)
+{
+ /* Avoid use-after-free: both *fi and *trxcon are children of
+ * the L2IF (L1CTL connection), so we need to re-parent *fi
+ * to NULL before calling l1ctl_client_conn_close(). */
+ talloc_steal(NULL, trxcon->fi);
+ l1ctl_client_conn_close((struct l1ctl_client *)trxcon->l2if);
+}
+
+int trxcon_l1ctl_send(struct trxcon_inst *trxcon, struct msgb *msg)
+{
+ struct l1ctl_client *l1c = (struct l1ctl_client *)trxcon->l2if;
+
+ return l1ctl_client_send(l1c, msg);
+}
+}
diff --git a/Transceiver52M/ms/ms_trxcon_if.h b/Transceiver52M/ms/ms_trxcon_if.h
new file mode 100644
index 0000000..0928d40
--- /dev/null
+++ b/Transceiver52M/ms/ms_trxcon_if.h
@@ -0,0 +1,42 @@
+#pragma once
+/*
+ * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Eric Wild <ewild@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "ms.h"
+extern "C" {
+#include <osmocom/bb/trxcon/phyif.h>
+}
+
+extern struct trxcon_inst *g_trxcon;
+struct internal_q_tx_buf {
+ trxcon_phyif_burst_req r;
+ uint8_t buf[148];
+ internal_q_tx_buf() = default;
+ internal_q_tx_buf(const internal_q_tx_buf &) = delete;
+ internal_q_tx_buf &operator=(const internal_q_tx_buf &) = default;
+ internal_q_tx_buf(const struct trxcon_phyif_burst_req *br) : r(*br)
+ {
+ memcpy(buf, (void *)br->burst, br->burst_len);
+ }
+};
+using tx_queue_t = spsc_cond<8 * 1, internal_q_tx_buf, true, false>;
+using cmd_queue_t = spsc_cond_timeout<8 * 1, trxcon_phyif_cmd, true, false>;
+using cmdr_queue_t = spsc_cond<8 * 1, trxcon_phyif_rsp, false, false>;
diff --git a/Transceiver52M/ms/ms_upper.cpp b/Transceiver52M/ms/ms_upper.cpp
new file mode 100644
index 0000000..2e8bc11
--- /dev/null
+++ b/Transceiver52M/ms/ms_upper.cpp
@@ -0,0 +1,512 @@
+/*
+ * (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 <csignal>
+#include "sigProcLib.h"
+#include "ms.h"
+#include <signalVector.h>
+#include <radioVector.h>
+#include <radioInterface.h>
+#include <grgsm_vitac/grgsm_vitac.h>
+
+// #define TXDEBUG
+
+extern "C" {
+
+#include "sch.h"
+#include "convolve.h"
+#include "convert.h"
+
+#include <osmocom/core/application.h>
+#include <osmocom/gsm/gsm_utils.h>
+
+#include <osmocom/bb/trxcon/trxcon.h>
+#include <osmocom/bb/trxcon/trxcon_fsm.h>
+#include <osmocom/bb/trxcon/l1ctl_server.h>
+
+extern void trxc_log_init(void *tallctx);
+#ifdef LSANDEBUG
+void __lsan_do_recoverable_leak_check();
+#endif
+}
+
+#include "ms_trxcon_if.h"
+#include "ms_upper.h"
+#include "threadsched.h"
+
+extern bool trxc_l1ctl_init(void *tallctx);
+struct trxcon_inst *g_trxcon;
+tx_queue_t txq;
+cmd_queue_t cmdq_to_phy;
+cmdr_queue_t cmdq_from_phy;
+
+#ifdef LOG
+#undef LOG
+#define LOG(...) upper_trx::dummy_log()
+#endif
+
+#define DBGLG(...) upper_trx::dummy_log()
+
+std::atomic<bool> g_exit_flag;
+
+void upper_trx::stop_upper_threads()
+{
+ g_exit_flag = true;
+
+ pthread_join(thr_control, NULL);
+ pthread_join(thr_tx, NULL);
+}
+
+void upper_trx::start_threads()
+{
+ DBGLG(...) << "spawning threads.." << std::endl;
+
+ thr_control = spawn_worker_thread(
+ sched_params::thread_names::U_CTL,
+ [](void *args) -> void * {
+ upper_trx *t = reinterpret_cast<upper_trx *>(args);
+#ifdef TXDEBUG
+ struct sched_param param;
+ int policy;
+ pthread_getschedparam(pthread_self(), &policy, &param);
+ printf("ID: %lu, CPU: %d policy = %d priority = %d\n", pthread_self(), sched_getcpu(), policy,
+ param.sched_priority);
+#endif
+ std::cerr << "started U control!" << std::endl;
+ while (!g_exit_flag) {
+ t->driveControl();
+ }
+ std::cerr << "exit U control!" << std::endl;
+
+ return 0;
+ },
+ this);
+ thr_tx = spawn_worker_thread(
+ sched_params::thread_names::U_TX,
+ [](void *args) -> void * {
+ upper_trx *t = reinterpret_cast<upper_trx *>(args);
+#ifdef TXDEBUG
+ struct sched_param param;
+ int policy;
+ pthread_getschedparam(pthread_self(), &policy, &param);
+ printf("ID: %lu, CPU: %d policy = %d priority = %d\n", pthread_self(), sched_getcpu(), policy,
+ param.sched_priority);
+#endif
+ std::cerr << "started U tx!" << std::endl;
+ while (!g_exit_flag) {
+ t->driveTx();
+ }
+ std::cerr << "exit U tx!" << std::endl;
+
+ return 0;
+ },
+ this);
+
+#ifdef LSANDEBUG
+ std::thread([this] {
+ set_name_aff_sched(sched_params::thread_names::LEAKCHECK);
+
+ while (1) {
+ std::this_thread::sleep_for(std::chrono::seconds{ 5 });
+ __lsan_do_recoverable_leak_check();
+ }
+ }).detach();
+#endif
+}
+
+void upper_trx::main_loop()
+{
+ set_name_aff_sched(sched_params::thread_names::U_RX);
+ set_upper_ready(true);
+ while (!g_exit_flag) {
+ driveReceiveFIFO();
+ osmo_select_main(1);
+
+ trxcon_phyif_rsp r;
+ if (cmdq_from_phy.spsc_pop(&r)) {
+ DBGLG() << "HAVE RESP:" << r.type << std::endl;
+ trxcon_phyif_handle_rsp(g_trxcon, &r);
+ }
+ }
+ set_upper_ready(false);
+ std::cerr << "exit U rx!" << std::endl;
+ mOn = false;
+}
+
+// signalvector is owning despite claiming not to, but we can pretend, too..
+static void static_free(void *wData){};
+static void *static_alloc(size_t newSize)
+{
+ return 0;
+};
+
+bool upper_trx::pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset)
+{
+ float pow, avg = 1.0;
+ const auto zero_pad_len = 40; // give the VA some runway for misaligned bursts
+ const auto workbuf_size = zero_pad_len + ONE_TS_BURST_LEN + zero_pad_len;
+ static complex workbuf[workbuf_size];
+
+ static signalVector sv(workbuf, zero_pad_len, ONE_TS_BURST_LEN, static_alloc, static_free);
+ one_burst e;
+ auto ss = reinterpret_cast<std::complex<float> *>(&workbuf[zero_pad_len]);
+ std::fill(workbuf, workbuf + workbuf_size, 0);
+ // assert(sv.begin() == &workbuf[40]);
+
+ while (!rxqueue.spsc_pop(&e)) {
+ rxqueue.spsc_prep_pop();
+ }
+
+ wTime = e.gsmts;
+
+ const auto is_sch = gsm_sch_check_ts(wTime.TN(), wTime.FN());
+ const auto is_fcch = gsm_fcch_check_ts(wTime.TN(), wTime.FN());
+
+ trxcon_phyif_rtr_ind i = { static_cast<uint32_t>(wTime.FN()), static_cast<uint8_t>(wTime.TN()) };
+ trxcon_phyif_rtr_rsp r = {};
+ trxcon_phyif_handle_rtr_ind(g_trxcon, &i, &r);
+ if (!(r.flags & TRXCON_PHYIF_RTR_F_ACTIVE))
+ return false;
+
+ if (is_fcch) {
+ // return trash
+ return true;
+ }
+
+ if (is_sch) {
+ for (int i = 0; i < 148; i++)
+ (demodded_softbits)[i] = (e.sch_bits[i]);
+ RSSI = 10;
+ timingOffset = 0;
+ return true;
+ }
+
+#if 1
+ convert_and_scale(ss, e.burst, ONE_TS_BURST_LEN * 2, 1.f / float(rxFullScale));
+
+ pow = energyDetect(sv, 20 * 4 /*sps*/);
+ if (pow < -1) {
+ LOG(ALERT) << "Received empty burst";
+ return false;
+ }
+
+ avg = sqrt(pow);
+ {
+ float ncmax;
+ std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
+ auto normal_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp[0], &ncmax, mTSC);
+#ifdef DBGXX
+ float dcmax;
+ std::complex<float> chan_imp_resp2[CHAN_IMP_RESP_LENGTH * d_OSR];
+ auto dummy_burst_start = get_norm_chan_imp_resp(ss, &chan_imp_resp2[0], &dcmax, TS_DUMMY);
+ auto is_nb = ncmax > dcmax;
+ // DBGLG() << " U " << (is_nb ? "NB" : "DB") << "@ o nb: " << normal_burst_start
+ // << " o db: " << dummy_burst_start << std::endl;
+#endif
+ normal_burst_start = normal_burst_start < 39 ? normal_burst_start : 39;
+ normal_burst_start = normal_burst_start > -39 ? normal_burst_start : -39;
+#ifdef DBGXX
+ // fprintf(stderr, "%s %d\n", (is_nb ? "N":"D"), burst_time.FN());
+ // if (is_nb)
+#endif
+ detect_burst_nb(ss, &chan_imp_resp[0], normal_burst_start, demodded_softbits);
+#ifdef DBGXX
+ // else
+ // detect_burst(ss, &chan_imp_resp2[0], dummy_burst_start, outbin);
+#endif
+ }
+#else
+
+ // lower layer sch detection offset, easy to verify by just printing the detected value using both the va+sigproc code.
+ convert_and_scale(ss + 16, e.burst, ONE_TS_BURST_LEN * 2, 15);
+
+ pow = energyDetect(sv, 20 * 4 /*sps*/);
+ if (pow < -1) {
+ LOG(ALERT) << "Received empty burst";
+ return false;
+ }
+
+ avg = sqrt(pow);
+
+ /* Detect normal or RACH bursts */
+ CorrType type = CorrType::TSC;
+ struct estim_burst_params ebp;
+ auto rc = detectAnyBurst(sv, mTSC, 3, 4, type, 48, &ebp);
+ if (rc > 0) {
+ type = (CorrType)rc;
+ }
+
+ if (rc < 0) {
+ std::cerr << "UR : \x1B[31m rx fail \033[0m @ toa:" << ebp.toa << " " << e.gsmts.FN() << ":"
+ << e.gsmts.TN() << std::endl;
+ return false;
+ }
+ SoftVector *bits = demodAnyBurst(sv, type, 4, &ebp);
+
+ SoftVector::const_iterator burstItr = bits->begin();
+ // invert and fix to +-127 sbits
+ for (int ii = 0; ii < 148; ii++) {
+ demodded_softbits[ii] = *burstItr++ > 0.0f ? -127 : 127;
+ }
+ delete bits;
+
+#endif
+ RSSI = (int)floor(20.0 * log10(rxFullScale / avg));
+ // FIXME: properly handle offset, sch/nb alignment diff? handled by lower anyway...
+ timingOffset = (int)round(0);
+
+ return true;
+}
+
+void upper_trx::driveReceiveFIFO()
+{
+ int RSSI;
+ int TOA; // in 1/256 of a symbol
+ GSM::Time burstTime;
+
+ if (!mOn)
+ return;
+
+ if (pullRadioVector(burstTime, RSSI, TOA)) {
+ trxcon_phyif_burst_ind bi;
+ bi.fn = burstTime.FN();
+ bi.tn = burstTime.TN();
+ bi.rssi = RSSI;
+ bi.toa256 = TOA;
+ bi.burst = (sbit_t *)demodded_softbits;
+ bi.burst_len = sizeof(demodded_softbits);
+ trxcon_phyif_handle_burst_ind(g_trxcon, &bi);
+ }
+
+ burstTime.incTN(2);
+ struct trxcon_phyif_rts_ind rts {
+ static_cast<uint32_t>(burstTime.FN()), static_cast<uint8_t>(burstTime.TN())
+ };
+ trxcon_phyif_handle_rts_ind(g_trxcon, &rts);
+}
+
+void upper_trx::driveTx()
+{
+ internal_q_tx_buf e;
+ static BitVector newBurst(sizeof(e.buf));
+ while (!txq.spsc_pop(&e)) {
+ txq.spsc_prep_pop();
+ }
+
+ // ensure our tx cb is tickled and can exit
+ if (g_exit_flag) {
+ submit_burst_ts(0, 1337, 1);
+ return;
+ }
+
+ internal_q_tx_buf *burst = &e;
+
+#ifdef TXDEBUG2
+ DBGLG() << "got burst!" << burst->r.fn << ":" << burst->ts << " current: " << timekeeper.gsmtime().FN()
+ << " dff: " << (int64_t)((int64_t)timekeeper.gsmtime().FN() - (int64_t)burst->r.fn) << std::endl;
+#endif
+
+ auto currTime = GSM::Time(burst->r.fn, burst->r.tn);
+ int RSSI = (int)burst->r.pwr;
+
+ BitVector::iterator itr = newBurst.begin();
+ auto *bufferItr = burst->buf;
+ while (itr < newBurst.end())
+ *itr++ = *bufferItr++;
+
+ auto txburst = modulateBurst(newBurst, 8 + (currTime.TN() % 4 == 0), 4);
+ scaleVector(*txburst, txFullScale * pow(10, -RSSI / 10));
+
+ // float -> int16
+ blade_sample_type burst_buf[txburst->size()];
+ convert_and_scale(burst_buf, txburst->begin(), txburst->size() * 2, 1);
+#ifdef TXDEBUG2
+ auto check = signalVector(txburst->size(), 40);
+ convert_and_scale(check.begin(), burst_buf, txburst->size() * 2, 1);
+ estim_burst_params ebp;
+ auto d = detectAnyBurst(check, 2, 4, 4, CorrType::RACH, 40, &ebp);
+ if (d)
+ DBGLG() << "RACH D! " << ebp.toa << std::endl;
+ else
+ DBGLG() << "RACH NOOOOOOOOOO D! " << ebp.toa << std::endl;
+
+ // memory read --binary --outfile /tmp/mem.bin &burst_buf[0] --count 2500 --force
+#endif
+ submit_burst(burst_buf, txburst->size(), currTime);
+ delete txburst;
+}
+
+#ifdef TXDEBUG
+static const char *cmd2str(trxcon_phyif_cmd_type c)
+{
+ switch (c) {
+ case TRXCON_PHYIF_CMDT_RESET:
+ return "TRXCON_PHYIF_CMDT_RESET";
+ case TRXCON_PHYIF_CMDT_POWERON:
+ return "TRXCON_PHYIF_CMDT_POWERON";
+ case TRXCON_PHYIF_CMDT_POWEROFF:
+ return "TRXCON_PHYIF_CMDT_POWEROFF";
+ case TRXCON_PHYIF_CMDT_MEASURE:
+ return "TRXCON_PHYIF_CMDT_MEASURE";
+ case TRXCON_PHYIF_CMDT_SETFREQ_H0:
+ return "TRXCON_PHYIF_CMDT_SETFREQ_H0";
+ case TRXCON_PHYIF_CMDT_SETFREQ_H1:
+ return "TRXCON_PHYIF_CMDT_SETFREQ_H1";
+ case TRXCON_PHYIF_CMDT_SETSLOT:
+ return "TRXCON_PHYIF_CMDT_SETSLOT";
+ case TRXCON_PHYIF_CMDT_SETTA:
+ return "TRXCON_PHYIF_CMDT_SETTA";
+ default:
+ return "UNKNOWN COMMAND!";
+ }
+}
+
+static void print_cmd(trxcon_phyif_cmd_type c)
+{
+ DBGLG() << "handling " << cmd2str(c) << std::endl;
+}
+#endif
+
+bool upper_trx::driveControl()
+{
+ trxcon_phyif_rsp r;
+ trxcon_phyif_cmd cmd;
+ while (!cmdq_to_phy.spsc_pop(&cmd)) {
+ cmdq_to_phy.spsc_prep_pop();
+ if (g_exit_flag)
+ return false;
+ }
+
+ if (g_exit_flag)
+ return false;
+
+#ifdef TXDEBUG
+ print_cmd(cmd.type);
+#endif
+
+ switch (cmd.type) {
+ case TRXCON_PHYIF_CMDT_RESET:
+ set_ta(0);
+ break;
+ case TRXCON_PHYIF_CMDT_POWERON:
+ if (!mOn) {
+ mOn = true;
+ start_lower_ms();
+ }
+ break;
+ case TRXCON_PHYIF_CMDT_POWEROFF:
+ break;
+ case TRXCON_PHYIF_CMDT_MEASURE:
+ r.type = trxcon_phyif_cmd_type::TRXCON_PHYIF_CMDT_MEASURE;
+ 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;
+ tuneRx(gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 0) * 1000 * 100);
+ tuneTx(gsm_arfcn2freq10(cmd.param.measure.band_arfcn, 1) * 1000 * 100);
+ cmdq_from_phy.spsc_push(&r);
+ break;
+ case TRXCON_PHYIF_CMDT_SETFREQ_H0:
+ tuneRx(gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 0) * 1000 * 100);
+ tuneTx(gsm_arfcn2freq10(cmd.param.setfreq_h0.band_arfcn, 1) * 1000 * 100);
+ break;
+ case TRXCON_PHYIF_CMDT_SETFREQ_H1:
+ break;
+ case TRXCON_PHYIF_CMDT_SETSLOT:
+ break;
+ case TRXCON_PHYIF_CMDT_SETTA:
+ set_ta(cmd.param.setta.ta);
+ break;
+ }
+ return false;
+}
+
+void sighandler(int sigset)
+{
+ // we might get a sigpipe in case the l1ctl ud socket disconnects because mobile quits
+ if (sigset == SIGPIPE || sigset == SIGINT) {
+ 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_phyif_cmd cmd = {};
+ internal_q_tx_buf b = {};
+ txq.spsc_push(&b);
+ cmdq_to_phy.spsc_push(&cmd);
+ msleep(200);
+
+ return;
+ }
+}
+
+int main(int argc, char *argv[])
+{
+ auto tall_trxcon_ctx = talloc_init("trxcon context");
+ signal(SIGPIPE, sighandler);
+ signal(SIGINT, sighandler);
+
+ msgb_talloc_ctx_init(tall_trxcon_ctx, 0);
+ trxc_log_init(tall_trxcon_ctx);
+
+ /* Configure pretty logging */
+ log_set_print_extended_timestamp(osmo_stderr_target, 1);
+ log_set_print_category_hex(osmo_stderr_target, 0);
+ log_set_print_category(osmo_stderr_target, 1);
+ log_set_print_level(osmo_stderr_target, 1);
+
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_BASENAME);
+ log_set_print_filename_pos(osmo_stderr_target, LOG_FILENAME_POS_LINE_END);
+
+ osmo_fsm_log_timeouts(true);
+
+ g_trxcon = trxcon_inst_alloc(tall_trxcon_ctx, 0);
+ g_trxcon->gsmtap = nullptr;
+ g_trxcon->phyif = nullptr;
+ g_trxcon->phy_quirks.fbsb_extend_fns = 866; // 4 seconds, known to work.
+
+ convolve_init();
+ convert_init();
+ sigProcLibSetup();
+ initvita();
+
+ int status = 0;
+ auto trx = new upper_trx();
+ trx->do_auto_gain = true;
+
+ status = trx->init_dev_and_streams();
+ if (status < 0) {
+ std::cerr << "Error initializing hardware, quitting.." << std::endl;
+ return -1;
+ }
+ set_name_aff_sched(sched_params::thread_names::MAIN);
+
+ if (!trxc_l1ctl_init(tall_trxcon_ctx)) {
+ std::cerr << "Error initializing l1ctl, quitting.." << std::endl;
+ return -1;
+ }
+
+ // blocking, will return when global exit is requested
+ trx->start_threads();
+ trx->main_loop();
+ trx->stop_threads();
+ trx->stop_upper_threads();
+
+ return status;
+}
diff --git a/Transceiver52M/ms/ms_upper.h b/Transceiver52M/ms/ms_upper.h
new file mode 100644
index 0000000..06f7c54
--- /dev/null
+++ b/Transceiver52M/ms/ms_upper.h
@@ -0,0 +1,48 @@
+#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 <netdb.h>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+
+#include "GSMCommon.h"
+#include "ms.h"
+
+class upper_trx : public ms_trx {
+ volatile bool mOn;
+ char demodded_softbits[444];
+
+ // void driveControl();
+ bool pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
+
+ pthread_t thr_control, thr_tx;
+
+ public:
+ void start_threads();
+ void main_loop();
+ void stop_upper_threads();
+
+ bool driveControl();
+ void driveReceiveFIFO();
+ void driveTx();
+
+ upper_trx() : mOn(false){};
+};
diff --git a/Transceiver52M/ms/sch.c b/Transceiver52M/ms/sch.c
new file mode 100644
index 0000000..34809a2
--- /dev/null
+++ b/Transceiver52M/ms/sch.c
@@ -0,0 +1,329 @@
+/*
+ * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
+ * (C) 2016 by Tom Tsou <tom.tsou@ettus.com>
+ * (C) 2017 by Harald Welte <laforge@gnumonks.org>
+ * (C) 2022 by 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> / Eric Wild <ewild@sysmocom.de>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ */
+
+#include <complex.h>
+#include <stdio.h>
+#include <math.h>
+#include <string.h>
+
+#include <osmocom/core/bits.h>
+#include <osmocom/core/conv.h>
+#include <osmocom/core/utils.h>
+#include <osmocom/core/crcgen.h>
+#include <osmocom/coding/gsm0503_coding.h>
+#include <osmocom/coding/gsm0503_parity.h>
+
+#include "sch.h"
+
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wunused-variable"
+
+/* GSM 04.08, 9.1.30 Synchronization channel information */
+struct sch_packed_info {
+ ubit_t t1_hi[2];
+ ubit_t bsic[6];
+ ubit_t t1_md[8];
+ ubit_t t3p_hi[2];
+ ubit_t t2[5];
+ ubit_t t1_lo[1];
+ ubit_t t3p_lo[1];
+} __attribute__((packed));
+
+struct sch_burst {
+ sbit_t tail0[3];
+ sbit_t data0[39];
+ sbit_t etsc[64];
+ sbit_t data1[39];
+ sbit_t tail1[3];
+ sbit_t guard[8];
+} __attribute__((packed));
+
+static const uint8_t sch_next_output[][2] = {
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
+ { 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
+};
+
+static const uint8_t sch_next_state[][2] = {
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+ { 0, 1 }, { 2, 3 }, { 4, 5 }, { 6, 7 },
+ { 8, 9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
+};
+
+static const struct osmo_conv_code gsm_conv_sch = {
+ .N = 2,
+ .K = 5,
+ .len = GSM_SCH_UNCODED_LEN,
+ .next_output = sch_next_output,
+ .next_state = sch_next_state,
+};
+
+#define GSM_MAX_BURST_LEN 157 * 4
+#define GSM_SYM_RATE (1625e3 / 6) * 4
+
+/* Pre-generated FCCH measurement tone */
+static complex float fcch_ref[GSM_MAX_BURST_LEN];
+
+int float_to_sbit(const float *in, sbit_t *out, float scale, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++) {
+ out[i] = (in[i] - 0.5f) * scale;
+ }
+
+ return 0;
+}
+
+/* Check if FN contains a FCCH burst */
+int gsm_fcch_check_fn(int fn)
+{
+ int fn51 = fn % 51;
+
+ switch (fn51) {
+ case 0:
+ case 10:
+ case 20:
+ case 30:
+ case 40:
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Check if FN contains a SCH burst */
+int gsm_sch_check_fn(int fn)
+{
+ int fn51 = fn % 51;
+
+ switch (fn51) {
+ case 1:
+ case 11:
+ case 21:
+ case 31:
+ case 41:
+ return 1;
+ }
+
+ return 0;
+}
+
+int gsm_fcch_check_ts(int ts, int fn) {
+ return ts == 0 && gsm_fcch_check_fn(fn);
+}
+
+int gsm_sch_check_ts(int ts, int fn) {
+ return ts == 0 && gsm_sch_check_fn(fn);
+}
+
+/* SCH (T1, T2, T3p) to full FN value */
+int gsm_sch_to_fn(struct sch_info *sch)
+{
+ int t1 = sch->t1;
+ int t2 = sch->t2;
+ int t3p = sch->t3p;
+
+ if ((t1 < 0) || (t2 < 0) || (t3p < 0))
+ return -1;
+ int tt;
+ int t3 = t3p * 10 + 1;
+
+ if (t3 < t2)
+ tt = (t3 + 26) - t2;
+ else
+ tt = (t3 - t2) % 26;
+
+ return t1 * 51 * 26 + tt * 51 + t3;
+}
+
+/* Parse encoded SCH message */
+int gsm_sch_parse(const uint8_t *info, struct sch_info *desc)
+{
+ struct sch_packed_info *p = (struct sch_packed_info *) info;
+
+ desc->bsic = (p->bsic[0] << 0) | (p->bsic[1] << 1) |
+ (p->bsic[2] << 2) | (p->bsic[3] << 3) |
+ (p->bsic[4] << 4) | (p->bsic[5] << 5);
+
+ desc->t1 = (p->t1_lo[0] << 0) | (p->t1_md[0] << 1) |
+ (p->t1_md[1] << 2) | (p->t1_md[2] << 3) |
+ (p->t1_md[3] << 4) | (p->t1_md[4] << 5) |
+ (p->t1_md[5] << 6) | (p->t1_md[6] << 7) |
+ (p->t1_md[7] << 8) | (p->t1_hi[0] << 9) |
+ (p->t1_hi[1] << 10);
+
+ desc->t2 = (p->t2[0] << 0) | (p->t2[1] << 1) |
+ (p->t2[2] << 2) | (p->t2[3] << 3) |
+ (p->t2[4] << 4);
+
+ desc->t3p = (p->t3p_lo[0] << 0) | (p->t3p_hi[0] << 1) |
+ (p->t3p_hi[1] << 2);
+
+ return 0;
+}
+
+/* From osmo-bts */
+int gsm_sch_decode(uint8_t *info, sbit_t *data)
+{
+ int rc;
+ ubit_t uncoded[GSM_SCH_UNCODED_LEN];
+
+ osmo_conv_decode(&gsm_conv_sch, data, uncoded);
+
+ rc = osmo_crc16gen_check_bits(&gsm0503_sch_crc10,
+ uncoded, GSM_SCH_INFO_LEN,
+ uncoded + GSM_SCH_INFO_LEN);
+ if (rc)
+ return -1;
+
+ memcpy(info, uncoded, GSM_SCH_INFO_LEN * sizeof(ubit_t));
+
+ return 0;
+}
+
+#define FCCH_TAIL_BITS_LEN 3*4
+#define FCCH_DATA_LEN 100*4// 142
+#if 1
+/* Compute FCCH frequency offset */
+double org_gsm_fcch_offset(float *burst, int len)
+{
+ int i, start, end;
+ float a, b, c, d, ang, avg = 0.0f;
+ double freq;
+
+ if (len > GSM_MAX_BURST_LEN)
+ len = GSM_MAX_BURST_LEN;
+
+ for (i = 0; i < len; i++) {
+ a = burst[2 * i + 0];
+ b = burst[2 * i + 1];
+ c = crealf(fcch_ref[i]);
+ d = cimagf(fcch_ref[i]);
+
+ burst[2 * i + 0] = a * c - b * d;
+ burst[2 * i + 1] = a * d + b * c;
+ }
+
+ start = FCCH_TAIL_BITS_LEN;
+ end = start + FCCH_DATA_LEN;
+
+ for (i = start; i < end; i++) {
+ a = cargf(burst[2 * (i - 1) + 0] +
+ burst[2 * (i - 1) + 1] * I);
+ b = cargf(burst[2 * i + 0] +
+ burst[2 * i + 1] * I);
+
+ ang = b - a;
+
+ if (ang > M_PI)
+ ang -= 2 * M_PI;
+ else if (ang < -M_PI)
+ ang += 2 * M_PI;
+
+ avg += ang;
+ }
+
+ avg /= (float) (end - start);
+ freq = avg / (2 * M_PI) * GSM_SYM_RATE;
+
+ return freq;
+}
+
+
+static const int L1 = 3;
+static const int L2 = 32;
+static const int N1 = 92;
+static const int N2 = 92;
+
+static struct { int8_t r; int8_t s; } P_inv_table[3+32];
+
+void pinv(int P, int8_t* r, int8_t* s, int L1, int L2) {
+ for (int i = 0; i < L1; i++)
+ for (int j = 0; j < L2; j++)
+ if (P == L2 * i - L1 * j) {
+ *r = i;
+ *s = j;
+ return;
+ }
+}
+
+
+float ac_sum_with_lag( complex float* in, int lag, int offset, int N) {
+ complex float v = 0 + 0*I;
+ int total_offset = offset + lag;
+ for (int s = 0; s < N; s++)
+ v += in[s + total_offset] * conjf(in[s + total_offset - lag]);
+ return cargf(v);
+}
+
+
+double gsm_fcch_offset(float *burst, int len)
+{
+ int start;
+
+ const float fs = 13. / 48. * 1e6 * 4;
+ const float expected_fcch_val = ((2 * M_PI) / (fs)) * 67700;
+
+ if (len > GSM_MAX_BURST_LEN)
+ len = GSM_MAX_BURST_LEN;
+
+ start = FCCH_TAIL_BITS_LEN+10 * 4;
+ float alpha_one = ac_sum_with_lag((complex float*)burst, L1, start, N1);
+ float alpha_two = ac_sum_with_lag((complex float*)burst, L2, start, N2);
+
+ float P_unrounded = (L1 * alpha_two - L2 * alpha_one) / (2 * M_PI);
+ int P = roundf(P_unrounded);
+
+ int8_t r = 0, s = 0;
+ pinv(P, &r, &s, L1, L2);
+
+ float omegal1 = (alpha_one + 2 * M_PI * r) / L1;
+ float omegal2 = (alpha_two + 2 * M_PI * s) / L2;
+
+ float rv = org_gsm_fcch_offset(burst, len);
+ //return rv;
+
+ float reval = GSM_SYM_RATE / (2 * M_PI) * (expected_fcch_val - (omegal1+omegal2)/2);
+ //fprintf(stderr, "XX rv %f %f %f %f\n", rv, reval, omegal1 / (2 * M_PI) * fs, omegal2 / (2 * M_PI) * fs);
+
+ //fprintf(stderr, "XX rv %f %f\n", rv, reval);
+
+ return -reval;
+}
+#endif
+/* Generate FCCH measurement tone */
+static __attribute__((constructor)) void init()
+{
+ int i;
+ double freq = 0.25;
+
+ for (i = 0; i < GSM_MAX_BURST_LEN; i++) {
+ fcch_ref[i] = sin(2 * M_PI * freq * (double) i) +
+ cos(2 * M_PI * freq * (double) i) * I;
+ }
+
+}
+
+#pragma GCC diagnostic pop
diff --git a/Transceiver52M/ms/sch.h b/Transceiver52M/ms/sch.h
new file mode 100644
index 0000000..ea8ba85
--- /dev/null
+++ b/Transceiver52M/ms/sch.h
@@ -0,0 +1,48 @@
+#pragma once
+/*
+ * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
+ * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
+ * (C) 2016 by Tom Tsou <tom.tsou@ettus.com>
+ * (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>
+ *
+ * All Rights Reserved
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ *
+ * 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
+ * the Free Software Foundation; either version 2 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 General Public License for more details.
+ */
+
+#include <osmocom/core/bits.h>
+
+struct sch_info {
+ int bsic;
+ int t1;
+ int t2;
+ int t3p;
+};
+
+#define GSM_SCH_INFO_LEN 25
+#define GSM_SCH_UNCODED_LEN 35
+#define GSM_SCH_CODED_LEN 78
+
+int gsm_sch_decode(uint8_t *sb_info, sbit_t *burst);
+int gsm_sch_parse(const uint8_t *sb_info, struct sch_info *desc);
+int gsm_sch_to_fn(struct sch_info *sch);
+int gsm_sch_check_fn(int fn);
+int gsm_fcch_check_fn(int fn);
+int gsm_fcch_check_ts(int ts, int fn);
+int gsm_sch_check_ts(int ts, int fn);
+
+double gsm_fcch_offset(float *burst, int len);
+
+int float_to_sbit(const float *in, sbit_t *out, float scale, int len);
+
diff --git a/Transceiver52M/ms/threadpool.h b/Transceiver52M/ms/threadpool.h
new file mode 100644
index 0000000..4b1eefd
--- /dev/null
+++ b/Transceiver52M/ms/threadpool.h
@@ -0,0 +1,95 @@
+#pragma once
+/*
+ * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Eric Wild <ewild@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <atomic>
+#include <vector>
+#include <future>
+#include <mutex>
+#include <queue>
+#include "threadsched.h"
+
+struct single_thread_pool {
+ std::mutex m;
+ std::condition_variable cv;
+ std::atomic<bool> stop_flag;
+ std::atomic<bool> is_ready;
+ std::deque<std::function<void()>> wq;
+ pthread_t worker_thread;
+
+ template <class F>
+ void add_task(F &&f)
+ {
+ std::unique_lock<std::mutex> l(m);
+ wq.emplace_back(std::forward<F>(f));
+ cv.notify_one();
+ return;
+ }
+
+ single_thread_pool() : stop_flag(false), is_ready(false)
+ {
+ worker_thread = spawn_worker_thread(
+ sched_params::thread_names::SCH_SEARCH,
+ [](void *args) -> void * {
+ using thist = decltype(this);
+ thist t = reinterpret_cast<thist>(args);
+ t->thread_loop();
+ return 0;
+ },
+ this);
+ }
+ ~single_thread_pool()
+ {
+ stop();
+ }
+
+ private:
+ void stop()
+ {
+ {
+ std::unique_lock<std::mutex> l(m);
+ wq.clear();
+ stop_flag = true;
+ cv.notify_one();
+ }
+ pthread_join(worker_thread, nullptr);
+ }
+
+ void thread_loop()
+ {
+ while (true) {
+ is_ready = true;
+ std::function<void()> f;
+ {
+ std::unique_lock<std::mutex> l(m);
+ if (wq.empty()) {
+ cv.wait(l, [&] { return !wq.empty() || stop_flag; });
+ }
+ if (stop_flag)
+ return;
+ is_ready = false;
+ f = std::move(wq.front());
+ wq.pop_front();
+ }
+ f();
+ }
+ }
+}; \ No newline at end of file
diff --git a/Transceiver52M/ms/threadsched.cpp b/Transceiver52M/ms/threadsched.cpp
new file mode 100644
index 0000000..ba5cae7
--- /dev/null
+++ b/Transceiver52M/ms/threadsched.cpp
@@ -0,0 +1,104 @@
+/*
+ * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Eric Wild <ewild@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <cerrno>
+#include <cstdlib>
+#include <cstring>
+#include <iostream>
+#include <thread>
+
+extern "C" {
+#include <pthread.h>
+}
+
+#include "threadsched.h"
+
+sched_params::target scheduling_target;
+
+void set_sched_target(sched_params::target t)
+{
+ scheduling_target = t;
+}
+
+void set_name_aff_sched(std::thread::native_handle_type h, const char *name, int cpunum, int schedtype, int prio)
+{
+ pthread_setname_np(h, name);
+
+ cpu_set_t cpuset;
+
+ CPU_ZERO(&cpuset);
+ CPU_SET(cpunum, &cpuset);
+
+ if (pthread_setaffinity_np(h, sizeof(cpuset), &cpuset) < 0) {
+ std::cerr << name << " affinity: errreur! " << std::strerror(errno);
+ return exit(0);
+ }
+
+ sched_param sch_params;
+ sch_params.sched_priority = prio;
+ if (pthread_setschedparam(h, schedtype, &sch_params) < 0) {
+ std::cerr << name << " sched: errreur! " << std::strerror(errno);
+ return exit(0);
+ }
+}
+
+static pthread_t do_spawn_thr(const char *name, int cpunum, int schedtype, int prio, worker_func_sig fun, void *arg)
+{
+ pthread_t thread;
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+
+ sched_param sch_params;
+ sch_params.sched_priority = prio;
+ cpu_set_t cpuset;
+ CPU_ZERO(&cpuset);
+ CPU_SET(cpunum, &cpuset);
+ auto a = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
+ a |= pthread_attr_setschedpolicy(&attr, schedtype);
+ a |= pthread_attr_setschedparam(&attr, &sch_params);
+ a |= pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
+ if (a)
+ std::cerr << "thread arg rc:" << a << std::endl;
+ pthread_create(&thread, &attr, fun, arg);
+ pthread_setname_np(thread, name);
+ pthread_attr_destroy(&attr);
+ return thread;
+}
+
+void set_name_aff_sched(std::thread::native_handle_type h, sched_params::thread_names name)
+{
+ auto tgt = schdp[scheduling_target][name];
+ // std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << std::endl;
+ set_name_aff_sched(h, tgt.name, tgt.core, tgt.schedtype, tgt.prio);
+}
+
+void set_name_aff_sched(sched_params::thread_names name)
+{
+ set_name_aff_sched(pthread_self(), name);
+}
+
+pthread_t spawn_worker_thread(sched_params::thread_names name, worker_func_sig fun, void *arg)
+{
+ auto tgt = schdp[scheduling_target][name];
+ // std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << " prio:" << tgt.prio << std::endl;
+ return do_spawn_thr(tgt.name, tgt.core, tgt.schedtype, tgt.prio, fun, arg);
+}
diff --git a/Transceiver52M/ms/threadsched.h b/Transceiver52M/ms/threadsched.h
new file mode 100644
index 0000000..7cc9176
--- /dev/null
+++ b/Transceiver52M/ms/threadsched.h
@@ -0,0 +1,68 @@
+#pragma once
+/*
+ * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Eric Wild <ewild@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+extern "C" {
+#include <pthread.h>
+#include <sched.h>
+}
+
+static struct sched_params {
+ enum thread_names { U_CTL = 0, U_RX, U_TX, SCH_SEARCH, MAIN, LEAKCHECK, RXRUN, TXRUN, _THRD_NAME_COUNT };
+ enum target { ODROID = 0, PI4 };
+ const char *name;
+ int core;
+ int schedtype;
+ int prio;
+} schdp[][sched_params::_THRD_NAME_COUNT]{
+ {
+ { "upper_ctrl", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) },
+ { "upper_rx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
+ { "upper_tx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 1 },
+
+ { "sch_search", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
+ { "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
+ { "leakcheck", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
+
+ { "rxrun", 4, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
+ { "txrun", 5, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
+ },
+ {
+ { "upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) },
+ { "upper_rx", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
+ { "upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
+
+ { "sch_search", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
+ { "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
+ { "leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
+
+ { "rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
+ { "txrun", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
+ },
+};
+
+void set_sched_target(sched_params::target t);
+
+using worker_func_sig = void *(*)(void *);
+
+void set_name_aff_sched(sched_params::thread_names name);
+
+pthread_t spawn_worker_thread(sched_params::thread_names name, worker_func_sig fun, void *arg);
diff --git a/Transceiver52M/ms/uhd_specific.h b/Transceiver52M/ms/uhd_specific.h
new file mode 100644
index 0000000..151c002
--- /dev/null
+++ b/Transceiver52M/ms/uhd_specific.h
@@ -0,0 +1,279 @@
+#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 <uhd/version.hpp>
+#include <uhd/usrp/multi_usrp.hpp>
+#include <uhd/types/metadata.hpp>
+#include <complex>
+#include <cstring>
+#include <iostream>
+#include <thread>
+
+#include <Timeval.h>
+#include <vector>
+
+using blade_sample_type = std::complex<int16_t>;
+const int SAMPLE_SCALE_FACTOR = 1;
+
+struct uhd_buf_wrap {
+ double rxticks;
+ size_t num_samps;
+ uhd::rx_metadata_t *md;
+ blade_sample_type *buf;
+ auto actual_samples_per_buffer()
+ {
+ return num_samps;
+ }
+ long get_first_ts()
+ {
+ return 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 uhd_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;
+ static std::atomic<bool> stop_lower_threads_flag;
+ double rxfreq_cache, txfreq_cache;
+
+ virtual ~uhd_hw()
+ {
+ delete[] one_pkt_buf;
+ }
+ uhd_hw() : rxFullScale(32767), txFullScale(32767 * 0.3), rxtxdelay(-67), rxfreq_cache(0), txfreq_cache(0)
+ {
+ }
+
+ void close_device()
+ {
+ }
+
+ bool tuneTx(double freq, size_t chan = 0)
+ {
+ if (txfreq_cache == freq)
+ return true;
+ msleep(25);
+ dev->set_tx_freq(freq, chan);
+ txfreq_cache = freq;
+ msleep(25);
+ return true;
+ };
+ bool tuneRx(double freq, size_t chan = 0)
+ {
+ if (rxfreq_cache == freq)
+ return true;
+ msleep(25);
+ dev->set_rx_freq(freq, chan);
+ rxfreq_cache = freq;
+ 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)
+ {
+ auto const lock_delay_ms = 500;
+ auto clock_lock_attempts = 15; // x lock_delay_ms
+ auto const mcr = 26e6;
+ auto const rate = (1625e3 / 6) * 4;
+ auto const ref = "external";
+ auto const gain = 35;
+ auto const freq = 931.4e6; // 936.8e6
+ auto bw = 0.5e6;
+ auto const channel = 0;
+ // aligned to blade: 1020 samples per transfer
+ std::string args = { "recv_frame_size=4092,send_frame_size=4092" };
+
+ dev = uhd::usrp::multi_usrp::make(args);
+ std::cout << "Using Device: " << dev->get_pp_string() << std::endl;
+ dev->set_clock_source(ref);
+ dev->set_master_clock_rate(mcr);
+ dev->set_rx_rate(rate, channel);
+ dev->set_tx_rate(rate, channel);
+ uhd::tune_request_t tune_request(freq, 0);
+ dev->set_rx_freq(tune_request, channel);
+ dev->set_rx_gain(gain, channel);
+ dev->set_tx_gain(60, channel);
+ dev->set_rx_bandwidth(bw, channel);
+ dev->set_tx_bandwidth(bw, channel);
+
+ while (!(dev->get_rx_sensor("lo_locked", channel).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));
+ 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");
+ rx_stream = dev->get_rx_stream(stream_args);
+ uhd::stream_args_t stream_args2("sc16", "sc16");
+ tx_stream = dev->get_tx_stream(stream_args2);
+
+ rx_spp = rx_stream->get_max_num_samps();
+ rxticks = dev->get_rx_rate();
+ assert(rxticks == dev->get_tx_rate());
+ one_pkt_buf = new blade_sample_type[rx_spp];
+ pkt_ptrs = { 1, &one_pkt_buf[0] };
+ return 0;
+ }
+
+ void actually_enable_streams()
+ {
+ // nop: stream cmd in handler
+ }
+
+ void *rx_cb(bh_fn_t burst_handler)
+ {
+ void *ret = nullptr;
+ static int to_skip = 0;
+
+ uhd::rx_metadata_t md;
+ auto num_rx_samps = rx_stream->recv(pkt_ptrs.front(), rx_spp, md, 1.0, true);
+
+ if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_TIMEOUT) {
+ std::cerr << boost::format("Timeout while streaming") << std::endl;
+ exit(0);
+ }
+ if (md.error_code == uhd::rx_metadata_t::ERROR_CODE_OVERFLOW) {
+ std::cerr << boost::format("Got an overflow indication\n") << std::endl;
+ exit(0);
+ }
+ if (md.error_code != uhd::rx_metadata_t::ERROR_CODE_NONE) {
+ std::cerr << str(boost::format("Receiver error: %s") % md.strerror());
+ exit(0);
+ }
+
+ dev_buf_t rcd = { rxticks, num_rx_samps, &md, &one_pkt_buf[0] };
+
+ if (to_skip < 120) // prevents weird overflows on startup
+ to_skip++;
+ else {
+ burst_handler(&rcd);
+ }
+
+ return ret;
+ }
+
+ auto get_rx_burst_handler_fn(bh_fn_t burst_handler)
+ {
+ // C cb -> ghetto closure capture, which is fine, the args never change.
+ static auto rx_burst_cap_this = this;
+ static auto rx_burst_cap_bh = burst_handler;
+ auto fn = [](void *args) -> void * {
+ pthread_setname_np(pthread_self(), "rxrun");
+
+ uhd::stream_cmd_t stream_cmd(uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS);
+ stream_cmd.stream_now = true;
+ stream_cmd.time_spec = uhd::time_spec_t();
+ rx_burst_cap_this->rx_stream->issue_stream_cmd(stream_cmd);
+
+ while (!rx_burst_cap_this->stop_lower_threads_flag) {
+ rx_burst_cap_this->rx_cb(rx_burst_cap_bh);
+ }
+ return 0;
+ };
+ return fn;
+ }
+ auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
+ {
+ auto fn = [](void *args) -> void * {
+ // dummy
+ return 0;
+ };
+ return fn;
+ }
+ void submit_burst_ts(blade_sample_type *buffer, int len, uint64_t ts)
+ {
+ uhd::tx_metadata_t m = {};
+ m.end_of_burst = true;
+ m.start_of_burst = true;
+ m.has_time_spec = true;
+ m.time_spec = m.time_spec.from_ticks(ts + rxtxdelay, rxticks); // uhd specific b210 delay!
+ std::vector<void *> ptrs(1, buffer);
+
+ tx_stream->send(ptrs, len, m, 1.0);
+#ifdef DBGXX
+ uhd::async_metadata_t async_md;
+ bool tx_ack = false;
+ while (!tx_ack && tx_stream->recv_async_msg(async_md)) {
+ tx_ack = (async_md.event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK);
+ }
+ std::cout << (tx_ack ? "yay" : "nay") << " " << async_md.time_spec.to_ticks(rxticks) << std::endl;
+#endif
+ }
+}; \ No newline at end of file
diff --git a/Transceiver52M/osmo-trx.cpp b/Transceiver52M/osmo-trx.cpp
index ba1c740..8f5cc85 100644
--- a/Transceiver52M/osmo-trx.cpp
+++ b/Transceiver52M/osmo-trx.cpp
@@ -12,10 +12,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifdef HAVE_CONFIG_H
@@ -81,6 +77,24 @@ static struct ctrl_handle *g_ctrlh;
static RadioDevice *usrp;
static RadioInterface *radio;
+/* adjusts read timestamp offset to make the viterbi equalizer happy by including the start tail bits */
+template <typename B>
+class rif_va_wrapper : public B {
+ bool use_va;
+
+ public:
+ template <typename... Args>
+ rif_va_wrapper(bool use_va, Args &&...args) : B(std::forward<Args>(args)...), use_va(use_va)
+ {
+ }
+ bool start() override
+ {
+ auto rv = B::start();
+ B::readTimestamp -= use_va ? 20 : 0;
+ return rv;
+ };
+};
+
/* Create radio interface
* The interface consists of sample rate changes, frequency shifts,
* channel multiplexing, and other conversions. The transceiver core
@@ -95,17 +109,17 @@ RadioInterface *makeRadioInterface(struct trx_ctx *trx,
switch (type) {
case RadioDevice::NORMAL:
- radio = new RadioInterface(usrp, trx->cfg.tx_sps,
- trx->cfg.rx_sps, trx->cfg.num_chans);
+ radio = new rif_va_wrapper<RadioInterface>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps,
+ trx->cfg.num_chans);
break;
case RadioDevice::RESAMP_64M:
case RadioDevice::RESAMP_100M:
- radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
- trx->cfg.rx_sps);
+ radio = new rif_va_wrapper<RadioInterfaceResamp>(trx->cfg.use_va, usrp, trx->cfg.tx_sps,
+ trx->cfg.rx_sps);
break;
case RadioDevice::MULTI_ARFCN:
- radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
- trx->cfg.rx_sps, trx->cfg.num_chans);
+ radio = new rif_va_wrapper<RadioInterfaceMulti>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps,
+ trx->cfg.num_chans);
break;
default:
LOG(ALERT) << "Unsupported radio interface configuration";
@@ -177,6 +191,17 @@ static void sig_handler(int signo)
gshutdown = true;
break;
case SIGABRT:
+ /* in case of abort, we want to obtain a talloc report and
+ * then run default SIGABRT handler, who will generate coredump
+ * and abort the process. abort() should do this for us after we
+ * return, but program wouldn't exit if an external SIGABRT is
+ * received.
+ */
+ talloc_report(tall_trx_ctx, stderr);
+ talloc_report_full(tall_trx_ctx, stderr);
+ signal(SIGABRT, SIG_DFL);
+ raise(SIGABRT);
+ break;
case SIGUSR1:
talloc_report(tall_trx_ctx, stderr);
talloc_report_full(tall_trx_ctx, stderr);
@@ -458,6 +483,12 @@ int trx_validate_config(struct trx_ctx *trx)
return -1;
}
+ if (trx->cfg.use_va &&
+ (trx->cfg.egprs || trx->cfg.multi_arfcn || trx->cfg.tx_sps != 4 || trx->cfg.rx_sps != 4)) {
+ LOG(ERROR) << "Viterbi equalizer only works for gmsk with 4 tx/rx samples per symbol!";
+ return -1;
+ }
+
return 0;
}
@@ -478,6 +509,38 @@ static int set_sched_rr(unsigned int prio)
return 0;
}
+static void print_simd_info(void)
+{
+#ifdef HAVE_SSE3
+ LOGP(DMAIN, LOGL_INFO, "SSE3 support compiled in");
+#ifdef HAVE___BUILTIN_CPU_SUPPORTS
+ if (__builtin_cpu_supports("sse3"))
+ LOGPC(DMAIN, LOGL_INFO, " and supported by CPU\n");
+ else
+ LOGPC(DMAIN, LOGL_INFO, ", but not supported by CPU\n");
+#else
+ LOGPC(DMAIN, LOGL_INFO, ", but runtime SIMD detection disabled\n");
+#endif
+#endif
+
+#ifdef HAVE_SSE4_1
+ LOGP(DMAIN, LOGL_INFO, "SSE4.1 support compiled in");
+#ifdef HAVE___BUILTIN_CPU_SUPPORTS
+ if (__builtin_cpu_supports("sse4.1"))
+ LOGPC(DMAIN, LOGL_INFO, " and supported by CPU\n");
+ else
+ LOGPC(DMAIN, LOGL_INFO, ", but not supported by CPU\n");
+#else
+ LOGPC(DMAIN, LOGL_INFO, ", but runtime SIMD detection disabled\n");
+#endif
+#endif
+
+#ifndef HAVE_ATOMIC_OPS
+#pragma message ("Built without atomic operation support. Using Mutex, it may affect performance!")
+ LOG(NOTICE) << "Built without atomic operation support. Using Mutex, it may affect performance!";
+#endif
+}
+
static void print_config(struct trx_ctx *trx)
{
unsigned int i;
@@ -499,7 +562,9 @@ static void print_config(struct trx_ctx *trx)
ost << " Filler Burst TSC........ " << trx->cfg.rtsc << std::endl;
ost << " Filler Burst RACH Delay. " << trx->cfg.rach_delay << std::endl;
ost << " Multi-Carrier........... " << trx->cfg.multi_arfcn << std::endl;
- ost << " Tuning offset........... " << trx->cfg.offset << std::endl;
+ ost << " LO freq. offset......... " << trx->cfg.offset << std::endl;
+ if (trx->cfg.freq_offset_khz != 0)
+ ost << " Tune freq. offset....... " << trx->cfg.freq_offset_khz << std::endl;
ost << " RSSI to dBm offset...... " << trx->cfg.rssi_offset << (trx->cfg.force_rssi_offset ? "" : " (relative)") << std::endl;
ost << " Swap channels........... " << trx->cfg.swap_channels << std::endl;
ost << " Tx Antennas.............";
@@ -530,24 +595,14 @@ static void trx_stop()
static int trx_start(struct trx_ctx *trx)
{
int type, chans;
- unsigned int i;
- std::vector<std::string> rx_paths, tx_paths;
RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
/* Create the low level device object */
if (trx->cfg.multi_arfcn)
iface = RadioDevice::MULTI_ARFCN;
- /* Generate vector of rx/tx_path: */
- for (i = 0; i < trx->cfg.num_chans; i++) {
- rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path));
- tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path));
- }
-
- usrp = RadioDevice::make(trx->cfg.tx_sps, trx->cfg.rx_sps, iface,
- trx->cfg.num_chans, trx->cfg.offset,
- tx_paths, rx_paths);
- type = usrp->open(charp2str(trx->cfg.dev_args), trx->cfg.clock_ref, trx->cfg.swap_channels);
+ usrp = RadioDevice::make(iface, &trx->cfg);
+ type = usrp->open();
if (type < 0) {
LOG(ALERT) << "Failed to create radio device" << std::endl;
goto shutdown;
@@ -585,35 +640,6 @@ int main(int argc, char *argv[])
g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx);
-#ifdef HAVE_SSE3
- printf("Info: SSE3 support compiled in");
-#ifdef HAVE___BUILTIN_CPU_SUPPORTS
- if (__builtin_cpu_supports("sse3"))
- printf(" and supported by CPU\n");
- else
- printf(", but not supported by CPU\n");
-#else
- printf(", but runtime SIMD detection disabled\n");
-#endif
-#endif
-
-#ifdef HAVE_SSE4_1
- printf("Info: SSE4.1 support compiled in");
-#ifdef HAVE___BUILTIN_CPU_SUPPORTS
- if (__builtin_cpu_supports("sse4.1"))
- printf(" and supported by CPU\n");
- else
- printf(", but not supported by CPU\n");
-#else
- printf(", but runtime SIMD detection disabled\n");
-#endif
-#endif
-
-#ifndef HAVE_ATOMIC_OPS
-#pragma message ("Built without atomic operation support. Using Mutex, it may affect performance!")
- printf("Built without atomic operation support. Using Mutex, it may affect performance!\n");
-#endif
-
convolve_init();
convert_init();
@@ -639,11 +665,11 @@ int main(int argc, char *argv[])
exit(2);
}
- rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX);
+ rc = telnet_init_default(tall_trx_ctx, NULL, OSMO_VTY_PORT_TRX);
if (rc < 0)
exit(1);
- g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_TRX, NULL);
+ g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL);
if (!g_ctrlh) {
LOG(ERROR) << "Failed to create CTRL interface.\n";
exit(1);
@@ -660,6 +686,7 @@ int main(int argc, char *argv[])
" but expect your config to break in the future.";
}
+ print_simd_info();
print_config(g_trx_ctx);
if (trx_validate_config(g_trx_ctx) < 0) {
diff --git a/Transceiver52M/proto_trxd.h b/Transceiver52M/proto_trxd.h
index d72cfdd..c250a74 100644
--- a/Transceiver52M/proto_trxd.h
+++ b/Transceiver52M/proto_trxd.h
@@ -48,9 +48,8 @@ struct trxd_hdr_common {
reserved:1,
version:4;
#elif OSMO_IS_BIG_ENDIAN
- uint8_t version:4,
- reserved:1,
- tn:3;
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t version:4, reserved:1, tn:3;
#endif
uint32_t fn; /* big endian */
} __attribute__ ((packed));
@@ -86,9 +85,8 @@ struct trxd_hdr_v1_specific {
modulation:4,
idle:1;
#elif OSMO_IS_BIG_ENDIAN
- uint8_t idle:1,
- modulation:4,
- tsc:3;
+/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
+ uint8_t idle:1, modulation:4, tsc:3;
#endif
int16_t ci; /* big endian, in centiBels */
} __attribute__ ((packed));
diff --git a/Transceiver52M/radioBuffer.cpp b/Transceiver52M/radioBuffer.cpp
index 62f6553..ec868e5 100644
--- a/Transceiver52M/radioBuffer.cpp
+++ b/Transceiver52M/radioBuffer.cpp
@@ -28,7 +28,7 @@
RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen,
size_t hLen, bool outDirection)
- : writeIndex(0), readIndex(0), availSamples(0)
+ : writeIndex(0), readIndex(0), availSamples(0), segments(numSegments)
{
if (!outDirection)
hLen = 0;
@@ -36,7 +36,6 @@ RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen,
buffer = new float[2 * (hLen + numSegments * segmentLen)];
bufferLen = numSegments * segmentLen;
- segments.resize(numSegments);
for (size_t i = 0; i < numSegments; i++)
segments[i] = &buffer[2 * (hLen + i * segmentLen)];
diff --git a/Transceiver52M/radioInterface.cpp b/Transceiver52M/radioInterface.cpp
index 875245d..311af34 100644
--- a/Transceiver52M/radioInterface.cpp
+++ b/Transceiver52M/radioInterface.cpp
@@ -39,9 +39,10 @@ extern "C" {
RadioInterface::RadioInterface(RadioDevice *wDevice, size_t tx_sps,
size_t rx_sps, size_t chans,
int wReceiveOffset, GSM::Time wStartTime)
- : mDevice(wDevice), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
- underrun(false), overrun(false), writeTimestamp(0), readTimestamp(0),
- receiveOffset(wReceiveOffset), mOn(false)
+ : mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mReceiveFIFO(mChans), mDevice(wDevice),
+ sendBuffer(mChans), recvBuffer(mChans), convertRecvBuffer(mChans),
+ convertSendBuffer(mChans), powerScaling(mChans), underrun(false), overrun(false),
+ writeTimestamp(0), readTimestamp(0), receiveOffset(wReceiveOffset), mOn(false)
{
mClock.set(wStartTime);
}
@@ -58,15 +59,6 @@ bool RadioInterface::init(int type)
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++) {
sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
diff --git a/Transceiver52M/radioInterface.h b/Transceiver52M/radioInterface.h
index efe5606..b05af78 100644
--- a/Transceiver52M/radioInterface.h
+++ b/Transceiver52M/radioInterface.h
@@ -31,6 +31,9 @@ static const unsigned gSlotLen = 148; ///< number of symbols per slot, not
class RadioInterface {
protected:
+ size_t mSPSTx;
+ size_t mSPSRx;
+ size_t mChans;
Thread mAlignRadioServiceLoopThread; ///< thread that synchronizes transmit and receive sections
@@ -38,10 +41,6 @@ protected:
RadioDevice *mDevice; ///< the USRP object
- size_t mSPSTx;
- size_t mSPSRx;
- size_t mChans;
-
std::vector<RadioBuffer *> sendBuffer;
std::vector<RadioBuffer *> recvBuffer;
@@ -76,7 +75,7 @@ private:
public:
/** start the interface */
- bool start();
+ virtual bool start();
bool stop();
/** initialization */
@@ -152,7 +151,7 @@ private:
public:
RadioInterfaceResamp(RadioDevice* wDevice, size_t tx_sps, size_t rx_sps);
- ~RadioInterfaceResamp();
+ virtual ~RadioInterfaceResamp();
bool init(int type);
void close();
@@ -185,7 +184,7 @@ private:
public:
RadioInterfaceMulti(RadioDevice* radio, size_t tx_sps,
size_t rx_sps, size_t chans = 1);
- ~RadioInterfaceMulti();
+ virtual ~RadioInterfaceMulti();
bool init(int type);
void close();
diff --git a/Transceiver52M/radioInterfaceMulti.cpp b/Transceiver52M/radioInterfaceMulti.cpp
index 465fd41..a7195b4 100644
--- a/Transceiver52M/radioInterfaceMulti.cpp
+++ b/Transceiver52M/radioInterfaceMulti.cpp
@@ -44,8 +44,9 @@ extern "C" {
RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps,
size_t rx_sps, size_t chans)
: RadioInterface(radio, tx_sps, rx_sps, chans),
- outerSendBuffer(NULL), outerRecvBuffer(NULL),
- dnsampler(NULL), upsampler(NULL), channelizer(NULL), synthesis(NULL)
+ outerSendBuffer(NULL), outerRecvBuffer(NULL), history(mChans), active(MCHANS, false),
+ rx_freq_state(mChans), tx_freq_state(mChans), dnsampler(NULL), upsampler(NULL), channelizer(NULL),
+ synthesis(NULL)
{
}
@@ -70,12 +71,16 @@ void RadioInterfaceMulti::close()
channelizer = NULL;
synthesis = NULL;
- mReceiveFIFO.resize(0);
- powerScaling.resize(0);
- history.resize(0);
- active.resize(0);
- rx_freq_state.resize(0);
- tx_freq_state.resize(0);
+
+ for (std::vector<signalVector*>::iterator it = history.begin(); it != history.end(); ++it)
+ delete *it;
+
+ mReceiveFIFO.clear();
+ powerScaling.clear();
+ history.clear();
+ active.clear();
+ rx_freq_state.clear();
+ tx_freq_state.clear();
RadioInterface::close();
}
@@ -148,20 +153,9 @@ bool RadioInterfaceMulti::init(int type)
return false;
}
- close();
-
- sendBuffer.resize(mChans);
- recvBuffer.resize(mChans);
convertSendBuffer.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 */
inchunk = RESAMP_INRATE * 4;
outchunk = RESAMP_OUTRATE * 4;
diff --git a/Transceiver52M/radioInterfaceResamp.cpp b/Transceiver52M/radioInterfaceResamp.cpp
index b92432f..869acd0 100644
--- a/Transceiver52M/radioInterfaceResamp.cpp
+++ b/Transceiver52M/radioInterfaceResamp.cpp
@@ -98,15 +98,6 @@ bool RadioInterfaceResamp::init(int type)
{
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) {
case RadioDevice::RESAMP_64M:
resamp_inrate = RESAMP_64M_INRATE;
diff --git a/Transceiver52M/radioVector.cpp b/Transceiver52M/radioVector.cpp
index 68e42c5..acefc97 100644
--- a/Transceiver52M/radioVector.cpp
+++ b/Transceiver52M/radioVector.cpp
@@ -76,22 +76,25 @@ bool radioVector::setVector(signalVector *vector, size_t chan)
return true;
}
-noiseVector::noiseVector(size_t size)
+avgVector::avgVector(size_t size)
: std::vector<float>(size), itr(0)
{
}
-float noiseVector::avg() const
+float avgVector::avg() const
{
float val = 0.0;
+ if (!size())
+ return 0.0f;
+
for (size_t i = 0; i < size(); i++)
val += (*this)[i];
return val / (float) size();
}
-bool noiseVector::insert(float val)
+bool avgVector::insert(float val)
{
if (!size())
return false;
diff --git a/Transceiver52M/radioVector.h b/Transceiver52M/radioVector.h
index 84e3987..90db626 100644
--- a/Transceiver52M/radioVector.h
+++ b/Transceiver52M/radioVector.h
@@ -48,9 +48,9 @@ private:
GSM::Time mTime;
};
-class noiseVector : std::vector<float> {
+class avgVector : std::vector<float> {
public:
- noiseVector(size_t size = 0);
+ avgVector(size_t size = 0);
bool insert(float val);
float avg() const;
diff --git a/Transceiver52M/sigProcLib.cpp b/Transceiver52M/sigProcLib.cpp
index 04f7e30..5fac365 100644
--- a/Transceiver52M/sigProcLib.cpp
+++ b/Transceiver52M/sigProcLib.cpp
@@ -34,6 +34,7 @@
#include "Resampler.h"
extern "C" {
+#include <osmocom/core/panic.h>
#include "convolve.h"
#include "scale.h"
#include "mult.h"
@@ -128,6 +129,8 @@ struct PulseSequence {
static CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
static CorrelationSequence *gEdgeMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL};
static CorrelationSequence *gRACHSequences[] = {NULL,NULL,NULL};
+static CorrelationSequence *gSCHSequence = NULL;
+static CorrelationSequence *gDummySequence = NULL;
static PulseSequence *GSMPulse1 = NULL;
static PulseSequence *GSMPulse4 = NULL;
@@ -150,6 +153,12 @@ void sigProcLibDestroy()
gRACHSequences[i] = NULL;
}
+ delete gSCHSequence;
+ gSCHSequence = NULL;
+
+ delete gDummySequence;
+ gDummySequence = NULL;
+
delete GMSKRotation1;
delete GMSKReverseRotation1;
delete GMSKRotation4;
@@ -314,6 +323,7 @@ static signalVector *convolve(const signalVector *x, const signalVector *h,
append = true;
break;
case CUSTOM:
+ // FIXME: x->getstart?
if (start < h->size() - 1) {
head = h->size() - start;
append = true;
@@ -1288,6 +1298,77 @@ release:
return status;
}
+static bool generateDummyMidamble(int sps)
+{
+ bool status = true;
+ float toa;
+ complex *data = NULL;
+ signalVector *autocorr = NULL, *midamble = NULL;
+ signalVector *midMidamble = NULL, *_midMidamble = NULL;
+
+ delete gDummySequence;
+
+ /* Use middle 16 bits of each TSC. Correlation sequence is not pulse shaped */
+ midMidamble = modulateBurst(gDummyBurstTSC.segment(5,16), 0, sps, true);
+ if (!midMidamble)
+ return false;
+
+ /* Simulated receive sequence is pulse shaped */
+ midamble = modulateBurst(gDummyBurstTSC, 0, sps, false);
+ if (!midamble) {
+ status = false;
+ goto release;
+ }
+
+ // NOTE: Because ideal TSC 16-bit midamble is 66 symbols into burst,
+ // the ideal TSC has an + 180 degree phase shift,
+ // due to the pi/2 frequency shift, that
+ // needs to be accounted for.
+ // 26-midamble is 61 symbols into burst, has +90 degree phase shift.
+ scaleVector(*midMidamble, complex(-1.0, 0.0));
+ scaleVector(*midamble, complex(0.0, 1.0));
+
+ conjugateVector(*midMidamble);
+
+ /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
+ data = (complex *) convolve_h_alloc(midMidamble->size());
+ _midMidamble = new signalVector(data, 0, midMidamble->size(), convolve_h_alloc, free);
+ _midMidamble->setAligned(true);
+ midMidamble->copyTo(*_midMidamble);
+
+ autocorr = convolve(midamble, _midMidamble, NULL, NO_DELAY);
+ if (!autocorr) {
+ status = false;
+ goto release;
+ }
+
+ gDummySequence = new CorrelationSequence;
+ gDummySequence->sequence = _midMidamble;
+ gDummySequence->gain = peakDetect(*autocorr, &toa, NULL);
+
+ /* For 1 sps only
+ * (Half of correlation length - 1) + midpoint of pulse shape + remainder
+ * 13.5 = (16 / 2 - 1) + 1.5 + (26 - 10) / 2
+ */
+ if (sps == 1)
+ gDummySequence->toa = toa - 13.5;
+ else
+ gDummySequence->toa = 0;
+
+release:
+ delete autocorr;
+ delete midamble;
+ delete midMidamble;
+
+ if (!status) {
+ delete _midMidamble;
+ free(data);
+ gDummySequence = NULL;
+ }
+
+ return status;
+}
+
static CorrelationSequence *generateEdgeMidamble(int tsc)
{
complex *data = NULL;
@@ -1383,6 +1464,69 @@ release:
return status;
}
+bool generateSCHSequence(int sps)
+{
+ bool status = true;
+ float toa;
+ complex *data = NULL;
+ signalVector *autocorr = NULL;
+ signalVector *seq0 = NULL, *seq1 = NULL, *_seq1 = NULL;
+
+ delete gSCHSequence;
+
+ seq0 = modulateBurst(gSCHSynchSequence, 0, sps, false);
+ if (!seq0)
+ return false;
+
+ seq1 = modulateBurst(gSCHSynchSequence, 0, sps, true);
+ if (!seq1) {
+ status = false;
+ goto release;
+ }
+
+ conjugateVector(*seq1);
+
+ /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */
+ data = (complex *) convolve_h_alloc(seq1->size());
+ _seq1 = new signalVector(data, 0, seq1->size(), convolve_h_alloc, free);
+ _seq1->setAligned(true);
+ seq1->copyTo(*_seq1);
+
+ autocorr = convolve(seq0, _seq1, autocorr, NO_DELAY);
+ if (!autocorr) {
+ status = false;
+ goto release;
+ }
+
+ gSCHSequence = new CorrelationSequence;
+ gSCHSequence->sequence = _seq1;
+ gSCHSequence->buffer = data;
+ gSCHSequence->gain = peakDetect(*autocorr, &toa, NULL);
+
+ /* For 1 sps only
+ * (Half of correlation length - 1) + midpoint of pulse shaping filer
+ * 20.5 = (64 / 2 - 1) + 1.5
+ */
+ if (sps == 1)
+ gSCHSequence->toa = toa - 32.5;
+ else
+ gSCHSequence->toa = 0.0;
+
+release:
+ delete autocorr;
+ delete seq0;
+ delete seq1;
+
+ if (!status) {
+ delete _seq1;
+ free(data);
+ gSCHSequence = NULL;
+ }
+
+ return status;
+}
+
+
/*
* Peak-to-average computation +/- range from peak in symbols
*/
@@ -1440,14 +1584,15 @@ float energyDetect(const signalVector &rxBurst, unsigned windowLength)
return energy/windowLength;
}
-static signalVector *downsampleBurst(const signalVector &burst)
+static signalVector *downsampleBurst(const signalVector &burst, int in_len = DOWNSAMPLE_IN_LEN,
+ int out_len = DOWNSAMPLE_OUT_LEN)
{
- signalVector in(DOWNSAMPLE_IN_LEN, dnsampler->len());
- signalVector *out = new signalVector(DOWNSAMPLE_OUT_LEN);
- burst.copyToSegment(in, 0, DOWNSAMPLE_IN_LEN);
+ signalVector in(in_len, dnsampler->len());
+ // gSCHSequence->sequence->size(), ensure next conv has no realloc
+ signalVector *out = new signalVector(out_len, 64);
+ burst.copyToSegment(in, 0, in_len);
- if (dnsampler->rotate((float *) in.begin(), DOWNSAMPLE_IN_LEN,
- (float *) out->begin(), DOWNSAMPLE_OUT_LEN) < 0) {
+ if (dnsampler->rotate((float *)in.begin(), in_len, (float *)out->begin(), out_len) < 0) {
delete out;
out = NULL;
}
@@ -1460,25 +1605,36 @@ static signalVector *downsampleBurst(const signalVector &burst)
* It is computed from the training sequence of each received burst,
* by comparing the "ideal" training sequence with the actual one.
*/
-static float computeCI(const signalVector *burst, CorrelationSequence *sync,
- float toa, int start, complex xcorr)
+static float computeCI(const signalVector *burst, const CorrelationSequence *sync,
+ float toa, int start, const complex &xcorr)
{
+ const int N = sync->sequence->size();
float S, C;
- int ps;
-
/* Integer position where the sequence starts */
- ps = start + 1 - sync->sequence->size() + (int)roundf(toa);
+ const int ps = start + 1 - N + (int)roundf(toa);
+
+ if(ps < 0) // might be -22 for toa 40 with N=64, if off by a lot during sch ms sync
+ return 0;
+
+ if (ps + N > (int)burst->size())
+ return 0;
/* Estimate Signal power */
S = 0.0f;
- for (int i=0, j=ps; i<(int)sync->sequence->size(); i++,j++)
+ for (int i=0, j=ps; i<(int)N; i++,j++)
S += (*burst)[j].norm2();
- S /= sync->sequence->size();
+ S /= N;
/* Esimate Carrier power */
- C = xcorr.norm2() / ((sync->sequence->size() - 1) * sync->gain.abs());
-
- /* Interference = Signal - Carrier, so C/I = C / (S - C) */
+ C = xcorr.norm2() / ((N - 1) * sync->gain.abs());
+
+ /* Interference = Signal - Carrier, so C/I = C / (S - C).
+ * Calculated in dB:
+ * C/I_dB = 10 * log10(C/I)
+ * C/I_dB = 10 * (1/log2(10)) * log2(C/I)
+ * C/I_dB = 10 * 0.30103 * log2(C/I)
+ * C/I_dB = 3.0103 * log2(C/I)
+ */
return 3.0103f * log2f(C / (S - C));
}
@@ -1491,7 +1647,7 @@ static float computeCI(const signalVector *burst, CorrelationSequence *sync,
* and we run full interpolating peak detection.
*/
static int detectBurst(const signalVector &burst,
- signalVector &corr, CorrelationSequence *sync,
+ signalVector &corr, const CorrelationSequence *sync,
float thresh, int sps, int start, int len,
struct estim_burst_params *ebp)
{
@@ -1500,12 +1656,18 @@ static int detectBurst(const signalVector &burst,
complex xcorr;
int rc = 1;
- if (sps == 4) {
- dec = downsampleBurst(burst);
- corr_in = dec;
- sps = 1;
- } else {
+ switch (sps) {
+ case 1:
corr_in = &burst;
+ break;
+ case 4:
+ dec = downsampleBurst(burst);
+ /* Running at the downsampled rate at this point: */
+ corr_in = dec;
+ sps = 1;
+ break;
+ default:
+ osmo_panic("%s:%d SPS %d not supported! Only 1 or 4 supported", __FILE__, __LINE__, sps);
}
/* Correlate */
@@ -1515,9 +1677,6 @@ static int detectBurst(const signalVector &burst,
goto del_ret;
}
- /* Running at the downsampled rate at this point */
- sps = 1;
-
/* Peak detection - place restrictions at correlation edges */
ebp->amp = fastPeakDetect(corr, &ebp->toa);
@@ -1586,7 +1745,7 @@ static int detectGeneralBurst(const signalVector &rxBurst, float thresh, int sps
// and only report clipping if we can't demod.
float maxAmpl = maxAmplitude(rxBurst);
if (maxAmpl > CLIP_THRESH) {
- LOG(DEBUG) << "max burst amplitude: " << maxAmpl << " is above the clipping threshold: " << CLIP_THRESH << std::endl;
+ LOG(INFO) << "max burst amplitude: " << maxAmpl << " is above the clipping threshold: " << CLIP_THRESH << std::endl;
clipping = true;
}
@@ -1643,6 +1802,80 @@ static int detectRACHBurst(const signalVector &burst, float threshold, int sps,
return rc;
}
+int detectSCHBurst(signalVector &burst,
+ float thresh,
+ int sps,
+ sch_detect_type state, struct estim_burst_params *ebp)
+{
+ int rc, start, target, head, tail, len;
+ complex _amp;
+ CorrelationSequence *sync;
+
+ if ((sps != 1) && (sps != 4))
+ return -1;
+
+ target = 3 + 39 + 64;
+
+ switch (state) {
+ case sch_detect_type::SCH_DETECT_NARROW:
+ head = 4;
+ tail = 4;
+ break;
+ case sch_detect_type::SCH_DETECT_BUFFER:
+ target = 1;
+ head = 0;
+ tail = (12 * 8 * 625) / 4; // 12 frames, downsampled /4 to 1 sps
+ break;
+ case sch_detect_type::SCH_DETECT_FULL:
+ default:
+ head = target - 1;
+ tail = 39 + 3 + 9;
+ break;
+ }
+
+ start = (target - head) * 1 - 1;
+ len = (head + tail) * 1;
+ sync = gSCHSequence;
+ signalVector corr(len);
+
+ signalVector *dec = downsampleBurst(burst, len * 4, len);
+ rc = detectBurst(*dec, corr, sync, thresh, 1, start, len, ebp);
+ delete dec;
+
+ if (rc < 0) {
+ return -1;
+ } else if (!rc) {
+ ebp->amp = 0.0f;
+ ebp->toa = 0.0f;
+ return 0;
+ }
+
+ if (state == sch_detect_type::SCH_DETECT_BUFFER)
+ ebp->toa = ebp->toa - (3 + 39 + 64);
+ else {
+ /* Subtract forward search bits from delay */
+ ebp->toa = ebp->toa - head;
+ }
+
+ return rc;
+}
+
+static int detectDummyBurst(const signalVector &burst, float threshold,
+ int sps, unsigned max_toa, struct estim_burst_params *ebp)
+{
+ int rc, target, head, tail;
+ CorrelationSequence *sync;
+
+ target = 3 + 58 + 16 + 5;
+ head = 10;
+ tail = 6 + max_toa;
+ sync = gDummySequence;
+
+ ebp->tsc = 0;
+ rc = detectGeneralBurst(burst, threshold, sps, target, head, tail, sync, ebp);
+ return rc;
+}
+
/*
* Normal burst detection
*
@@ -1661,7 +1894,7 @@ static int analyzeTrafficBurst(const signalVector &burst, unsigned tsc, float th
return -SIGERR_UNSUPPORTED;
target = 3 + 58 + 16 + 5;
- head = 6;
+ head = 10;
tail = 6 + max_toa;
sync = gMidambles[tsc];
@@ -1710,6 +1943,9 @@ int detectAnyBurst(const signalVector &burst, unsigned tsc, float threshold,
case RACH:
rc = detectRACHBurst(burst, threshold, sps, max_toa, type == EXT_RACH, ebp);
break;
+ case IDLE:
+ rc = detectDummyBurst(burst, threshold, sps, max_toa, ebp);
+ break;
default:
LOG(ERR) << "Invalid correlation type";
}
@@ -1792,15 +2028,15 @@ static SoftVector *signalToSoftVector(signalVector *dec)
* stages.
*/
static signalVector *demodCommon(const signalVector &burst, int sps,
- complex chan, float toa)
+ const struct estim_burst_params *ebp)
{
signalVector *delay, *dec;
if ((sps != 1) && (sps != 4))
return NULL;
- delay = delayVector(&burst, NULL, -toa * (float) sps);
- scaleVector(*delay, (complex) 1.0 / chan);
+ delay = delayVector(&burst, NULL, -ebp->toa * (float) sps);
+ scaleVector(*delay, (complex) 1.0 / ebp->amp);
if (sps == 1)
return delay;
@@ -1816,13 +2052,13 @@ static signalVector *demodCommon(const signalVector &burst, int sps,
* 4 SPS (if activated) to minimize distortion through the fractional
* delay filters. Symbol rotation and after always operates at 1 SPS.
*/
-static SoftVector *demodGmskBurst(const signalVector &rxBurst,
- int sps, complex channel, float TOA)
+static SoftVector *demodGmskBurst(const signalVector &rxBurst, int sps,
+ const struct estim_burst_params *ebp)
{
SoftVector *bits;
signalVector *dec;
- dec = demodCommon(rxBurst, sps, channel, TOA);
+ dec = demodCommon(rxBurst, sps, ebp);
if (!dec)
return NULL;
@@ -1835,29 +2071,51 @@ static SoftVector *demodGmskBurst(const signalVector &rxBurst,
return bits;
}
+static float computeEdgeCI(const signalVector *rot)
+{
+ float err_pwr = 0.0f;
+ float step = 2.0f * M_PI_F / 8.0f;
+
+ for (size_t i = 8; i < rot->size() - 8; i++) {
+ /* Compute the ideal symbol */
+ complex sym = (*rot)[i];
+ float phase = step * roundf(sym.arg() / step);
+ complex ideal = complex(cos(phase), sin(phase));
+
+ /* Compute the error vector */
+ complex err = ideal - sym;
+
+ /* Accumulate power */
+ err_pwr += err.norm2();
+ }
+
+ return 3.0103f * log2f(1.0f * (rot->size() - 16) / err_pwr);
+}
+
/*
* Demodulate an 8-PSK burst. Prior to symbol rotation, operate at
* 4 SPS (if activated) to minimize distortion through the fractional
* delay filters. Symbol rotation and after always operates at 1 SPS.
*
* Allow 1 SPS demodulation here, but note that other parts of the
- * transceiver restrict EDGE operatoin to 4 SPS - 8-PSK distortion
+ * transceiver restrict EDGE operation to 4 SPS - 8-PSK distortion
* through the fractional delay filters at 1 SPS renders signal
* nearly unrecoverable.
*/
-static SoftVector *demodEdgeBurst(const signalVector &burst,
- int sps, complex chan, float toa)
+static SoftVector *demodEdgeBurst(const signalVector &burst, int sps,
+ struct estim_burst_params *ebp)
{
SoftVector *bits;
signalVector *dec, *rot, *eq;
- dec = demodCommon(burst, sps, chan, toa);
+ dec = demodCommon(burst, sps, ebp);
if (!dec)
return NULL;
/* Equalize and derotate */
eq = convolve(dec, GSMPulse4->c0_inv, NULL, NO_DELAY);
rot = derotateEdgeBurst(*eq, 1);
+ ebp->ci = computeEdgeCI(rot);
/* Soft slice and normalize */
bits = softSliceEdgeBurst(*rot);
@@ -1869,13 +2127,13 @@ static SoftVector *demodEdgeBurst(const signalVector &burst,
return bits;
}
-SoftVector *demodAnyBurst(const signalVector &burst, int sps, complex amp,
- float toa, CorrType type)
+SoftVector *demodAnyBurst(const signalVector &burst, CorrType type,
+ int sps, struct estim_burst_params *ebp)
{
if (type == EDGE)
- return demodEdgeBurst(burst, sps, amp, toa);
+ return demodEdgeBurst(burst, sps, ebp);
else
- return demodGmskBurst(burst, sps, amp, toa);
+ return demodGmskBurst(burst, sps, ebp);
}
bool sigProcLibSetup()
@@ -1890,6 +2148,9 @@ bool sigProcLibSetup()
generateRACHSequence(&gRACHSequences[1], gRACHSynchSequenceTS1, 1);
generateRACHSequence(&gRACHSequences[2], gRACHSynchSequenceTS2, 1);
+ generateSCHSequence(1);
+ generateDummyMidamble(1);
+
for (int tsc = 0; tsc < 8; tsc++) {
generateMidamble(1, tsc);
gEdgeMidambles[tsc] = generateEdgeMidamble(tsc);
diff --git a/Transceiver52M/sigProcLib.h b/Transceiver52M/sigProcLib.h
index dd03190..39c8ddd 100644
--- a/Transceiver52M/sigProcLib.h
+++ b/Transceiver52M/sigProcLib.h
@@ -31,6 +31,7 @@ enum CorrType{
TSC, ///< timeslot should contain a normal burst
EXT_RACH, ///< timeslot should contain an extended access burst
RACH, ///< timeslot should contain an access burst
+ SCH,
EDGE, ///< timeslot should contain an EDGE burst
IDLE ///< timeslot is an idle (or dummy) burst
};
@@ -93,6 +94,8 @@ signalVector *generateDummyBurst(int sps, int tn);
void scaleVector(signalVector &x,
complex scale);
+signalVector *delayVector(const signalVector *in, signalVector *out, float delay);
+
/**
Rough energy estimator.
@param rxBurst A GSM burst.
@@ -133,8 +136,19 @@ int detectAnyBurst(const signalVector &burst,
unsigned max_toa,
struct estim_burst_params *ebp);
+enum class sch_detect_type {
+ SCH_DETECT_FULL,
+ SCH_DETECT_NARROW,
+ SCH_DETECT_BUFFER,
+};
+
+int detectSCHBurst(signalVector &rxBurst,
+ float detectThreshold,
+ int sps,
+ sch_detect_type state, struct estim_burst_params *ebp);
+
/** Demodulate burst basde on type and output soft bits */
-SoftVector *demodAnyBurst(const signalVector &burst, int sps,
- complex amp, float toa, CorrType type);
+SoftVector *demodAnyBurst(const signalVector &burst, CorrType type,
+ int sps, struct estim_burst_params *ebp);
#endif /* SIGPROCLIB_H */
diff --git a/configure.ac b/configure.ac
index 4ad344d..8aecb27 100644
--- a/configure.ac
+++ b/configure.ac
@@ -34,6 +34,8 @@ AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE([foreign subdir-objects])
+CFLAGS="$CFLAGS -std=gnu11"
+
dnl Linux kernel KBuild style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -80,19 +82,10 @@ AC_TYPE_SIZE_T
AC_HEADER_TIME
AC_C_BIGENDIAN
-# Check if gettid is available (despite not being documented in glibc doc, it requires __USE_GNU on some systems)
-# C compiler is used since __USE_GNU seems to be always defined for g++.
-save_CPPFLAGS=$CPPFLAGS
-AC_LANG_PUSH(C)
-CPPFLAGS="$CPPFLAGS -D_GNU_SOURCE"
-AC_CHECK_FUNCS([gettid])
-AC_LANG_POP(C)
-CPPFLAGS=$save_CPPFLAGS
-
-PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.3.0)
-PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.3.0)
-PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.3.0)
-PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.3.0)
+PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
+PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
+PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
+PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.9.0)
AC_ARG_ENABLE(sanitize,
[AS_HELP_STRING(
@@ -145,6 +138,16 @@ AC_ARG_WITH(ipc, [
[enable IPC])
])
+AC_ARG_WITH(bladerf, [
+ AS_HELP_STRING([--with-bladerf],
+ [enable bladeRF])
+])
+
+AC_ARG_WITH(mstrx, [
+ AS_HELP_STRING([--with-mstrx],
+ [enable MS TRX])
+])
+
AC_ARG_WITH(singledb, [
AS_HELP_STRING([--with-singledb],
[enable single daughterboard use on USRP1])
@@ -192,6 +195,37 @@ AS_IF([test "x$with_uhd" = "xyes"],[
[PKG_CHECK_MODULES(UHD, uhd >= 003.005)]
)]
)
+ # OS#5608: libuhd < 4.2.0 includes boost/thread/thread.hpp in its logging
+ # code and therefore requires linking against boost_thread. It's missing in
+ # uhd.pc, so work around it here.
+ # https://github.com/EttusResearch/uhd/commit/04a83b6e76beef970854da69ba882d717669b49c
+ PKG_CHECK_MODULES(UHD, uhd < 004.002,
+ [LIBS="$LIBS -lboost_thread"],
+ []
+ )
+])
+
+AS_IF([test "x$with_bladerf" = "xyes"], [
+ PKG_CHECK_MODULES(BLADE, libbladeRF >= 2.0)
+])
+
+AC_MSG_CHECKING([whether to enable building MS TRX])
+AS_IF([test "x$with_mstrx" = "xyes"], [
+ AC_CONFIG_SUBDIRS([osmocom-bb/src/host/trxcon])
+ LIBTRXCON_DIR="osmocom-bb/src/host/trxcon"
+ if ! test -d "$srcdir/$LIBTRXCON_DIR"; then
+ AC_MSG_RESULT([no])
+ AC_MSG_ERROR([$LIBTRXCON_DIR does not exist])
+ fi
+ AC_SUBST(LIBTRXCON_DIR)
+ AC_MSG_RESULT([yes])
+], [
+ # Despite LIBTRXCON_DIR is added to SUBDIRS conditionally,
+ # autoconf/automake still requires the directory to be present
+ # and thus the submodule to be fetched (even if MS TRX is not needed).
+ # Work this around by pointing it to an empty dir.
+ AC_SUBST(LIBTRXCON_DIR, "osmocom-bb")
+ AC_MSG_RESULT([no])
])
AS_IF([test "x$with_singledb" = "xyes"], [
@@ -247,8 +281,10 @@ AM_CONDITIONAL(DEVICE_UHD, [test "x$with_uhd" = "xyes"])
AM_CONDITIONAL(DEVICE_USRP1, [test "x$with_usrp1" = "xyes"])
AM_CONDITIONAL(DEVICE_LMS, [test "x$with_lms" = "xyes"])
AM_CONDITIONAL(DEVICE_IPC, [test "x$with_ipc" = "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_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(FFTWF, fftw3f)
@@ -316,6 +352,7 @@ AC_MSG_RESULT([CFLAGS="$CFLAGS"])
AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"])
AC_MSG_RESULT([LDFLAGS="$LDFLAGS"])
+
dnl Output files
AC_CONFIG_FILES([\
Makefile \
@@ -332,16 +369,17 @@ AC_CONFIG_FILES([\
Transceiver52M/device/usrp1/Makefile \
Transceiver52M/device/lms/Makefile \
Transceiver52M/device/ipc/Makefile \
+ Transceiver52M/device/bladerf/Makefile \
tests/Makefile \
tests/CommonLibs/Makefile \
tests/Transceiver52M/Makefile \
utils/Makefile \
+ utils/va-test/Makefile \
doc/Makefile \
doc/examples/Makefile \
contrib/Makefile \
contrib/systemd/Makefile \
+ doc/manuals/Makefile \
+ contrib/osmo-trx.spec \
])
-
-AC_OUTPUT(
- doc/manuals/Makefile
- contrib/osmo-trx.spec)
+AC_OUTPUT
diff --git a/contrib/jenkins.sh b/contrib/jenkins.sh
index 7b751ad..73d3ffd 100755
--- a/contrib/jenkins.sh
+++ b/contrib/jenkins.sh
@@ -85,11 +85,20 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
export LD_LIBRARY_PATH="$inst/lib"
export PATH="$inst/bin:$PATH"
-CONFIG="--enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms --with-ipc $INSTR"
+CONFIG="
+ --enable-sanitize
+ --enable-werror
+ --with-bladerf
+ --with-ipc
+ --with-lms
+ --with-mstrx
+ --with-uhd
+ --with-usrp1
+ $INSTR
+"
# Additional configure options and depends
if [ "$WITH_MANUALS" = "1" ]; then
- osmo-build-dep.sh osmo-gsm-manuals
CONFIG="$CONFIG --enable-manuals"
fi
@@ -102,13 +111,17 @@ echo
set -x
cd "$base"
+git submodule status
autoreconf --install --force
./configure $CONFIG
$MAKE $PARALLEL_MAKE
$MAKE check \
|| cat-testlogs.sh
-DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck \
- || cat-testlogs.sh
+
+if arch | grep -v -q arm; then
+ DISTCHECK_CONFIGURE_FLAGS="$(echo $CONFIG | tr -d '\n')" $MAKE $PARALLEL_MAKE distcheck \
+ || cat-testlogs.sh
+fi
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
make -C "$base/doc/manuals" publish
@@ -116,9 +129,4 @@ fi
$MAKE $PARALLEL_MAKE maintainer-clean
-# Verify distro-specific package patches apply:
-for patch in debian/patches/*.patch; do
- patch --dry-run -p1 < "$patch"
-done
-
osmo-clean-workspace.sh
diff --git a/contrib/osmo-trx.spec.in b/contrib/osmo-trx.spec.in
index ace7212..5243d63 100644
--- a/contrib/osmo-trx.spec.in
+++ b/contrib/osmo-trx.spec.in
@@ -34,10 +34,10 @@ BuildRequires: pkgconfig(LimeSuite)
BuildRequires: pkgconfig(usrp) >= 3.3
%endif
BuildRequires: pkgconfig(fftw3f)
-BuildRequires: pkgconfig(libosmocoding) >= 1.3.0
-BuildRequires: pkgconfig(libosmocore) >= 0.12.0
-BuildRequires: pkgconfig(libosmoctrl) >= 0.12.0
-BuildRequires: pkgconfig(libosmovty) >= 0.12.0
+BuildRequires: pkgconfig(libosmocoding) >= 1.9.0
+BuildRequires: pkgconfig(libosmocore) >= 1.9.0
+BuildRequires: pkgconfig(libosmoctrl) >= 1.9.0
+BuildRequires: pkgconfig(libosmovty) >= 1.9.0
BuildRequires: pkgconfig(libusb-1.0)
BuildRequires: pkgconfig(uhd)
%{?systemd_requires}
@@ -149,6 +149,16 @@ connect mobile phones to the mobile network.
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)
+%package ipc-test
+Summary: SDR transceiver that implements Layer 1 of a GSM BTS (IPC) driver test utility
+Group: Productivity/Telephony/Servers
+
+%description ipc-test
+OsmoTRX is a software-defined radio transceiver that implements the Layer 1
+physical layer of a BTS comprising the following 3GPP specifications:
+
+This package include the test tools for osmo-trx-ipc
+
%prep
%setup -q
@@ -234,9 +244,11 @@ make %{?_smp_mflags} check || (find . -name testsuite.log -exec cat {} +)
%files ipc
%{_bindir}/osmo-trx-ipc
-%{_bindir}/ipc-driver-test
%dir %{_sysconfdir}/osmocom
%config(noreplace) %{_sysconfdir}/osmocom/osmo-trx-ipc.cfg
%{_unitdir}/osmo-trx-ipc.service
+%files ipc-test
+%{_bindir}/ipc-driver-test
+
%changelog
diff --git a/contrib/systemd/osmo-trx-ipc.service b/contrib/systemd/osmo-trx-ipc.service
index c886ed7..c7f7c17 100644
--- a/contrib/systemd/osmo-trx-ipc.service
+++ b/contrib/systemd/osmo-trx-ipc.service
@@ -1,11 +1,20 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (IPC Backend)
+After=network-online.target
+Wants=network-online.target
[Service]
Type=simple
Restart=always
+StateDirectory=osmocom
+WorkingDirectory=%S/osmocom
ExecStart=/usr/bin/osmo-trx-ipc -C /etc/osmocom/osmo-trx-ipc.cfg
RestartSec=2
+# CPU scheduling policy:
+CPUSchedulingPolicy=rr
+# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
+CPUSchedulingPriority=21
+# See sched(7) for further details on real-time policies and priorities
[Install]
WantedBy=multi-user.target
diff --git a/contrib/systemd/osmo-trx-lms.service b/contrib/systemd/osmo-trx-lms.service
index df63e21..f51bd45 100644
--- a/contrib/systemd/osmo-trx-lms.service
+++ b/contrib/systemd/osmo-trx-lms.service
@@ -1,11 +1,20 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (LimeSuite backend)
+After=network-online.target
+Wants=network-online.target
[Service]
Type=simple
Restart=always
+StateDirectory=osmocom
+WorkingDirectory=%S/osmocom
ExecStart=/usr/bin/osmo-trx-lms -C /etc/osmocom/osmo-trx-lms.cfg
RestartSec=2
+# CPU scheduling policy:
+CPUSchedulingPolicy=rr
+# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
+CPUSchedulingPriority=21
+# See sched(7) for further details on real-time policies and priorities
[Install]
WantedBy=multi-user.target
diff --git a/contrib/systemd/osmo-trx-uhd.service b/contrib/systemd/osmo-trx-uhd.service
index ba27f37..6c5c2de 100644
--- a/contrib/systemd/osmo-trx-uhd.service
+++ b/contrib/systemd/osmo-trx-uhd.service
@@ -1,11 +1,21 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (UHD Backend)
+After=network-online.target
+Wants=network-online.target
[Service]
Type=simple
Restart=always
+StateDirectory=osmocom
+WorkingDirectory=%S/osmocom
+Environment=HOME=%h
ExecStart=/usr/bin/osmo-trx-uhd -C /etc/osmocom/osmo-trx-uhd.cfg
RestartSec=2
+# CPU scheduling policy:
+CPUSchedulingPolicy=rr
+# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
+CPUSchedulingPriority=21
+# See sched(7) for further details on real-time policies and priorities
[Install]
WantedBy=multi-user.target
diff --git a/contrib/systemd/osmo-trx-usrp1.service b/contrib/systemd/osmo-trx-usrp1.service
index fbff631..988c053 100644
--- a/contrib/systemd/osmo-trx-usrp1.service
+++ b/contrib/systemd/osmo-trx-usrp1.service
@@ -1,11 +1,20 @@
[Unit]
Description=Osmocom SDR BTS L1 Transceiver (libusrp backend)
+After=network-online.target
+Wants=network-online.target
[Service]
Type=simple
Restart=always
+StateDirectory=osmocom
+WorkingDirectory=%S/osmocom
ExecStart=/usr/bin/osmo-trx-usrp1 -C /etc/osmocom/osmo-trx-usrp1.cfg
RestartSec=2
+# CPU scheduling policy:
+CPUSchedulingPolicy=rr
+# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
+CPUSchedulingPriority=21
+# See sched(7) for further details on real-time policies and priorities
[Install]
WantedBy=multi-user.target
diff --git a/debian/changelog b/debian/changelog
index 0ec7b9f..90f554e 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,325 @@
+osmo-trx (1.6.0) unstable; urgency=medium
+
+ [ Vadim Yanitskiy ]
+ * configure.ac: check if LIBTRXCON_DIR (submodule) exists
+ * tests: Makefile.am: move -I flags from AM_CFLAGS to AM_CPPFLAGS
+ * tests: there shall be no libraries in LDFLAGS
+ * tests: use -no-install libtool flag to avoid ./lt-* scripts
+ * tests: LMSDeviceTest: fix CPPFLAGS vs CXXFLAGS
+ * ipc-driver-test: clean up variables in Makefile.am
+ * CommonLibs: remove unused *trx in cfg[_no]_ctr_error_threshold_cmd
+ * CommonLibs: clean up and fix Makefile.am
+ * ms: logging: print category, level, and extended timestamp
+
+ [ Oliver Smith ]
+ * Run struct_endianness.py
+ * debian: set compat level to 10
+ * systemd: depend on networking-online.target
+ * USRPDevice:updateAlignment: remove byteswap code
+
+ [ Eric ]
+ * .clang-format: adjust template formatting
+ * ms: update submodule to currently known working version
+ * ms: adjust tx scaling for tx samples
+ * ms : fix the template formatting
+ * ms: fix the gain init for blade
+ * ms: prettify scheduling + add odroid
+ * ms: fix startup & shutdown of blade
+ * ms: block burst q to upper layer
+ * ms: use single thread pool
+ * ms : rename var
+ * ms : rename var
+ * ms: cache frequency
+ * ms: pretty tx buf class
+ * ms: rearrange internal trxcon<->phy if
+ * ms: remove syncthing tool
+ * ms: prune common sch acq code
+ * ms: rearrange code to allow clean exits
+ * ms: flexible template for value_type buffer sum
+ * ms: make init call less confusing
+ * transceiver: pass cfg struct instead of args
+ * devices: unify band handling
+ * ms: fix blocking logging
+ * ms: drop the tx burst padding
+ * trx: fix dev-args issue
+ * ms: update osmocom-bb
+ * ms: restructure the va code to add rach support
+ * transceiver: add experimental viterbi equalizer support
+ * ms/va: make ancient gcc < 8 happy
+ * ms: fix thread prio startup issue
+ * ms: fix a few coverity complaints related to initialization
+ * ms: bump osmocom-bb submodule to current head
+
+ [ Eric Wild ]
+ * ms: adjust float<->integral type conversion
+ * ms: sch: drop intermediate softvector
+ * devices: add freq/gain override for uhd
+
+ [ arehbein ]
+ * Transition to use of 'telnet_init_default'
+
+ [ Pau Espin Pedrol ]
+ * Call osmo_fd_unregister() before closing and changing bfd->fd
+ * ms: update osmocom-bb submodule
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 12 Sep 2023 15:56:57 +0200
+
+osmo-trx (1.5.0) unstable; urgency=medium
+
+ [ Oliver Smith ]
+ * configure.ac: add -lboost_thread for uhd < 4.2.0
+ * gitignore: add uhddev_ipc.cpp
+ * contrib/jenkins: don't run "make distcheck" on arm
+
+ [ Vadim Yanitskiy ]
+ * threshold_timer_update_intv(): call osmo_timer_del() unconditionally
+ * Transceiver::expectedCorrType(): RACH is always 8-bit on PTCCH/U
+ * contrib/jenkins.sh: dump submodule status before building
+ * configure.ac: fix: properly check whether to enable ms-trx
+ * configure.ac: allow building without cloning submodules
+ * configure.ac: cosmetic: rearrange MS TRX related logic
+ * configure.ac: make use of AC_MSG_CHECKING and AC_MSG_RESULT
+
+ [ Max ]
+ * Set working directory in systemd service file
+ * Add realtime scheduling and set priority in service file
+ * ctrl: take both address and port from vty config
+
+ [ Eric ]
+ * ignore vscode dirs
+ * rename noisevector class -> avgvector
+ * osmocom-bb for ms-trx side trxcon integration
+ * add checkpatch config
+ * bladerf xa4 support
+ * update osmocom-bb submodule to fix make distcheck
+ * vita demod by piotr krysik, modified
+ * properly update osmocom-bb submodule, for real this time..
+ * ms-trx support
+ * clean up mutex, scopedlock, and signal classes
+ * ipc: add missing override
+ * clang-format: proper c++ standard
+ * ipc: remove old autotools workaround
+ * ms: init trash used to escape the usb callbacks
+ * radio interface: fix init
+
+ [ Eric Wild ]
+ * mstrx: do not wait forever if clock locking fails
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 07 Feb 2023 17:08:17 +0100
+
+osmo-trx (1.4.1) unstable; urgency=medium
+
+ [ Oliver Smith ]
+ * treewide: remove FSF address
+
+ [ Vadim Yanitskiy ]
+ * tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
+
+ [ Harald Welte ]
+ * update git URLs (git -> https; gitea)
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Wed, 29 Jun 2022 09:32:56 +0200
+
+osmo-trx (1.4.0) unstable; urgency=medium
+
+ [ Pau Espin Pedrol ]
+ * Threads.cpp: Fix missing extern C around libosmocore include
+ * Drop logging pthread ID
+ * Threads: Avoid printing pthread_self()
+ * ipc: Makefile.am: Clean LDADD variable
+ * Use new stat item/ctr getter APIs
+ * detectBurst(): Clear downsampling code path
+ * detectBurst(): constify parameter
+ * computeCI(): Constify param and pass it as reference
+ * computeCI(): Rename verbose repeated getter to constant
+ * computeCI(): Constify read-only variable
+ * detectGeneralBurst(): Increase log level about clipping to INFO
+ * cosmetic: Fix typo in comment
+ * computeCI: Document hardcoded multiplier
+ * lms: Drop duplicated check
+ * lms,uhd: Validate band of RxFreq too
+ * lms,uhd: Skip re-assigning same band
+ * lms,uhd: Allow changing band between poweroff & poweron
+
+ [ Vadim Yanitskiy ]
+ * gitignore: remove non-existing 'doc/manuals/osmomsc-usermanual.xml'
+ * ctrl_sock_handle_rx(): fix missing space in LOGCHAN() statement
+ * trx_rate_ctr: use thread safe strerror() in device_sig_cb()
+ * IPCDevice: use thread safe strerror_r() instead of strerror()
+ * IPCDevice: check value returned from select()
+ * LMSDevice: LMS_GetDeviceList() may return a negative number
+
+ [ Eric ]
+ * add hidden fn adjustment command
+ * uhd: ensure configured clock source is actually used
+ * vty: printing fn offset should be signed
+ * lms: init band
+
+ [ Oliver Smith ]
+ * d/patches/build-for-debian8.patch: remove
+
+ -- Pau Espin Pedrol <pespin@sysmocom.de> Tue, 16 Nov 2021 16:27:26 +0100
+
+osmo-trx (1.3.1) unstable; urgency=medium
+
+ * mark uhddev_ipc.cpp as BUILT_SOURCES
+
+ -- Harald Welte <laforge@osmocom.org> Sun, 28 Feb 2021 11:32:11 +0100
+
+osmo-trx (1.3.0) unstable; urgency=medium
+
+ [ Pau Espin Pedrol ]
+ * Make logging category DLMS generic and reusable for other backends
+ * uhd: Use DEVDRV log category and support UHD >=3.11 logging framework
+ * uhd: Improve some logging lines printing UHD pretty-print output
+ * doc: clarify number of channels on B210 with multi-arfcn enabled
+ * radioInterfaceMulti: Fail to tune on freq not following multi-arfcn restrictions
+ * doc: Update vty reference xml file
+ * lms: Move initialization of field started to constructor
+ * lms: Drop unused define
+ * smpl_buf: Fix str_code() param and print unknown error val
+ * lms: Improve smpl_buf error logging
+ * lms: Change radioDevice constructor arg name to avoid masking instance attr
+ * lms: Make reference to std::vector unambiguous
+ * lms: Move rx_buffers allocation to constructor
+ * lms: Store device type specific parameters in one place
+ * lms: Make ts_offset and smpl rate coefs device-specific
+ * lms: Initial multi-arfcn support
+ * contrib/jenkins.sh: Reorder sanity checks
+ * debug.h: Avoid printing pthread_t type
+ * debug.h: Fix print format of chan in CLOGCHAN
+ * cosmetic: fix several typos found by codespell
+ * radioDevice: Drop unused RSSI param from readSamples API
+ * radioDevice: Drop unused isControl param from WriteSamples API
+ * Use OSMO_FD_READ instead of deprecated BSC_FD_READ
+ * Transceiver: Fix extra space in RSP NOISELEV error
+ * Transceiver: Implement TRXC cmd NOMTXPOWER
+ * UHDDevice: Implement getNominalTxPower() based on TxFrequency
+ * radioInterface: Operate on real Tx power attenuation rather than on device specific gains
+ * UHDDevice: Compute TxGain on UHD API based on expected Tx output power
+ * proto_trxd: Fix UndefinedBehaviorSanitizer from ubsan
+ * Transceiver: Allow sending negative nominal tx power in RSP NOMTXPOWER
+ * LMSDevice: Compute TxGain on LimeSuite API based on expected Tx output power
+ * Drop old TxGain APIs from parent radioDevice abstract class
+ * {UHD,LMS}Dervice: Log expected resulting TxPower when setting device specific TxGain
+ * cosmetic: trx_rate_ctr: Fix whitespace
+ * trx_rate_ctr: Fix immediate rescheduling on per-sec thresholds
+ * Rename device specific rate counter multi-thread helpers
+ * Introduce rate counter tx_stale_bursts
+ * TransceiverState: Initialize ctrs field in constructor
+ * doc/manuals: Update thread documentation after dropping CTRL sock threads
+ * trx_rate_ctr: Fix locking wrong mutex
+ * Introduce rate counters to detect issues in received Dl bursts from TRXD
+ * Transceiver: Fix race condition obtaining Dl burst from Upper layer
+ * Add rate counter for missing Txbursts when scheduled towards the radioInterface
+ * Transceiver: Provide initial value for TransceiverState::mFiller in constructor
+ * Transceiver: Use already obtained value from Rx msg structure
+ * Transceiver: Restrict conditions where FN gaps are detected
+ * trx_rate_ctr: Lower some log levels
+ * Introduce CTR log category
+ * Transceiver: Lower some log levels which have an associated counter
+ * Transceiver: Check log level before generating burst str representation
+ * Transceiver: Add several rate_ctr for rx error conditions
+ * Use new libosmovty cpu sched config features
+ * debian: Update debian8 osmo-trx specific patch
+ * jenkins.sh: Verify distro-specific patches apply
+ * ipc: fix var declaration in for loop
+ * Add support for TRXC MUTE command
+ * arch: x86: Fix convolve optimizations breaking signal
+ * contrib/jenkins: Enable parallel make in make distcheck
+ * Transceiver: Pass config struct instead of large list of params
+ * Calculate RSSI offset based on RxGain configuration
+ * main: generate coredump and exit upon SIGABRT received
+ * ipc: Fix wrong reference to BTS in log line
+ * ipc-driver-test: Allow setting dir prefix for UD socket
+ * radioInterfaceMulti: Fix memory leak upon close()
+ * ChannelizerBase: Fix memory leak
+ * Threads.cpp: Use already existing gettid wrapper function
+ * Replace my_gettid with libosmocore osmo_gettid API
+ * tests: Explicitly drop category from log
+ * tests: Replace deprecated API log_set_print_filename
+
+ [ Philipp Maier ]
+ * debug: use LOGL_NOTICE for log category DDEV
+ * doc: do not set the base-port of the trx
+ * doc: apply an rssi-offset of 28 by default.
+ * doc: switch log levels to notice
+ * Transceiver: Log when sending of CLK indications begins
+ * vty: add attributes to VTY commands indicating when they apply
+ * osmo-trx: add commandline option --vty-ref-xml
+
+ [ Eric ]
+ * configure.ac: fix libtool issue with clang and sanitizer
+ * transceiver: check the right vector
+ * transceiver: get rid of the ctrl threads
+ * add kernel style .clang-format with 120 chars per line limit
+ * devices: reset internal smart sample buffers upon stop
+ * transceiver: optimize code if optimizations are enabled
+ * transceiver: initialize reorder flag so we don't miscount
+
+ [ Harald Welte ]
+ * PRBS tool sending PRBS sequence to TRX
+ * prbs-tool: Add error simulation capabilities
+ * utils: Ensure content of this directory is included in 'make dist'
+ * prbs-tool: Don't require C99
+ * RPM spec file: Require uhd-firmware for osmo-trx-uhd
+ * osmo-trx.spec.in: Use %config(noreplace) to retain current config file
+ * Fix build on Debian8
+ * [cosmetic] radioInterfaceMulti: More comments
+ * [cosmetic] radioIntefaceMulti: Fix whitespace / indent
+ * ipc: Use OSMO_FD_* instead of deprecated BSC_FD_*
+ * Use osmo_fd_setup() wherever applicable
+ * Use osmo_fd_*_{disable,enable}
+ * README update
+ * manual: Fix typo OsmTRX -> OsmoTRX
+
+ [ Oliver Smith ]
+ * contrib: import RPM spec
+ * contrib: integrate RPM spec
+ * Makefile.am: EXTRA_DIST: debian, contrib/*.spec.in
+ * contrib/jenkins: don't build osmo-gsm-manuals
+ * configure.ac: set -std=gnu11
+
+ [ Vadim Yanitskiy ]
+ * UHDDevice: catch LookupError/IndexError in set{Rx,Tx}Antenna()
+ * debian/control: change maintainer to the Osmocom team / mailing list
+ * proto_trxd: cosmetic: 'if' is not a function, add space
+ * vty: add multi-ARFCN specific warning for chan N > 0
+ * radioDevice: fix set_antennas(): consider MULTI_ARFCN mode
+ * device/lms: fix: 'trx_vty.h' header requires C linkage
+ * device/lms: fix missing semicolon in LMSDevice::assign_band_desc()
+ * device/lms: get rid of 'using namespace std'
+ * device/common/Makefile.am: remove $(LMS_CFLAGS) from AM_CXXFLAGS
+ * main: add --vty-ref-mode, use vty_dump_xml_ref_mode()
+ * device: drop unreasonable LIBOSMO{CTRL,VTY}_{CFLAGS,LIBS}
+ * Transceiver: use size_t and ARRAY_SIZE() in constructor
+ * Transceiver: explicitly init m{Rx,Tx}LowerLoopThread
+ * vty: fix documentation for 'swap-channels (disable|enable)'
+ * vty: fix documentation for 'egprs (disable|enable)'
+ * vty: fix documentation for 'rx-sps (1|4)' and 'tx-sps (1|4)'
+ * vty: cosmetic: use VTY_IPV4_CMD in 'bind-ip' / 'remote-ip'
+ * vty: fix documentation for 'multi-arfcn (disable|enable)'
+ * vty: remove groundless statement about filler type 'dummy'
+ * vty: auto-generate cmd and doc strings for cfg_filler_type_cmd
+ * vty: fix documentation for 'ext-rach (disable|enable)'
+ * main: use logging API to print SIMD info instead of printf()
+ * doc/manuals: generate XML VTY reference at build-time
+ * vty: fix swapped documentation for 'filler type' command
+ * Transceiver: fix integer division in addRadioVector()
+ * Transceiver: use proper factor for amplitude scaling
+ * Add a (hidden) VTY parameter for Rx/Tx freq. shifting
+
+ [ Eric Wild ]
+ * osmo-trx-ipc
+
+ [ Alexander Couzens ]
+ * osmo-trx.spec: move ipc-driver-test into package ipc-test
+
+ [ Sylvain Munaut ]
+ * sigProcLib: fix C/I computation for 8-PSK modulated bursts
+
+ -- Pau Espin Pedrol <pespin@espeweb.net> Tue, 23 Feb 2021 14:27:15 +0100
+
osmo-trx (1.2.0) unstable; urgency=medium
[ Pau Espin Pedrol ]
diff --git a/debian/compat b/debian/compat
index ec63514..f599e28 100644
--- a/debian/compat
+++ b/debian/compat
@@ -1 +1 @@
-9
+10
diff --git a/debian/control b/debian/control
index 12d9af5..1186479 100644
--- a/debian/control
+++ b/debian/control
@@ -2,7 +2,7 @@ Source: osmo-trx
Section: net
Priority: optional
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
-Build-Depends: debhelper (>= 9),
+Build-Depends: debhelper (>= 10),
autotools-dev,
autoconf-archive,
pkg-config,
@@ -14,11 +14,12 @@ Build-Depends: debhelper (>= 9),
libtalloc-dev,
libusrp-dev,
liblimesuite-dev,
- libosmocore-dev (>= 1.3.0),
- osmo-gsm-manuals-dev
+ libbladerf-dev,
+ libosmocore-dev (>= 1.9.0),
+ osmo-gsm-manuals-dev (>= 1.5.0)
Standards-Version: 3.9.6
-Vcs-Browser: http://cgit.osmocom.org/osmo-trx
-Vcs-Git: git://git.osmocom.org/osmo-trx
+Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
+Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
Homepage: https://projects.osmocom.org/projects/osmotrx
Package: osmo-trx
@@ -110,6 +111,25 @@ Description: SDR transceiver that implements Layer 1 of a GSM BTS (generic IPC)
between different telecommunication associations for developing new
generations of mobile phone networks. (post-2G/GSM)
+Package: osmo-trx-ms-blade
+Architecture: any
+Depends: ${shlibs:Depends}, ${misc:Depends}
+Description: MS side transceiver (bladeRF)
+ OsmoTRX is a software-defined radio transceiver that implements the Layer 1
+ physical layer of a BTS comprising the following 3GPP specifications:
+ .
+ TS 05.01 "Physical layer on the radio path"
+ TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
+ TS 05.04 "Modulation"
+ TS 05.10 "Radio subsystem synchronization"
+ .
+ In this context, BTS is "Base transceiver station". It's the stations that
+ connect mobile phones to the mobile network.
+ .
+ 3GPP is the "3rd Generation Partnership Project" which is the collaboration
+ between different telecommunication associations for developing new
+ generations of mobile phone networks. (post-2G/GSM)
+
Package: osmo-trx-doc
Architecture: all
Section: doc
diff --git a/debian/copyright b/debian/copyright
index 19926cd..8ec85cf 100644
--- a/debian/copyright
+++ b/debian/copyright
@@ -1,6 +1,6 @@
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: OsmoTRX
-Source: http://cgit.osmocom.org/osmo-trx/
+Source: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
Files-Excluded: Transceiver52M/device/usrp1/std_inband.rbf
Files: *
diff --git a/debian/osmo-trx-ms-blade.install b/debian/osmo-trx-ms-blade.install
new file mode 100644
index 0000000..0a3f163
--- /dev/null
+++ b/debian/osmo-trx-ms-blade.install
@@ -0,0 +1 @@
+/usr/bin/osmo-trx-ms-blade
diff --git a/debian/patches/build-for-debian8.patch b/debian/patches/build-for-debian8.patch
deleted file mode 100644
index ee3bd19..0000000
--- a/debian/patches/build-for-debian8.patch
+++ /dev/null
@@ -1,60 +0,0 @@
-diff --git a/debian/control b/debian/control
-index 12d9af5..27b9d60 100644
---- a/debian/control
-+++ b/debian/control
-@@ -13,7 +13,6 @@ Build-Depends: debhelper (>= 9),
- libfftw3-dev,
- libtalloc-dev,
- libusrp-dev,
-- liblimesuite-dev,
- libosmocore-dev (>= 1.3.0),
- osmo-gsm-manuals-dev
- Standards-Version: 3.9.6
-@@ -30,7 +29,7 @@ Package: osmo-trx-dbg
- Architecture: any
- Section: debug
- Priority: extra
--Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), osmo-trx-lms (= ${binary:Version}), osmo-trx-ipc (= ${binary:Version}), ${misc:Depends}
-+Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), osmo-trx-ipc (= ${binary:Version}), ${misc:Depends}
- Description: Debug symbols for the osmo-trx-*
- Make debugging possible
-
-@@ -72,25 +71,6 @@ Description: SDR transceiver that implements Layer 1 of a GSM BTS (USRP1)
- between different telecommunication associations for developing new
- generations of mobile phone networks. (post-2G/GSM)
-
--Package: osmo-trx-lms
--Architecture: any
--Depends: ${shlibs:Depends}, ${misc:Depends}
--Description: SDR transceiver that implements Layer 1 of a GSM BTS (LimeSuite)
-- OsmoTRX is a software-defined radio transceiver that implements the Layer 1
-- physical layer of a BTS comprising the following 3GPP specifications:
-- .
-- TS 05.01 "Physical layer on the radio path"
-- TS 05.02 "Multiplexing and Multiple Access on the Radio Path"
-- TS 05.04 "Modulation"
-- TS 05.10 "Radio subsystem synchronization"
-- .
-- In this context, BTS is "Base transceiver station". It's the stations that
-- connect mobile phones to the mobile network.
-- .
-- 3GPP is the "3rd Generation Partnership Project" which is the collaboration
-- between different telecommunication associations for developing new
-- generations of mobile phone networks. (post-2G/GSM)
--
- Package: osmo-trx-ipc
- Architecture: any
- Depends: ${shlibs:Depends}, ${misc:Depends}
-diff --git a/debian/rules b/debian/rules
-index 5795643..5937c17 100755
---- a/debian/rules
-+++ b/debian/rules
-@@ -9,7 +9,7 @@ override_dh_shlibdeps:
- dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
-
- override_dh_auto_configure:
-- dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-ipc --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
-+ dh_auto_configure -- --with-uhd --with-usrp1 --with-ipc --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
-
- override_dh_strip:
- dh_strip --dbg-package=osmo-trx-dbg
diff --git a/debian/patches/series b/debian/patches/series
deleted file mode 100644
index 82063b7..0000000
--- a/debian/patches/series
+++ /dev/null
@@ -1 +0,0 @@
-# build-for-debian8.patch
diff --git a/debian/rules b/debian/rules
index 5795643..5d4c73d 100755
--- a/debian/rules
+++ b/debian/rules
@@ -9,7 +9,16 @@ override_dh_shlibdeps:
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
override_dh_auto_configure:
- dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-ipc --with-systemdsystemunitdir=/lib/systemd/system --enable-manuals
+ dh_auto_configure -- \
+ --enable-manuals \
+ --with-systemdsystemunitdir=/lib/systemd/system \
+ --with-bladerf \
+ --with-ipc \
+ --with-lms \
+ --with-mstrx \
+ --with-uhd \
+ --with-usrp1 \
+ $(NULL)
override_dh_strip:
dh_strip --dbg-package=osmo-trx-dbg
diff --git a/doc/examples/Makefile.am b/doc/examples/Makefile.am
index 0b06130..ac87457 100644
--- a/doc/examples/Makefile.am
+++ b/doc/examples/Makefile.am
@@ -1,3 +1,15 @@
+# all config examples must be listed here unconditionally, so that
+# all of them end up in the release tarball (see OS#6349)
+EXTRA_DIST = \
+ osmo-trx-uhd/osmo-trx-limesdr.cfg \
+ osmo-trx-uhd/osmo-trx-usrp_b200.cfg \
+ osmo-trx-uhd/osmo-trx-uhd.cfg \
+ osmo-trx-uhd/osmo-trx-umtrx.cfg \
+ osmo-trx-lms/osmo-trx-limesdr.cfg \
+ osmo-trx-lms/osmo-trx-lms.cfg \
+ osmo-trx-ipc/osmo-trx-ipc.cfg \
+ $(NULL)
+
OSMOCONF_FILES =
osmoconfdir = $(sysconfdir)/osmocom
@@ -19,7 +31,6 @@ OSMOCONF_FILES += osmo-trx-ipc/osmo-trx-ipc.cfg
endif
osmoconf_DATA = $(OSMOCONF_FILES)
-EXTRA_DIST = $(OSMOCONF_FILES)
CFG_FILES = find $(srcdir) -type f -name '*.cfg*' | sed -e 's,^$(srcdir),,'
diff --git a/doc/examples/osmo-trx-ipc/osmo-trx-ipc.cfg b/doc/examples/osmo-trx-ipc/osmo-trx-ipc.cfg
index e3047c0..d66f09a 100644
--- a/doc/examples/osmo-trx-ipc/osmo-trx-ipc.cfg
+++ b/doc/examples/osmo-trx-ipc/osmo-trx-ipc.cfg
@@ -1,9 +1,11 @@
log stderr
logging filter all 1
logging color 1
+ logging print category-hex 0
logging print category 1
- logging timestamp 1
- logging print file basename
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
logging level set-all notice
!
line vty
diff --git a/doc/examples/osmo-trx-lms/osmo-trx-limesdr.cfg b/doc/examples/osmo-trx-lms/osmo-trx-limesdr.cfg
index ae55efe..8989565 100644
--- a/doc/examples/osmo-trx-lms/osmo-trx-limesdr.cfg
+++ b/doc/examples/osmo-trx-lms/osmo-trx-limesdr.cfg
@@ -1,9 +1,11 @@
log stderr
logging filter all 1
logging color 1
+ logging print category-hex 0
logging print category 1
- logging timestamp 1
- logging print file basename
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
logging level set-all notice
!
line vty
diff --git a/doc/examples/osmo-trx-uhd/osmo-trx-limesdr.cfg b/doc/examples/osmo-trx-uhd/osmo-trx-limesdr.cfg
index ae55efe..8989565 100644
--- a/doc/examples/osmo-trx-uhd/osmo-trx-limesdr.cfg
+++ b/doc/examples/osmo-trx-uhd/osmo-trx-limesdr.cfg
@@ -1,9 +1,11 @@
log stderr
logging filter all 1
logging color 1
+ logging print category-hex 0
logging print category 1
- logging timestamp 1
- logging print file basename
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
logging level set-all notice
!
line vty
diff --git a/doc/examples/osmo-trx-uhd/osmo-trx-umtrx.cfg b/doc/examples/osmo-trx-uhd/osmo-trx-umtrx.cfg
index 1468e93..6af7844 100644
--- a/doc/examples/osmo-trx-uhd/osmo-trx-umtrx.cfg
+++ b/doc/examples/osmo-trx-uhd/osmo-trx-umtrx.cfg
@@ -1,9 +1,11 @@
log stderr
logging filter all 1
logging color 1
+ logging print category-hex 0
logging print category 1
- logging timestamp 1
- logging print file basename
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
logging level set-all notice
!
line vty
diff --git a/doc/examples/osmo-trx-uhd/osmo-trx-usrp_b200.cfg b/doc/examples/osmo-trx-uhd/osmo-trx-usrp_b200.cfg
index 9eeb395..87aed57 100644
--- a/doc/examples/osmo-trx-uhd/osmo-trx-usrp_b200.cfg
+++ b/doc/examples/osmo-trx-uhd/osmo-trx-usrp_b200.cfg
@@ -1,9 +1,11 @@
log stderr
logging filter all 1
logging color 1
+ logging print category-hex 0
logging print category 1
- logging timestamp 1
- logging print file basename
+ logging timestamp 0
+ logging print file basename last
+ logging print level 1
logging level set-all notice
!
line vty
diff --git a/doc/manuals/Makefile.am b/doc/manuals/Makefile.am
index 7bf1f58..ed7353d 100644
--- a/doc/manuals/Makefile.am
+++ b/doc/manuals/Makefile.am
@@ -1,6 +1,5 @@
EXTRA_DIST = osmotrx-usermanual.adoc \
osmotrx-usermanual-docinfo.xml \
- osmotrx-vty-reference.xml \
chapters \
vty
@@ -9,8 +8,24 @@ if BUILD_MANUALS
ASCIIDOC_DEPS = $(srcdir)/chapters/*.adoc
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.asciidoc.inc
- VTY_REFERENCE = osmotrx-vty-reference.xml
- include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
+ VARIANTS = $(NULL)
+
+if DEVICE_UHD
+ VARIANTS += uhd
+endif
+if DEVICE_USRP1
+ VARIANTS += usrp1
+endif
+if DEVICE_LMS
+ VARIANTS += lms
+endif
+if DEVICE_IPC
+ VARIANTS += ipc
+endif
+
+ # This is a significantly modified, multi-target adopted copy of
+ # $(OSMO_GSM_MANUALS_DIR)/build/Makefile.vty-reference.inc
+ include $(srcdir)/vty/Makefile.vty-reference.inc
OSMO_REPOSITORY = osmo-trx
include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.common.inc
diff --git a/doc/manuals/chapters/code-architecture.adoc b/doc/manuals/chapters/code-architecture.adoc
index f1feb2c..5311dbe 100644
--- a/doc/manuals/chapters/code-architecture.adoc
+++ b/doc/manuals/chapters/code-architecture.adoc
@@ -9,7 +9,7 @@ digraph hierarchy {
node[shape=record,style=filled,fillcolor=gray95]
edge[dir=back, arrowtail=empty]
-2[label = "{Transceiver|+ constructor()\l+ destructor()\l+ init()\l+ numChans()\l+ receiveFIFO()\l+ setSignalHandler()}"]
+2[label = "{Transceiver|+ constructor()\l+ destructor()\l+ init()\l+ numChans()\l+ receiveFIFO()\l+ setSignalHandler()\l}"]
3[label = "{RadioInterface|...}"]
4[label = "{RadioInterfaceResamp|...}"]
5[label = "{RadioInterfaceMulti|...}"]
@@ -17,6 +17,7 @@ edge[dir=back, arrowtail=empty]
7[label = "{UHDDevice|...}"]
8[label = "{LMSDevice|...}"]
9[label = "{USRPDevice|...}"]
+10[label = "{IPCDevice|...}"]
2->3[arrowtail=odiamond]
3->4[constraint=false]
@@ -25,6 +26,7 @@ edge[dir=back, arrowtail=empty]
6->7
6->8
6->9
+6->10
}
----
diff --git a/doc/manuals/chapters/configuration.adoc b/doc/manuals/chapters/configuration.adoc
index a194537..e1323ef 100644
--- a/doc/manuals/chapters/configuration.adoc
+++ b/doc/manuals/chapters/configuration.adoc
@@ -1,4 +1,4 @@
-== Configuring OsmTRX
+== Configuring OsmoTRX
OsmoTRX will read the configuration at startup time and configure the
transceiver accordingly after validating the configuration.
diff --git a/doc/manuals/chapters/ipc_if.adoc b/doc/manuals/chapters/ipc_if.adoc
new file mode 100644
index 0000000..9994693
--- /dev/null
+++ b/doc/manuals/chapters/ipc_if.adoc
@@ -0,0 +1,301 @@
+[[ipc_if]]
+== osmo-trx-ipc IPC Interface
+
+This interface is the one used by _osmo_trx_ipc_ backend to communicate to a
+third party process in charge of driving the lowest layer device-specific bits
+(from now on the Driver).
+
+It consists of a set of Unix Domain (UD) sockets for the control plane, plus a
+shared memory region for the data plane.
+
+Related code can be found in the
+https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc[Transceiver52M/device/ipc/]
+directory in _osmo-trx.git_.
+
+If you are a potential driver implementator, the
+various primitives and data structures are publicly available in header file
+https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/shm.h[Transceiver52M/device/ipc/shm.h].
+
+=== Control plane
+
+Control plane protocol is transmitted over Unix Domain (UD) sockets using
+message based primitives. Each primitive has a type identified by an integer,
+and each type of primitive has a number of extra attributes attached to it. The
+IPC interface consists of 2 types of UD sockets:
+
+* _Master_ UD socket: One per osmo-trx-ipc process.
+
+* _Channel_ UD socket: One for each channel managed by osmo-trx-ipc process.
+
+The _Driver_ is in all cases expected to take the server role when creating UD
+sockets, while _osmo-trx-ipc_ takes the client role and connects to sockets
+provided by the driver.
+
+=== Master UD socket
+
+During startup, _osmo-trx-ipc_ will try connecting to the _Driver_ Master UD
+socket located in the path provided by its own (VTY) configuration. As a result,
+it means the _Driver_ process must be running and listening on the Master UD
+socket before _osmo-trx-ipc_ is started, otherwise _osmo-trx-ipc_ will fail and
+exit.
+
+Once connected, _osmo-trx-ipc_ will submit a `GREETING_REQ` message primitive
+announcing the maximum supported protocol version (first version ever is `1`,
+increasing over time).
+
+The _Driver_ shall then answer in `GREETING_CNF` message primitive with its own
+maximum supported version (`<=` version received), providing 0 if none is
+supported.
+
+If _osmo-trx-ipc_ receives back the requested version, then both sides agreed
+on the protocol version to use.
+If _osmo-trx-ipc_ receives back a lower version, it shall decide to continue
+with version negotiation using a lower version, until a supported version or 0
+is received. If finally 0 is received, _osmo-trx-ipc_ will disconnect and exit
+with failure.
+
+Once the version is negotiated (`v1` as of current date), _osmo-trx-ipc_ will
+ask for device information and available characeristics to the _Driver_ using
+the `INFO_REQ` message primitive.
+
+The _Driver_ shall then answer with a `INFO_CNF` message
+containing information, such as:
+
+* String containing device description
+
+* Available reference clocks,
+
+* {rx,tx} I/Q scaling factors
+
+* Maximum number of channels supported
+
+* for each channel:
+
+** List of available {rx,tx} paths/antennas.
+
+** {min,max}{rx,tx} gains
+
+** Nominal transmit power
+
+All the information received from the _Driver_ during `INFO_CNF` will be used by
+_osmo-trx-ipc_ to decide whether it can fullfil the requested configuration from
+the user, and proceed to open the device, or exit with a failure (for instance
+number of channels, referece clock or tx/rx antenna selected by the user cannot
+be fullfilled).
+
+_osmo-trx-ipc_ will then proceed to open the device and do an initial
+configuration using an `OPEN_REQ` message, where it will provide the _Driver_
+with the desired selected configuration (such as number of channels, rx/tx
+paths, clock reference, bandwidth filters, etc.).
+
+The _Driver_ shall then configure the device and send back a `OPEN_CNF` with:
+
+* `return_code` integer attribute set to `0` on success or `!0` on error.
+
+* Name of the Posix Shared Memory region where data plane is going to be
+transmitted.
+
+* One path for each channel, containing the just-created UD socket to manage
+that channel (for instance by taking Master UD socket path and appending
+`_$chan_idx`).
+
+* Path Delay: this is the loopback path delay in samples (= used as a timestamp
+offset internally by _osmo-trx-ipc_), this value contains the analog delay as
+well as the delay introduced by the digital filters in the fpga in the sdr
+devices, and is therefore device type and bandwidth/sample rate dependant. This
+can not be omitted, wrong values will lead to a _osmo-trx-ipc_ that just doesn't
+detect any bursts.
+
+Finally, _osmo-trx-ipc_ will connect to each channel's UD socket (see next
+section).
+
+Upon _osmo-trx-ipc_ closing the UD master socket connection, the _Driver_ shall
+go into _closed_ state: stop all processing and instruct the device to power
+off.
+
+TIP: See
+https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/shm.h[Transceiver52M/device/ipc/shm.h]
+for the detailed definition of all the related message primitives and data
+types for this socket.
+
+=== Channel UD Socket
+
+This socket can be used by _osmo-trx-ipc_ to start/stop data plane processing or
+change channel's parameters such as Rx/Tx Frequency, Rx/Tx gains, etc.
+
+A channel can be either in _started_ or _stopped_ state. When a channel is
+created (during `OPEN_REQ` in the Master UD Socket), it's by default in
+_stopped_ state. `START_REQ` and `STOP_REQ` messages control this state, and
+eventual failures can be reported through `START_CNF` and `STOP_CNF` by the
+_Driver_.
+
+The message `START_REQ` instructs the _Driver_ to start processing data in the
+data plane. Similary, `STOP_REQ` instructs the _Driver_ to stop processing data
+in the data plane.
+
+Some parameters are usually changed only when the channel is in stopped mode,
+for instance Rx/Tx Frequency.
+
+TIP: See
+https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/shm.h[Transceiver52M/device/ipc/shm.h]
+for the detailed definition of all the related message primitives and data
+types for this socket.
+
+=== Data Plane
+
+Data plane protocol is implemented by means of a ring buffer structure on top of
+Posix Shared Memory (see `man 7 shm_overview`) between _osmo-trx-ipc_ process
+and the _Driver_.
+
+The Posix Shared Memory region is created and its memory structure prepared by
+the _Driver_ and its name shared with _osmo-trx-ipc_ during _OPEN_CNF_ message
+in the Master UD Socket from the Control Plane. Resource allocation for the
+shared memory area and cleanup is up to the ipc server, as is mutex
+initialization for the buffers.
+
+==== Posix Shared Memory structure
+
+[[fig-shm-structure]]
+.General overview of Posix Shared Memory structure
+[graphviz]
+----
+digraph hierarchy {
+node[shape=record,style=filled,fillcolor=gray95]
+edge[dir=back, arrowtail=empty]
+
+SHM[label = "{Posix Shared Memory region|+ num_chans\l+ Channels[]\l}"]
+CHAN0[label = "{Channel 0|...}"]
+CHAN1[label = "{Channel 1|...}"]
+CHANN[label = "{Channel ...|}"]
+STREAM0_UL[label = "{UL Stream|+ semaphore\l+ read_next\l+ write_next\l+ buffer_size /* In samples */\l+ num_buffers\l+ sample_buffers[]\l}"]
+STREAM0_DL[label = "{DL Stream|+ semaphore\l+ read_next\l+ write_next\l+ buffer_size /* In samples */\l+ num_buffers\l+ sample_buffers[]\l}"]
+STREAM1_UL[label = "{UL Stream|...}"]
+STREAM1_DL[label = "{DL Stream|...}"]
+STREAMN_UL[label = "{UL Stream|...}"]
+STREAMN_DL[label = "{DL Stream|...}"]
+BUF_0DL0[label = "{DL Sample Buffer 0|+ timestamp\l+ buffer_size /* In samples */\l+ samples[] = [16bit I + 16bit Q,...]\l}"]
+BUF_0DLN[label = "{DL Sample Buffer ....|...}"]
+BUF_0UL0[label = "{UL Sample Buffer 0|+ timestamp\l+ buffer_size /* In samples */\l+ samples[] = [16bit I + 16bit Q,...]\l}"]
+BUF_0ULN[label = "{UL Sample Buffer ...|...}"]
+
+SHM->CHAN0
+SHM->CHAN1
+SHM->CHANN
+
+CHAN0->STREAM0_DL
+CHAN0->STREAM0_UL
+STREAM0_DL->BUF_0DL0
+STREAM0_DL->BUF_0DLN
+STREAM0_UL->BUF_0UL0
+STREAM0_UL->BUF_0ULN
+
+CHAN1->STREAM1_UL
+CHAN1->STREAM1_DL
+
+CHANN->STREAMN_UL
+CHANN->STREAMN_DL
+}
+----
+
+The Posix Shared Memory region contains an array of _Channels_.
+
+Each _Channel_ contains 2 Streams:
+
+* Downlink _Stream_
+
+* Uplink _Stream_
+
+Each _Stream_ handles a ring buffer, which is implemented as:
+
+* An array of pointers to _Sample Buffer_ structures.
+
+* Variables containing the number of buffers in the array, as well as the
+maximum size in samples for each Sample Buffer.
+
+* Variables containing `next_read` and `next_write` _Sample Buffer_ (its index
+in the array of pointers).
+
+* Unnamed Posix semaphores to do the required locking while using the ring
+buffer.
+
+Each _Sample Buffer_ contains:
+
+* A `timestamp` variable, containing the position in the stream of the first
+sample in the buffer
+
+* A `data_len` variable, containing the amount of samples available to process
+in the buffer
+
+* An array of samples of size specified by the stream struct it is part of.
+
+==== Posix Shared Memory format
+
+The Posix Shared memory region shall be formatted applying the following
+considerations:
+
+* All pointers in the memory region are encoded as offsets from the start
+address of the region itself, to allow different processes with different
+address spaces to decode them.
+
+* All structs must be force-aligned to 8 bytes
+
+* Number of buffers must be power of 2 (2,4,8,16,...) - 4 appears to be plenty
+
+* IQ samples format: One (complex) sample consists of 16bit i + 16bit q, so the
+buffer size is number of IQ pairs.
+
+* A reasonable per-buffer size (in samples) is 2500, since this happens to be
+the ususal TX (downlink) buffer size used by _osmo-trx-ipc_ with the b210 (rx
+over-the-wire packet size for the b210 is 2040 samples, so the larger value of
+both is convenient).
+
+TIP: See
+https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/shm.h[Transceiver52M/device/ipc/shm.h]
+for the detailed definition of all the objects being part of the Posix Shared
+memory region structure
+
+==== Posix Shared Memory procedures
+
+The queue in the shared memory area is not supposed to be used for actual
+buffering of data, only for exchange, so the general expectation is that it is
+mostly empty. The only exception to that might be minor processing delays, and
+during startup.
+
+Care must be taken to ensure that only timed waits for the mutex protecting it
+and the condition variables are used, in order to ensure that no deadlock occurs
+should the other side die/quit.
+
+Thread cancellation should be disabled during reads/writes from/to the queue. In
+general a timeout can be considered a non recoverable error during regular
+processing after startup, at least with the current timeout value of one second.
+
+Should over- or underflows occur a corresponding message should be sent towards
+_osmo-trx-ipc_.
+
+Upon **read** of `N` samples, the reader does something like:
+
+. Acquire the semaphore in the channel's stream object.
+
+. Read `stream->next_read`, if `next_read==next_write`, become blocked in
+another sempahore (unlocking the previous one) until writer signals us, then
+`buff = stream->buffers[next_read]`
+
+. Read `buff->data_len` samples, reset the buffer data (`data_len=0`),
+increment `next_read` and if read samples is `<N`, continue with next buffer
+until `next_read==next_write`, then block again or if timeout elapsed, then we
+reach conditon buffer underflow and `return len < N`.
+
+. Release the semaphore
+
+Upon **write** of `N` samples, the writer does something like:
+
+. Acquire the semapore in the channel's stream object.
+
+. Write samples to `buff = stream->buffers[next_write]`. If `data_len!=0`,
+signal `buffer_overflow` (increase field in stream object) and probably
+increase next_read`.
+
+. Increase `next_write`.
+
+. If `next_write` was `== next_read`, signal the reader through the other
+semaphore that it can continue reading. \ No newline at end of file
diff --git a/doc/manuals/chapters/trx-backends.adoc b/doc/manuals/chapters/trx-backends.adoc
index 78bc45d..021c6f4 100644
--- a/doc/manuals/chapters/trx-backends.adoc
+++ b/doc/manuals/chapters/trx-backends.adoc
@@ -71,3 +71,103 @@ with a memory buffer. In this mode, data written to the USRP is actually stored
in a buffer, and read commands to the USRP simply pull data from this buffer.
This was very useful in early testing, and still may be useful in testing basic
Transceiver and radioInterface functionality.
+
+
+[[backend_ipc]]
+=== `osmo-trx-ipc` Inter Process Communication backend
+
+This OsmoTRX model provides its own Inter Process Communication (IPC) interface
+to drive the radio device driver (from now on the Driver), allowing for third
+party processes to implement the lowest layer device-specific bits without being
+affected by copyleft licenses of OsmoTRX.
+
+For more information on such interface, see section <<ipc_if>>.
+
+[[fig-backend-ipc]]
+.Architecture with _osmo-trx-ipc_ and its IPC _Driver_
+[graphviz]
+----
+digraph G {
+ rankdir=LR;
+ MS0 [label="MS"];
+ MS1 [label="MS"];
+ OsmoTRX [label="osmo-trx-ipc", color=red];
+ BTS;
+
+ subgraph cluster_ipc_driver {
+ label = "IPC Driver";
+ color=red;
+ RE [label = "Radio Equipment"];
+ REC [label="Radio Equipment Controller"];
+ RE->REC;
+ }
+
+ REC->OsmoTRX [label="IPC Interface", color=red];
+
+ MS0->RE [label="Um"];
+ MS1->RE [label="Um"];
+ OsmoTRX->BTS [label="bursts over UDP"];
+
+}
+----
+
+A sample config file for this OsmoTRX model can be found in _osmo-trx.git_ https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/doc/examples/osmo-trx-ipc/osmo-trx-ipc.cfg[doc/examples/osmo-trx-ipc/osmo-trx-ipc.cfg]
+
+In the config file, the following VTY command can be used to set up the IPC UD Master Socket _osmo-trx-ipc_ will connect to at startup:
+
+.Example: _osmo-trx-ipc_ will connect to UD Master Socket /tmp/ipc_sock0 upon startup
+----
+dev-args ipc_msock=/tmp/ipc_sock0
+----
+
+==== ipc-device-test
+
+When built with `--with-ipc --with-uhd` configure options, _osmo-trx.git_ will
+build the test program called _ipc-driver-test_. This program implements the
+_Driver_ side of the osmo-trx-ipc interface (see <<ipc_if>> for more
+information) on one side, and also interacts internally with UHD (eg B210 as
+when using osmo-trx-uhd).
+
+You can use this small program as a reference to:
+
+* Test and experiment with _osmo-trx-ipc_.
+
+* Write your own IPC _Driver_ connecting to osmo-trx-ipc.
+
+[[fig-backend-ipc-device-test]]
+.Architecture with _osmo-trx-ipc_ and ipc-device-test as IPC _Driver_
+[graphviz]
+----
+digraph G {
+ rankdir=LR;
+ MS0 [label="MS"];
+ MS1 [label="MS"];
+ SDR;
+ ipc_device_test[label = "ipc-device-test", color=red];
+ OsmoTRX [label="osmo-trx-ipc", color=red];
+ BTS;
+
+ MS0->SDR [label="Um"];
+ MS1->SDR [label="Um"];
+ SDR->ipc_device_test [label="UHD"];
+ ipc_device_test->OsmoTRX [label="IPC Interface", color=red];
+ OsmoTRX->BTS [label="bursts over UDP"];
+}
+----
+
+The code for this app is found here:
+
+* https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/ipc-driver-test.h[Transceiver52M/device/ipc/ipc-driver-test.h]
+
+* https://gitea.osmocom.org/cellular-infrastructure/osmo-trx/src/branch/master/Transceiver52M/device/ipc/ipc-driver-test.c[Transceiver52M/device/ipc/ipc-driver-test.c]
+
+Those files use the server-side (_Driver_ side) code to operate the Posix Shared
+Memory region implemented in files `shm.c`, `shm.h`, `ipc_shm.c` and `ipc_shm.h`
+in the same directory.
+
+Most of the code in that same directory is deliverately released under a BSD
+license (unlike most of _osmo-trx.git_), allowing third parties to reuse/recycle
+the code on their implemented _Driver_ program no matter it being proprietary or
+under an open license. However, care must be taken with external dependencies,
+as for instance shm.c uses the talloc memory allocator, which is GPL licensed
+and hence cannot be used in a proprietary driver. \ No newline at end of file
diff --git a/doc/manuals/osmotrx-usermanual.adoc b/doc/manuals/osmotrx-usermanual.adoc
index 2d1caad..b768306 100644
--- a/doc/manuals/osmotrx-usermanual.adoc
+++ b/doc/manuals/osmotrx-usermanual.adoc
@@ -35,6 +35,8 @@ include::./common/chapters/vty_cpu_sched.adoc[]
include::./common/chapters/trx_if.adoc[]
+include::{srcdir}/chapters/ipc_if.adoc[]
+
include::./common/chapters/port_numbers.adoc[]
include::./common/chapters/bibliography.adoc[]
diff --git a/doc/manuals/vty/Makefile.vty-reference.inc b/doc/manuals/vty/Makefile.vty-reference.inc
new file mode 100644
index 0000000..d86f638
--- /dev/null
+++ b/doc/manuals/vty/Makefile.vty-reference.inc
@@ -0,0 +1,37 @@
+DOCBOOKS = $(foreach v,$(VARIANTS),vty/osmotrx-$(v)-vty-reference.xml)
+DOCBOOKS_DEPS = $(DOCBOOKS) $(addsuffix .inc,$(DOCBOOKS))
+INC_DIR = $(abspath $(builddir)/vty)
+
+include $(OSMO_GSM_MANUALS_DIR)/build/Makefile.docbook.inc
+
+CLEAN_FILES += $(DOCBOOKS_DEPS)
+CLEAN_FILES += $(addsuffix .inc.gen,$(DOCBOOKS))
+CLEAN_FILES += $(addsuffix .inc.merged,$(DOCBOOKS))
+
+$(INC_DIR):
+ mkdir -p $@
+
+vty/osmotrx-%-vty-reference.xml: $(top_builddir)/Transceiver52M/osmo-trx-% $(INC_DIR)
+ sed -e "s|@@GENERATED@@|$@.inc|" \
+ -e "s|@@VARIANT@@|$(notdir $<)|" \
+ -e "s|@@REV_NUMBER@@|$(VERSION)|" \
+ -e "s|@@REV_DATE@@|$(shell date +"%dth %B %Y")|" \
+ -e "s|@@CR_YEAR@@|$(shell date +"%Y")|" \
+ $(srcdir)/vty/osmotrx-vty-reference.xml > $@
+
+vty/osmotrx-%-vty-reference.xml.inc: $(top_builddir)/Transceiver52M/osmo-trx-% \
+ $(OSMO_GSM_MANUALS_DIR)/common/vty_additions.xml \
+ $(OSMO_GSM_MANUALS_DIR)/common/chapters/vty.xml \
+ $(OSMO_GSM_MANUALS_DIR)/vty_reference.xsl \
+ $(srcdir)/vty/*.xml $(INC_DIR)
+ # a) Invoke osmo-trx-% to generate the list of commands first
+ $< --vty-ref-mode default --vty-ref-xml > "$@.gen"
+ # ... filter garbage potentially printed by libraries to stdout
+ sed -i '/^<vtydoc/,$$!d' "$@.gen"
+ # b) Merge the result of a) with global and local additions
+ $(OSMO_GSM_MANUALS_DIR)/build/vty_reference_combine.sh \
+ $(realpath $(OSMO_GSM_MANUALS_DIR)/merge_doc.xsl) "$@.gen" \
+ $(OSMO_GSM_MANUALS_DIR)/common/vty_additions.xml \
+ $(srcdir)/vty/*additions*.xml > "$@.merged"
+ # c) Convert the result of b) into a valid docbook
+ xsltproc $(OSMO_GSM_MANUALS_DIR)/vty_reference.xsl "$@.merged" > $@
diff --git a/doc/manuals/osmotrx-vty-reference.xml b/doc/manuals/vty/osmotrx-vty-reference.xml
index 4291929..4d2a23d 100644
--- a/doc/manuals/osmotrx-vty-reference.xml
+++ b/doc/manuals/vty/osmotrx-vty-reference.xml
@@ -6,7 +6,7 @@
<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML 5.0//EN"
"http://docbook.org/xml/5.0/dtd/docbook.dtd" [
<!ENTITY chapter-vty SYSTEM "./common/chapters/vty.xml" >
-<!ENTITY sections-vty SYSTEM "generated/docbook_vty.xml" >
+<!ENTITY sections-vty SYSTEM "@@GENERATED@@" >
]>
<book>
@@ -18,12 +18,19 @@
<authorinitials>pe</authorinitials>
<revremark>Initial</revremark>
</revision>
+ <revision>
+ <revnumber>v2</revnumber>
+ <date>@@REV_DATE@@</date>
+ <authorinitials>s.f.m.c.</authorinitials>
+ <revremark>Automatic build (@@REV_NUMBER@@)</revremark>
+ </revision>
</revhistory>
<title>OsmoTRX VTY Reference</title>
+ <subtitle>@@VARIANT@@</subtitle>
<copyright>
- <year>2018</year>
+ <year>@@CR_YEAR@@</year>
</copyright>
<legalnotice>
diff --git a/doc/manuals/vty/trx_vty_reference.xml b/doc/manuals/vty/trx_vty_reference.xml
deleted file mode 100644
index ff44078..0000000
--- a/doc/manuals/vty/trx_vty_reference.xml
+++ /dev/null
@@ -1,1406 +0,0 @@
-<vtydoc xmlns='urn:osmocom:xml:libosmocore:vty:doc:1.0'>
- <node id='_common_cmds_'>
- <name>Common Commands</name>
- <description>These commands are available on all VTY nodes. They are listed here only once, to unclutter the VTY reference.</description>
- <command id='help'>
- <params>
- <param name='help' doc='Description of the interactive help system' />
- </params>
- </command>
- <command id='list'>
- <params>
- <param name='list' doc='Print command list' />
- </params>
- </command>
- <command id='write terminal'>
- <params>
- <param name='write' doc='Write running configuration to memory, network, or terminal' />
- <param name='terminal' doc='Write to terminal' />
- </params>
- </command>
- <command id='write file [PATH]'>
- <params>
- <param name='write' doc='Write running configuration to memory, network, or terminal' />
- <param name='file' doc='Write to configuration file' />
- <param name='[PATH]' doc='Set file path to store the config, or replace if already exists' />
- </params>
- </command>
- <command id='write memory'>
- <params>
- <param name='write' doc='Write running configuration to memory, network, or terminal' />
- <param name='memory' doc='Write configuration to the file (same as write file)' />
- </params>
- </command>
- <command id='write'>
- <params>
- <param name='write' doc='Write running configuration to memory, network, or terminal' />
- </params>
- </command>
- <command id='show running-config'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='running-config' doc='running configuration' />
- </params>
- </command>
- <command id='exit'>
- <params>
- <param name='exit' doc='Exit current mode and down to previous mode' />
- </params>
- </command>
- <command id='end'>
- <params>
- <param name='end' doc='End current mode and change to enable mode.' />
- </params>
- </command>
- </node>
- <node id='view'>
- <name>view</name>
- <command id='show version'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='version' doc='Displays program version' />
- </params>
- </command>
- <command id='show online-help'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='online-help' doc='Online help' />
- </params>
- </command>
- <command id='enable'>
- <params>
- <param name='enable' doc='Turn on privileged mode command' />
- </params>
- </command>
- <command id='terminal length &lt;0-512&gt;'>
- <params>
- <param name='terminal' doc='Set terminal line parameters' />
- <param name='length' doc='Set number of lines on a screen' />
- <param name='&lt;0-512&gt;' doc='Number of lines on screen (0 for no pausing)' />
- </params>
- </command>
- <command id='terminal no length'>
- <params>
- <param name='terminal' doc='Set terminal line parameters' />
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='length' doc='Set number of lines on a screen' />
- </params>
- </command>
- <command id='who'>
- <params>
- <param name='who' doc='Display who is on vty' />
- </params>
- </command>
- <command id='show history'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='history' doc='Display the session command history' />
- </params>
- </command>
- <command id='logging enable'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='enable' doc='Enables logging to this vty' />
- </params>
- </command>
- <command id='logging disable'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='disable' doc='Disables logging to this vty' />
- </params>
- </command>
- <command id='logging filter all (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='filter' doc='Filter log messages' />
- <param name='all' doc='Do you want to log all messages?' />
- <param name='0' doc='Only print messages matched by other filters' />
- <param name='1' doc='Bypass filter and print all messages' />
- </params>
- </command>
- <command id='logging color (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='color' doc='Configure color-printing for log messages' />
- <param name='0' doc='Don&apos;t use color for printing messages' />
- <param name='1' doc='Use color for printing messages' />
- </params>
- </command>
- <command id='logging timestamp (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='timestamp' doc='Configure log message timestamping' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with current timestamp' />
- </params>
- </command>
- <command id='logging print extended-timestamp (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='extended-timestamp' doc='Configure log message timestamping' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with current timestamp with YYYYMMDDhhmmssnnn' />
- </params>
- </command>
- <command id='logging print category (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='category' doc='Configure log message' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with category/subsystem name' />
- </params>
- </command>
- <command id='logging print category-hex (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='category-hex' doc='Configure log message' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with category/subsystem nr in hex (&apos;&lt;000b&gt;&apos;)' />
- </params>
- </command>
- <command id='logging print level (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='level' doc='Configure log message' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with the log level name' />
- </params>
- </command>
- <command id='logging print file (0|1|basename) [last]'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='file' doc='Configure log message' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with the source file and line' />
- <param name='basename' doc='Prefix each log message with the source file&apos;s basename (strip leading paths) and line' />
- <param name='[last]' doc='Log source file info at the end of a log line. If omitted, log source file info just before the log text.' />
- </params>
- </command>
- <command id='logging set-log-mask MASK'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='set-log-mask' doc='Set the logmask of this logging target' />
- <param name='MASK' doc='List of logging categories to log, e.g. &apos;abc:mno:xyz&apos;. Available log categories depend on the specific application, refer to the &apos;logging level&apos; command. Optionally add individual log levels like &apos;abc,1:mno,3:xyz,5&apos;, where the level numbers are LOGL_DEBUG=1 LOGL_INFO=3 LOGL_NOTICE=5 LOGL_ERROR=7 LOGL_FATAL=8' />
- </params>
- </command>
- <command id='logging level (main|trxclk|trxctrl|trxddl|trxdul|dev|devdrv|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='level' doc='Set the log level for a specified category' />
- <param name='main' doc='Main generic category' />
- <param name='trxclk' doc='TRX Master Clock' />
- <param name='trxctrl' doc='TRX CTRL interface' />
- <param name='trxddl' doc='TRX Data interface Downlink' />
- <param name='trxdul' doc='TRX CTRL interface Uplink' />
- <param name='dev' doc='Device/Driver specific code' />
- <param name='devdrv' doc='Logging from external device driver library implementing lower level specifics' />
- <param name='lglobal' doc='Library-internal global log family' />
- <param name='llapd' doc='LAPD in libosmogsm' />
- <param name='linp' doc='A-bis Intput Subsystem' />
- <param name='lmux' doc='A-bis B-Subchannel TRAU Frame Multiplex' />
- <param name='lmi' doc='A-bis Input Driver for Signalling' />
- <param name='lmib' doc='A-bis Input Driver for B-Channels (voice)' />
- <param name='lsms' doc='Layer3 Short Message Service (SMS)' />
- <param name='lctrl' doc='Control Interface' />
- <param name='lgtp' doc='GPRS GTP library' />
- <param name='lstats' doc='Statistics messages and logging' />
- <param name='lgsup' doc='Generic Subscriber Update Protocol' />
- <param name='loap' doc='Osmocom Authentication Protocol' />
- <param name='lss7' doc='libosmo-sigtran Signalling System 7' />
- <param name='lsccp' doc='libosmo-sigtran SCCP Implementation' />
- <param name='lsua' doc='libosmo-sigtran SCCP User Adaptation' />
- <param name='lm3ua' doc='libosmo-sigtran MTP3 User Adaptation' />
- <param name='lmgcp' doc='libosmo-mgcp Media Gateway Control Protocol' />
- <param name='ljibuf' doc='libosmo-netif Jitter Buffer' />
- <param name='lrspro' doc='Remote SIM protocol' />
- <param name='debug' doc='Log debug messages and higher levels' />
- <param name='info' doc='Log informational messages and higher levels' />
- <param name='notice' doc='Log noticeable messages and higher levels' />
- <param name='error' doc='Log error messages and higher levels' />
- <param name='fatal' doc='Log only fatal messages' />
- </params>
- </command>
- <command id='logging level set-all (debug|info|notice|error|fatal)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='level' doc='Set the log level for a specified category' />
- <param name='set-all' doc='Once-off set all categories to the given log level. There is no single command to take back these changes -- each category is set to the given level, period.' />
- <param name='debug' doc='Log debug messages and higher levels' />
- <param name='info' doc='Log informational messages and higher levels' />
- <param name='notice' doc='Log noticeable messages and higher levels' />
- <param name='error' doc='Log error messages and higher levels' />
- <param name='fatal' doc='Log only fatal messages' />
- </params>
- </command>
- <command id='logging level force-all (debug|info|notice|error|fatal)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='level' doc='Set the log level for a specified category' />
- <param name='force-all' doc='Globally force all logging categories to a specific level. This is released by the &apos;no logging level force-all&apos; command. Note: any &apos;logging level &lt;category&gt; &lt;level&gt;&apos; commands will have no visible effect after this, until the forced level is released.' />
- <param name='debug' doc='Log debug messages and higher levels' />
- <param name='info' doc='Log informational messages and higher levels' />
- <param name='notice' doc='Log noticeable messages and higher levels' />
- <param name='error' doc='Log error messages and higher levels' />
- <param name='fatal' doc='Log only fatal messages' />
- </params>
- </command>
- <command id='no logging level force-all'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='logging' doc='Configure logging' />
- <param name='level' doc='Set the log level for a specified category' />
- <param name='force-all' doc='Release any globally forced log level set with &apos;logging level force-all &lt;level&gt;&apos;' />
- </params>
- </command>
- <command id='logp (main|trxclk|trxctrl|trxddl|trxdul|dev|devdrv|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal) .LOGMESSAGE'>
- <params>
- <param name='logp' doc='Print a message on all log outputs; useful for placing markers in test logs' />
- <param name='main' doc='Main generic category' />
- <param name='trxclk' doc='TRX Master Clock' />
- <param name='trxctrl' doc='TRX CTRL interface' />
- <param name='trxddl' doc='TRX Data interface Downlink' />
- <param name='trxdul' doc='TRX CTRL interface Uplink' />
- <param name='dev' doc='Device/Driver specific code' />
- <param name='devdrv' doc='Logging from external device driver library implementing lower level specifics' />
- <param name='lglobal' doc='Library-internal global log family' />
- <param name='llapd' doc='LAPD in libosmogsm' />
- <param name='linp' doc='A-bis Intput Subsystem' />
- <param name='lmux' doc='A-bis B-Subchannel TRAU Frame Multiplex' />
- <param name='lmi' doc='A-bis Input Driver for Signalling' />
- <param name='lmib' doc='A-bis Input Driver for B-Channels (voice)' />
- <param name='lsms' doc='Layer3 Short Message Service (SMS)' />
- <param name='lctrl' doc='Control Interface' />
- <param name='lgtp' doc='GPRS GTP library' />
- <param name='lstats' doc='Statistics messages and logging' />
- <param name='lgsup' doc='Generic Subscriber Update Protocol' />
- <param name='loap' doc='Osmocom Authentication Protocol' />
- <param name='lss7' doc='libosmo-sigtran Signalling System 7' />
- <param name='lsccp' doc='libosmo-sigtran SCCP Implementation' />
- <param name='lsua' doc='libosmo-sigtran SCCP User Adaptation' />
- <param name='lm3ua' doc='libosmo-sigtran MTP3 User Adaptation' />
- <param name='lmgcp' doc='libosmo-mgcp Media Gateway Control Protocol' />
- <param name='ljibuf' doc='libosmo-netif Jitter Buffer' />
- <param name='lrspro' doc='Remote SIM protocol' />
- <param name='debug' doc='Log debug messages and higher levels' />
- <param name='info' doc='Log informational messages and higher levels' />
- <param name='notice' doc='Log noticeable messages and higher levels' />
- <param name='error' doc='Log error messages and higher levels' />
- <param name='fatal' doc='Log only fatal messages' />
- <param name='.LOGMESSAGE' doc='Arbitrary message to log on given category and log level' />
- </params>
- </command>
- <command id='show logging vty'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='logging' doc='Show current logging configuration' />
- <param name='vty' doc='Show current logging configuration for this vty' />
- </params>
- </command>
- <command id='show alarms'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='alarms' doc='Show current logging configuration' />
- </params>
- </command>
- <command id='show trx'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='trx' doc='Display information on the TRX' />
- </params>
- </command>
- <command id='show talloc-context (application|all) (full|brief|DEPTH)'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='talloc-context' doc='Show talloc memory hierarchy' />
- <param name='application' doc='Application&apos;s context' />
- <param name='all' doc='All contexts, if NULL-context tracking is enabled' />
- <param name='full' doc='Display a full talloc memory hierarchy' />
- <param name='brief' doc='Display a brief talloc memory hierarchy' />
- <param name='DEPTH' doc='Specify required maximal depth value' />
- </params>
- </command>
- <command id='show talloc-context (application|all) (full|brief|DEPTH) tree ADDRESS'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='talloc-context' doc='Show talloc memory hierarchy' />
- <param name='application' doc='Application&apos;s context' />
- <param name='all' doc='All contexts, if NULL-context tracking is enabled' />
- <param name='full' doc='Display a full talloc memory hierarchy' />
- <param name='brief' doc='Display a brief talloc memory hierarchy' />
- <param name='DEPTH' doc='Specify required maximal depth value' />
- <param name='tree' doc='Display only a specific memory chunk' />
- <param name='ADDRESS' doc='Chunk address (e.g. 0xdeadbeef)' />
- </params>
- </command>
- <command id='show talloc-context (application|all) (full|brief|DEPTH) filter REGEXP'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='talloc-context' doc='Show talloc memory hierarchy' />
- <param name='application' doc='Application&apos;s context' />
- <param name='all' doc='All contexts, if NULL-context tracking is enabled' />
- <param name='full' doc='Display a full talloc memory hierarchy' />
- <param name='brief' doc='Display a brief talloc memory hierarchy' />
- <param name='DEPTH' doc='Specify required maximal depth value' />
- <param name='filter' doc='Filter chunks using regular expression' />
- <param name='REGEXP' doc='Regular expression' />
- </params>
- </command>
- <command id='show stats'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='stats' doc='Show statistical values' />
- </params>
- </command>
- <command id='show stats level (global|peer|subscriber)'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='stats' doc='Show statistical values' />
- <param name='level' doc='Set the maximum group level' />
- <param name='global' doc='Show global groups only' />
- <param name='peer' doc='Show global and network peer related groups' />
- <param name='subscriber' doc='Show global, peer, and subscriber groups' />
- </params>
- </command>
- <command id='show asciidoc counters'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='asciidoc' doc='Asciidoc generation' />
- <param name='counters' doc='Generate table of all registered counters' />
- </params>
- </command>
- <command id='show rate-counters'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='rate-counters' doc='Show all rate counters' />
- </params>
- </command>
- </node>
- <node id='enable'>
- <name>enable</name>
- <command id='disable'>
- <params>
- <param name='disable' doc='Turn off privileged mode command' />
- </params>
- </command>
- <command id='configure terminal'>
- <params>
- <param name='configure' doc='Configuration from vty interface' />
- <param name='terminal' doc='Configuration terminal' />
- </params>
- </command>
- <command id='copy running-config startup-config'>
- <params>
- <param name='copy' doc='Copy configuration' />
- <param name='running-config' doc='Copy running config to... ' />
- <param name='startup-config' doc='Copy running config to startup config (same as write file)' />
- </params>
- </command>
- <command id='show startup-config'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='startup-config' doc='Contentes of startup configuration' />
- </params>
- </command>
- <command id='show version'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='version' doc='Displays program version' />
- </params>
- </command>
- <command id='show online-help'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='online-help' doc='Online help' />
- </params>
- </command>
- <command id='terminal length &lt;0-512&gt;'>
- <params>
- <param name='terminal' doc='Set terminal line parameters' />
- <param name='length' doc='Set number of lines on a screen' />
- <param name='&lt;0-512&gt;' doc='Number of lines on screen (0 for no pausing)' />
- </params>
- </command>
- <command id='terminal no length'>
- <params>
- <param name='terminal' doc='Set terminal line parameters' />
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='length' doc='Set number of lines on a screen' />
- </params>
- </command>
- <command id='who'>
- <params>
- <param name='who' doc='Display who is on vty' />
- </params>
- </command>
- <command id='show history'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='history' doc='Display the session command history' />
- </params>
- </command>
- <command id='terminal monitor'>
- <params>
- <param name='terminal' doc='Set terminal line parameters' />
- <param name='monitor' doc='Copy debug output to the current terminal line' />
- </params>
- </command>
- <command id='terminal no monitor'>
- <params>
- <param name='terminal' doc='Set terminal line parameters' />
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='monitor' doc='Copy debug output to the current terminal line' />
- </params>
- </command>
- <command id='logging enable'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='enable' doc='Enables logging to this vty' />
- </params>
- </command>
- <command id='logging disable'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='disable' doc='Disables logging to this vty' />
- </params>
- </command>
- <command id='logging filter all (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='filter' doc='Filter log messages' />
- <param name='all' doc='Do you want to log all messages?' />
- <param name='0' doc='Only print messages matched by other filters' />
- <param name='1' doc='Bypass filter and print all messages' />
- </params>
- </command>
- <command id='logging color (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='color' doc='Configure color-printing for log messages' />
- <param name='0' doc='Don&apos;t use color for printing messages' />
- <param name='1' doc='Use color for printing messages' />
- </params>
- </command>
- <command id='logging timestamp (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='timestamp' doc='Configure log message timestamping' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with current timestamp' />
- </params>
- </command>
- <command id='logging print extended-timestamp (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='extended-timestamp' doc='Configure log message timestamping' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with current timestamp with YYYYMMDDhhmmssnnn' />
- </params>
- </command>
- <command id='logging print category (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='category' doc='Configure log message' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with category/subsystem name' />
- </params>
- </command>
- <command id='logging print category-hex (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='category-hex' doc='Configure log message' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with category/subsystem nr in hex (&apos;&lt;000b&gt;&apos;)' />
- </params>
- </command>
- <command id='logging print level (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='level' doc='Configure log message' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with the log level name' />
- </params>
- </command>
- <command id='logging print file (0|1|basename) [last]'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='file' doc='Configure log message' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with the source file and line' />
- <param name='basename' doc='Prefix each log message with the source file&apos;s basename (strip leading paths) and line' />
- <param name='[last]' doc='Log source file info at the end of a log line. If omitted, log source file info just before the log text.' />
- </params>
- </command>
- <command id='logging set-log-mask MASK'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='set-log-mask' doc='Set the logmask of this logging target' />
- <param name='MASK' doc='List of logging categories to log, e.g. &apos;abc:mno:xyz&apos;. Available log categories depend on the specific application, refer to the &apos;logging level&apos; command. Optionally add individual log levels like &apos;abc,1:mno,3:xyz,5&apos;, where the level numbers are LOGL_DEBUG=1 LOGL_INFO=3 LOGL_NOTICE=5 LOGL_ERROR=7 LOGL_FATAL=8' />
- </params>
- </command>
- <command id='logging level (main|trxclk|trxctrl|trxddl|trxdul|dev|devdrv|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='level' doc='Set the log level for a specified category' />
- <param name='main' doc='Main generic category' />
- <param name='trxclk' doc='TRX Master Clock' />
- <param name='trxctrl' doc='TRX CTRL interface' />
- <param name='trxddl' doc='TRX Data interface Downlink' />
- <param name='trxdul' doc='TRX CTRL interface Uplink' />
- <param name='dev' doc='Device/Driver specific code' />
- <param name='devdrv' doc='Logging from external device driver library implementing lower level specifics' />
- <param name='lglobal' doc='Library-internal global log family' />
- <param name='llapd' doc='LAPD in libosmogsm' />
- <param name='linp' doc='A-bis Intput Subsystem' />
- <param name='lmux' doc='A-bis B-Subchannel TRAU Frame Multiplex' />
- <param name='lmi' doc='A-bis Input Driver for Signalling' />
- <param name='lmib' doc='A-bis Input Driver for B-Channels (voice)' />
- <param name='lsms' doc='Layer3 Short Message Service (SMS)' />
- <param name='lctrl' doc='Control Interface' />
- <param name='lgtp' doc='GPRS GTP library' />
- <param name='lstats' doc='Statistics messages and logging' />
- <param name='lgsup' doc='Generic Subscriber Update Protocol' />
- <param name='loap' doc='Osmocom Authentication Protocol' />
- <param name='lss7' doc='libosmo-sigtran Signalling System 7' />
- <param name='lsccp' doc='libosmo-sigtran SCCP Implementation' />
- <param name='lsua' doc='libosmo-sigtran SCCP User Adaptation' />
- <param name='lm3ua' doc='libosmo-sigtran MTP3 User Adaptation' />
- <param name='lmgcp' doc='libosmo-mgcp Media Gateway Control Protocol' />
- <param name='ljibuf' doc='libosmo-netif Jitter Buffer' />
- <param name='lrspro' doc='Remote SIM protocol' />
- <param name='debug' doc='Log debug messages and higher levels' />
- <param name='info' doc='Log informational messages and higher levels' />
- <param name='notice' doc='Log noticeable messages and higher levels' />
- <param name='error' doc='Log error messages and higher levels' />
- <param name='fatal' doc='Log only fatal messages' />
- </params>
- </command>
- <command id='logging level set-all (debug|info|notice|error|fatal)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='level' doc='Set the log level for a specified category' />
- <param name='set-all' doc='Once-off set all categories to the given log level. There is no single command to take back these changes -- each category is set to the given level, period.' />
- <param name='debug' doc='Log debug messages and higher levels' />
- <param name='info' doc='Log informational messages and higher levels' />
- <param name='notice' doc='Log noticeable messages and higher levels' />
- <param name='error' doc='Log error messages and higher levels' />
- <param name='fatal' doc='Log only fatal messages' />
- </params>
- </command>
- <command id='logging level force-all (debug|info|notice|error|fatal)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='level' doc='Set the log level for a specified category' />
- <param name='force-all' doc='Globally force all logging categories to a specific level. This is released by the &apos;no logging level force-all&apos; command. Note: any &apos;logging level &lt;category&gt; &lt;level&gt;&apos; commands will have no visible effect after this, until the forced level is released.' />
- <param name='debug' doc='Log debug messages and higher levels' />
- <param name='info' doc='Log informational messages and higher levels' />
- <param name='notice' doc='Log noticeable messages and higher levels' />
- <param name='error' doc='Log error messages and higher levels' />
- <param name='fatal' doc='Log only fatal messages' />
- </params>
- </command>
- <command id='no logging level force-all'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='logging' doc='Configure logging' />
- <param name='level' doc='Set the log level for a specified category' />
- <param name='force-all' doc='Release any globally forced log level set with &apos;logging level force-all &lt;level&gt;&apos;' />
- </params>
- </command>
- <command id='logp (main|trxclk|trxctrl|trxddl|trxdul|dev|devdrv|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal) .LOGMESSAGE'>
- <params>
- <param name='logp' doc='Print a message on all log outputs; useful for placing markers in test logs' />
- <param name='main' doc='Main generic category' />
- <param name='trxclk' doc='TRX Master Clock' />
- <param name='trxctrl' doc='TRX CTRL interface' />
- <param name='trxddl' doc='TRX Data interface Downlink' />
- <param name='trxdul' doc='TRX CTRL interface Uplink' />
- <param name='dev' doc='Device/Driver specific code' />
- <param name='devdrv' doc='Logging from external device driver library implementing lower level specifics' />
- <param name='lglobal' doc='Library-internal global log family' />
- <param name='llapd' doc='LAPD in libosmogsm' />
- <param name='linp' doc='A-bis Intput Subsystem' />
- <param name='lmux' doc='A-bis B-Subchannel TRAU Frame Multiplex' />
- <param name='lmi' doc='A-bis Input Driver for Signalling' />
- <param name='lmib' doc='A-bis Input Driver for B-Channels (voice)' />
- <param name='lsms' doc='Layer3 Short Message Service (SMS)' />
- <param name='lctrl' doc='Control Interface' />
- <param name='lgtp' doc='GPRS GTP library' />
- <param name='lstats' doc='Statistics messages and logging' />
- <param name='lgsup' doc='Generic Subscriber Update Protocol' />
- <param name='loap' doc='Osmocom Authentication Protocol' />
- <param name='lss7' doc='libosmo-sigtran Signalling System 7' />
- <param name='lsccp' doc='libosmo-sigtran SCCP Implementation' />
- <param name='lsua' doc='libosmo-sigtran SCCP User Adaptation' />
- <param name='lm3ua' doc='libosmo-sigtran MTP3 User Adaptation' />
- <param name='lmgcp' doc='libosmo-mgcp Media Gateway Control Protocol' />
- <param name='ljibuf' doc='libosmo-netif Jitter Buffer' />
- <param name='lrspro' doc='Remote SIM protocol' />
- <param name='debug' doc='Log debug messages and higher levels' />
- <param name='info' doc='Log informational messages and higher levels' />
- <param name='notice' doc='Log noticeable messages and higher levels' />
- <param name='error' doc='Log error messages and higher levels' />
- <param name='fatal' doc='Log only fatal messages' />
- <param name='.LOGMESSAGE' doc='Arbitrary message to log on given category and log level' />
- </params>
- </command>
- <command id='show logging vty'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='logging' doc='Show current logging configuration' />
- <param name='vty' doc='Show current logging configuration for this vty' />
- </params>
- </command>
- <command id='show alarms'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='alarms' doc='Show current logging configuration' />
- </params>
- </command>
- <command id='show trx'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='trx' doc='Display information on the TRX' />
- </params>
- </command>
- <command id='show talloc-context (application|all) (full|brief|DEPTH)'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='talloc-context' doc='Show talloc memory hierarchy' />
- <param name='application' doc='Application&apos;s context' />
- <param name='all' doc='All contexts, if NULL-context tracking is enabled' />
- <param name='full' doc='Display a full talloc memory hierarchy' />
- <param name='brief' doc='Display a brief talloc memory hierarchy' />
- <param name='DEPTH' doc='Specify required maximal depth value' />
- </params>
- </command>
- <command id='show talloc-context (application|all) (full|brief|DEPTH) tree ADDRESS'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='talloc-context' doc='Show talloc memory hierarchy' />
- <param name='application' doc='Application&apos;s context' />
- <param name='all' doc='All contexts, if NULL-context tracking is enabled' />
- <param name='full' doc='Display a full talloc memory hierarchy' />
- <param name='brief' doc='Display a brief talloc memory hierarchy' />
- <param name='DEPTH' doc='Specify required maximal depth value' />
- <param name='tree' doc='Display only a specific memory chunk' />
- <param name='ADDRESS' doc='Chunk address (e.g. 0xdeadbeef)' />
- </params>
- </command>
- <command id='show talloc-context (application|all) (full|brief|DEPTH) filter REGEXP'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='talloc-context' doc='Show talloc memory hierarchy' />
- <param name='application' doc='Application&apos;s context' />
- <param name='all' doc='All contexts, if NULL-context tracking is enabled' />
- <param name='full' doc='Display a full talloc memory hierarchy' />
- <param name='brief' doc='Display a brief talloc memory hierarchy' />
- <param name='DEPTH' doc='Specify required maximal depth value' />
- <param name='filter' doc='Filter chunks using regular expression' />
- <param name='REGEXP' doc='Regular expression' />
- </params>
- </command>
- <command id='show stats'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='stats' doc='Show statistical values' />
- </params>
- </command>
- <command id='show stats level (global|peer|subscriber)'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='stats' doc='Show statistical values' />
- <param name='level' doc='Set the maximum group level' />
- <param name='global' doc='Show global groups only' />
- <param name='peer' doc='Show global and network peer related groups' />
- <param name='subscriber' doc='Show global, peer, and subscriber groups' />
- </params>
- </command>
- <command id='show asciidoc counters'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='asciidoc' doc='Asciidoc generation' />
- <param name='counters' doc='Generate table of all registered counters' />
- </params>
- </command>
- <command id='show rate-counters'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='rate-counters' doc='Show all rate counters' />
- </params>
- </command>
- </node>
- <node id='config'>
- <name>config</name>
- <command id='hostname WORD'>
- <params>
- <param name='hostname' doc='Set system&apos;s network name' />
- <param name='WORD' doc='This system&apos;s network name' />
- </params>
- </command>
- <command id='no hostname [HOSTNAME]'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='hostname' doc='Reset system&apos;s network name' />
- <param name='[HOSTNAME]' doc='Host name of this router' />
- </params>
- </command>
- <command id='password (8|) WORD'>
- <params>
- <param name='password' doc='Assign the terminal connection password' />
- <param name='8' doc='Specifies a HIDDEN password will follow' />
- <param name='' doc='dummy string ' />
- <param name='WORD' doc='The HIDDEN line password string' />
- </params>
- </command>
- <command id='password LINE'>
- <params>
- <param name='password' doc='Assign the terminal connection password' />
- <param name='LINE' doc='The UNENCRYPTED (cleartext) line password' />
- </params>
- </command>
- <command id='enable password (8|) WORD'>
- <params>
- <param name='enable' doc='Modify enable password parameters' />
- <param name='password' doc='Assign the privileged level password' />
- <param name='8' doc='Specifies a HIDDEN password will follow' />
- <param name='' doc='dummy string ' />
- <param name='WORD' doc='The HIDDEN &apos;enable&apos; password string' />
- </params>
- </command>
- <command id='enable password LINE'>
- <params>
- <param name='enable' doc='Modify enable password parameters' />
- <param name='password' doc='Assign the privileged level password' />
- <param name='LINE' doc='The UNENCRYPTED (cleartext) &apos;enable&apos; password' />
- </params>
- </command>
- <command id='no enable password'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='enable' doc='Modify enable password parameters' />
- <param name='password' doc='Assign the privileged level password' />
- </params>
- </command>
- <command id='banner motd default'>
- <params>
- <param name='banner' doc='Set banner string' />
- <param name='motd' doc='Strings for motd' />
- <param name='default' doc='Default string' />
- </params>
- </command>
- <command id='banner motd file [FILE]'>
- <params>
- <param name='banner' doc='Set banner' />
- <param name='motd' doc='Banner for motd' />
- <param name='file' doc='Banner from a file' />
- <param name='[FILE]' doc='Filename' />
- </params>
- </command>
- <command id='no banner motd'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='banner' doc='Set banner string' />
- <param name='motd' doc='Strings for motd' />
- </params>
- </command>
- <command id='service terminal-length &lt;0-512&gt;'>
- <params>
- <param name='service' doc='Set up miscellaneous service' />
- <param name='terminal-length' doc='System wide terminal length configuration' />
- <param name='&lt;0-512&gt;' doc='Number of lines of VTY (0 means no line control)' />
- </params>
- </command>
- <command id='no service terminal-length [&lt;0-512&gt;]'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='service' doc='Set up miscellaneous service' />
- <param name='terminal-length' doc='System wide terminal length configuration' />
- <param name='[&lt;0-512&gt;]' doc='Number of lines of VTY (0 means no line control)' />
- </params>
- </command>
- <command id='line vty'>
- <params>
- <param name='line' doc='Configure a terminal line' />
- <param name='vty' doc='Virtual terminal' />
- </params>
- </command>
- <command id='service advanced-vty'>
- <params>
- <param name='service' doc='Set up miscellaneous service' />
- <param name='advanced-vty' doc='Enable advanced mode vty interface' />
- </params>
- </command>
- <command id='no service advanced-vty'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='service' doc='Set up miscellaneous service' />
- <param name='advanced-vty' doc='Enable advanced mode vty interface' />
- </params>
- </command>
- <command id='show history'>
- <params>
- <param name='show' doc='Show running system information' />
- <param name='history' doc='Display the session command history' />
- </params>
- </command>
- <command id='log stderr'>
- <params>
- <param name='log' doc='Configure logging sub-system' />
- <param name='stderr' doc='Logging via STDERR of the process' />
- </params>
- </command>
- <command id='no log stderr'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='log' doc='Configure logging sub-system' />
- <param name='stderr' doc='Logging via STDERR of the process' />
- </params>
- </command>
- <command id='log file .FILENAME'>
- <params>
- <param name='log' doc='Configure logging sub-system' />
- <param name='file' doc='Logging to text file' />
- <param name='.FILENAME' doc='Filename' />
- </params>
- </command>
- <command id='no log file .FILENAME'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='log' doc='Configure logging sub-system' />
- <param name='file' doc='Logging to text file' />
- <param name='.FILENAME' doc='Filename' />
- </params>
- </command>
- <command id='log alarms &lt;2-32700&gt;'>
- <params>
- <param name='log' doc='Configure logging sub-system' />
- <param name='alarms' doc='Logging alarms to osmo_strrb' />
- <param name='&lt;2-32700&gt;' doc='Maximum number of messages to log' />
- </params>
- </command>
- <command id='no log alarms'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='log' doc='Configure logging sub-system' />
- <param name='alarms' doc='Logging alarms to osmo_strrb' />
- </params>
- </command>
- <command id='log syslog (authpriv|cron|daemon|ftp|lpr|mail|news|user|uucp)'>
- <params>
- <param name='log' doc='Configure logging sub-system' />
- <param name='syslog' doc='Logging via syslog' />
- <param name='authpriv' doc='Security/authorization messages facility' />
- <param name='cron' doc='Clock daemon (cron/at) facility' />
- <param name='daemon' doc='General system daemon facility' />
- <param name='ftp' doc='Ftp daemon facility' />
- <param name='lpr' doc='Line printer facility' />
- <param name='mail' doc='Mail facility' />
- <param name='news' doc='News facility' />
- <param name='user' doc='Generic facility' />
- <param name='uucp' doc='UUCP facility' />
- </params>
- </command>
- <command id='log syslog local &lt;0-7&gt;'>
- <params>
- <param name='log' doc='Configure logging sub-system' />
- <param name='syslog' doc='Logging via syslog' />
- <param name='local' doc='Syslog LOCAL facility' />
- <param name='&lt;0-7&gt;' doc='Local facility number' />
- </params>
- </command>
- <command id='no log syslog'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='log' doc='Configure logging sub-system' />
- <param name='syslog' doc='Logging via syslog' />
- </params>
- </command>
- <command id='log gsmtap [HOSTNAME]'>
- <params>
- <param name='log' doc='Configure logging sub-system' />
- <param name='gsmtap' doc='Logging via GSMTAP' />
- <param name='[HOSTNAME]' doc='Host name to send the GSMTAP logging to (UDP port 4729)' />
- </params>
- </command>
- <command id='ctrl'>
- <params>
- <param name='ctrl' doc='Configure the Control Interface' />
- </params>
- </command>
- <command id='trx'>
- <params>
- <param name='trx' doc='Configure the TRX' />
- </params>
- </command>
- <command id='stats reporter statsd'>
- <params>
- <param name='stats' doc='Configure stats sub-system' />
- <param name='reporter' doc='Configure a stats reporter' />
- <param name='statsd' doc='Report to a STATSD server' />
- </params>
- </command>
- <command id='no stats reporter statsd'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='stats' doc='Configure stats sub-system' />
- <param name='reporter' doc='Configure a stats reporter' />
- <param name='statsd' doc='Report to a STATSD server' />
- </params>
- </command>
- <command id='stats reporter log'>
- <params>
- <param name='stats' doc='Configure stats sub-system' />
- <param name='reporter' doc='Configure a stats reporter' />
- <param name='log' doc='Report to the logger' />
- </params>
- </command>
- <command id='no stats reporter log'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='stats' doc='Configure stats sub-system' />
- <param name='reporter' doc='Configure a stats reporter' />
- <param name='log' doc='Report to the logger' />
- </params>
- </command>
- <command id='stats interval &lt;1-65535&gt;'>
- <params>
- <param name='stats' doc='Configure stats sub-system' />
- <param name='interval' doc='Set the reporting interval' />
- <param name='&lt;1-65535&gt;' doc='Interval in seconds' />
- </params>
- </command>
- </node>
- <node id='config-log'>
- <name>config-log</name>
- <command id='logging filter all (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='filter' doc='Filter log messages' />
- <param name='all' doc='Do you want to log all messages?' />
- <param name='0' doc='Only print messages matched by other filters' />
- <param name='1' doc='Bypass filter and print all messages' />
- </params>
- </command>
- <command id='logging color (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='color' doc='Configure color-printing for log messages' />
- <param name='0' doc='Don&apos;t use color for printing messages' />
- <param name='1' doc='Use color for printing messages' />
- </params>
- </command>
- <command id='logging timestamp (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='timestamp' doc='Configure log message timestamping' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with current timestamp' />
- </params>
- </command>
- <command id='logging print extended-timestamp (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='extended-timestamp' doc='Configure log message timestamping' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with current timestamp with YYYYMMDDhhmmssnnn' />
- </params>
- </command>
- <command id='logging print category (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='category' doc='Configure log message' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with category/subsystem name' />
- </params>
- </command>
- <command id='logging print category-hex (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='category-hex' doc='Configure log message' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with category/subsystem nr in hex (&apos;&lt;000b&gt;&apos;)' />
- </params>
- </command>
- <command id='logging print level (0|1)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='level' doc='Configure log message' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with the log level name' />
- </params>
- </command>
- <command id='logging print file (0|1|basename) [last]'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='print' doc='Log output settings' />
- <param name='file' doc='Configure log message' />
- <param name='0' doc='Don&apos;t prefix each log message' />
- <param name='1' doc='Prefix each log message with the source file and line' />
- <param name='basename' doc='Prefix each log message with the source file&apos;s basename (strip leading paths) and line' />
- <param name='[last]' doc='Log source file info at the end of a log line. If omitted, log source file info just before the log text.' />
- </params>
- </command>
- <command id='logging level (main|trxclk|trxctrl|trxddl|trxdul|dev|devdrv|lglobal|llapd|linp|lmux|lmi|lmib|lsms|lctrl|lgtp|lstats|lgsup|loap|lss7|lsccp|lsua|lm3ua|lmgcp|ljibuf|lrspro) (debug|info|notice|error|fatal)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='level' doc='Set the log level for a specified category' />
- <param name='main' doc='Main generic category' />
- <param name='trxclk' doc='TRX Master Clock' />
- <param name='trxctrl' doc='TRX CTRL interface' />
- <param name='trxddl' doc='TRX Data interface Downlink' />
- <param name='trxdul' doc='TRX CTRL interface Uplink' />
- <param name='dev' doc='Device/Driver specific code' />
- <param name='devdrv' doc='Logging from external device driver library implementing lower level specifics' />
- <param name='lglobal' doc='Library-internal global log family' />
- <param name='llapd' doc='LAPD in libosmogsm' />
- <param name='linp' doc='A-bis Intput Subsystem' />
- <param name='lmux' doc='A-bis B-Subchannel TRAU Frame Multiplex' />
- <param name='lmi' doc='A-bis Input Driver for Signalling' />
- <param name='lmib' doc='A-bis Input Driver for B-Channels (voice)' />
- <param name='lsms' doc='Layer3 Short Message Service (SMS)' />
- <param name='lctrl' doc='Control Interface' />
- <param name='lgtp' doc='GPRS GTP library' />
- <param name='lstats' doc='Statistics messages and logging' />
- <param name='lgsup' doc='Generic Subscriber Update Protocol' />
- <param name='loap' doc='Osmocom Authentication Protocol' />
- <param name='lss7' doc='libosmo-sigtran Signalling System 7' />
- <param name='lsccp' doc='libosmo-sigtran SCCP Implementation' />
- <param name='lsua' doc='libosmo-sigtran SCCP User Adaptation' />
- <param name='lm3ua' doc='libosmo-sigtran MTP3 User Adaptation' />
- <param name='lmgcp' doc='libosmo-mgcp Media Gateway Control Protocol' />
- <param name='ljibuf' doc='libosmo-netif Jitter Buffer' />
- <param name='lrspro' doc='Remote SIM protocol' />
- <param name='debug' doc='Log debug messages and higher levels' />
- <param name='info' doc='Log informational messages and higher levels' />
- <param name='notice' doc='Log noticeable messages and higher levels' />
- <param name='error' doc='Log error messages and higher levels' />
- <param name='fatal' doc='Log only fatal messages' />
- </params>
- </command>
- <command id='logging level set-all (debug|info|notice|error|fatal)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='level' doc='Set the log level for a specified category' />
- <param name='set-all' doc='Once-off set all categories to the given log level. There is no single command to take back these changes -- each category is set to the given level, period.' />
- <param name='debug' doc='Log debug messages and higher levels' />
- <param name='info' doc='Log informational messages and higher levels' />
- <param name='notice' doc='Log noticeable messages and higher levels' />
- <param name='error' doc='Log error messages and higher levels' />
- <param name='fatal' doc='Log only fatal messages' />
- </params>
- </command>
- <command id='logging level force-all (debug|info|notice|error|fatal)'>
- <params>
- <param name='logging' doc='Configure logging' />
- <param name='level' doc='Set the log level for a specified category' />
- <param name='force-all' doc='Globally force all logging categories to a specific level. This is released by the &apos;no logging level force-all&apos; command. Note: any &apos;logging level &lt;category&gt; &lt;level&gt;&apos; commands will have no visible effect after this, until the forced level is released.' />
- <param name='debug' doc='Log debug messages and higher levels' />
- <param name='info' doc='Log informational messages and higher levels' />
- <param name='notice' doc='Log noticeable messages and higher levels' />
- <param name='error' doc='Log error messages and higher levels' />
- <param name='fatal' doc='Log only fatal messages' />
- </params>
- </command>
- <command id='no logging level force-all'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='logging' doc='Configure logging' />
- <param name='level' doc='Set the log level for a specified category' />
- <param name='force-all' doc='Release any globally forced log level set with &apos;logging level force-all &lt;level&gt;&apos;' />
- </params>
- </command>
- </node>
- <node id='config-stats'>
- <name>config-stats</name>
- <command id='local-ip ADDR'>
- <params>
- <param name='local-ip' doc='Set the IP address to which we bind locally' />
- <param name='ADDR' doc='IP Address' />
- </params>
- </command>
- <command id='no local-ip'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='local-ip' doc='Set the IP address to which we bind locally' />
- </params>
- </command>
- <command id='remote-ip ADDR'>
- <params>
- <param name='remote-ip' doc='Set the remote IP address to which we connect' />
- <param name='ADDR' doc='IP Address' />
- </params>
- </command>
- <command id='remote-port &lt;1-65535&gt;'>
- <params>
- <param name='remote-port' doc='Set the remote port to which we connect' />
- <param name='&lt;1-65535&gt;' doc='Remote port number' />
- </params>
- </command>
- <command id='mtu &lt;100-65535&gt;'>
- <params>
- <param name='mtu' doc='Set the maximum packet size' />
- <param name='&lt;100-65535&gt;' doc='Size in byte' />
- </params>
- </command>
- <command id='no mtu'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='mtu' doc='Set the maximum packet size' />
- </params>
- </command>
- <command id='prefix PREFIX'>
- <params>
- <param name='prefix' doc='Set the item name prefix' />
- <param name='PREFIX' doc='The prefix string' />
- </params>
- </command>
- <command id='no prefix'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='prefix' doc='Set the item name prefix' />
- </params>
- </command>
- <command id='level (global|peer|subscriber)'>
- <params>
- <param name='level' doc='Set the maximum group level' />
- <param name='global' doc='Report global groups only' />
- <param name='peer' doc='Report global and network peer related groups' />
- <param name='subscriber' doc='Report global, peer, and subscriber groups' />
- </params>
- </command>
- <command id='enable'>
- <params>
- <param name='enable' doc='Enable the reporter' />
- </params>
- </command>
- <command id='disable'>
- <params>
- <param name='disable' doc='Disable the reporter' />
- </params>
- </command>
- </node>
- <node id='config-line'>
- <name>config-line</name>
- <command id='login'>
- <params>
- <param name='login' doc='Enable password checking' />
- </params>
- </command>
- <command id='no login'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='login' doc='Enable password checking' />
- </params>
- </command>
- <command id='bind A.B.C.D [&lt;0-65535&gt;]'>
- <params>
- <param name='bind' doc='Accept VTY telnet connections on local interface' />
- <param name='A.B.C.D' doc='Local interface IP address (default: 127.0.0.1)' />
- <param name='[&lt;0-65535&gt;]' doc='Local TCP port number' />
- </params>
- </command>
- </node>
- <node id='config-ctrl'>
- <name>config-ctrl</name>
- <command id='bind A.B.C.D'>
- <params>
- <param name='bind' doc='Set bind address to listen for Control connections' />
- <param name='A.B.C.D' doc='Local IP address (default 127.0.0.1)' />
- </params>
- </command>
- </node>
- <node id='config-trx'>
- <name>config-trx</name>
- <command id='bind-ip A.B.C.D'>
- <params>
- <param name='bind-ip' doc='Set the IP address for the local bind' />
- <param name='A.B.C.D' doc='IPv4 Address' />
- </params>
- </command>
- <command id='remote-ip A.B.C.D'>
- <params>
- <param name='remote-ip' doc='Set the IP address for the remote BTS' />
- <param name='A.B.C.D' doc='IPv4 Address' />
- </params>
- </command>
- <command id='base-port &lt;1-65535&gt;'>
- <params>
- <param name='base-port' doc='Set the TRX Base Port' />
- <param name='&lt;1-65535&gt;' doc='TRX Base Port' />
- </params>
- </command>
- <command id='dev-args DESC'>
- <params>
- <param name='dev-args' doc='Set the device-specific arguments to pass to the device' />
- <param name='DESC' doc='Device-specific arguments' />
- </params>
- </command>
- <command id='tx-sps (1|4)'>
- <params>
- <param name='tx-sps' doc='Set the Tx Samples-per-Symbol' />
- <param name='1' doc='Tx Samples-per-Symbol' />
- <param name='4' doc='(null)' />
- </params>
- </command>
- <command id='rx-sps (1|4)'>
- <params>
- <param name='rx-sps' doc='Set the Rx Samples-per-Symbol' />
- <param name='1' doc='Rx Samples-per-Symbol' />
- <param name='4' doc='(null)' />
- </params>
- </command>
- <command id='clock-ref (internal|external|gpsdo)'>
- <params>
- <param name='clock-ref' doc='Set the Reference Clock' />
- <param name='internal' doc='Enable internal reference (default)' />
- <param name='external' doc='Enable external 10 MHz reference' />
- <param name='gpsdo' doc='Enable GPSDO reference' />
- </params>
- </command>
- <command id='multi-arfcn (disable|enable)'>
- <params>
- <param name='multi-arfcn' doc='Enable multi-ARFCN transceiver (default=disable)' />
- <param name='disable' doc='(null)' />
- <param name='enable' doc='(null)' />
- </params>
- </command>
- <command id='offset FLOAT'>
- <params>
- <param name='offset' doc='Set the baseband frequency offset (default=0, auto)' />
- <param name='FLOAT' doc='Baseband Frequency Offset' />
- </params>
- </command>
- <command id='rssi-offset FLOAT'>
- <params>
- <param name='rssi-offset' doc='Set the RSSI to dBm offset in dB (default=0)' />
- <param name='FLOAT' doc='RSSI to dBm offset in dB' />
- </params>
- </command>
- <command id='swap-channels (disable|enable)'>
- <params>
- <param name='swap-channels' doc='Swap channels (default=disable)' />
- <param name='disable' doc='(null)' />
- <param name='enable' doc='(null)' />
- </params>
- </command>
- <command id='egprs (disable|enable)'>
- <params>
- <param name='egprs' doc='Enable EDGE receiver (default=disable)' />
- <param name='disable' doc='(null)' />
- <param name='enable' doc='(null)' />
- </params>
- </command>
- <command id='ext-rach (disable|enable)'>
- <params>
- <param name='ext-rach' doc='Enable extended (11-bit) RACH (default=disable)' />
- <param name='disable' doc='(null)' />
- <param name='enable' doc='(null)' />
- </params>
- </command>
- <command id='rt-prio &lt;1-32&gt;'>
- <params>
- <param name='rt-prio' doc='Set the SCHED_RR real-time priority' />
- <param name='&lt;1-32&gt;' doc='Real time priority' />
- </params>
- </command>
- <command id='filler type (zero|dummy|random-nb-gmsk|random-nb-8psk|random-ab)'>
- <params>
- <param name='filler' doc='Filler burst settings' />
- <param name='type' doc='Filler burst type (default=zero)' />
- <param name='zero' doc='Send an empty burst when there is nothing to send (default)' />
- <param name='dummy' doc='Send a dummy burst when there is nothing to send on C0 (TRX0) and empty burst on other channels. Use for OpenBTS compatibility only, don&apos;t use with OsmoBTS as it breaks encryption.' />
- <param name='random-nb-gmsk' doc='Send a GMSK modulated Normal Burst with random bits when there is nothing to send. Use for spectrum mask testing. Configure &apos;filler tsc&apos; to set training sequence.' />
- <param name='random-nb-8psk' doc='Send an 8-PSK modulated Normal Burst with random bits when there is nothing to send. Use for spectrum mask testing. Configure &apos;filler tsc&apos; to set training sequence.' />
- <param name='random-ab' doc='Send an Access Burst with random bits when there is nothing to send. Use for Rx/Tx alignment. Configure &apos;filler access-burst-delay&apos; to introduce artificial delay.' />
- </params>
- </command>
- <command id='filler tsc &lt;0-7&gt;'>
- <params>
- <param name='filler' doc='Filler burst settings' />
- <param name='tsc' doc='Set the TSC for GMSK/8-PSK Normal Burst random fillers. Used only with &apos;random-nb-gmsk&apos; and &apos;random-nb-8psk&apos; filler types. (default=0)' />
- <param name='&lt;0-7&gt;' doc='TSC' />
- </params>
- </command>
- <command id='filler access-burst-delay &lt;0-68&gt;'>
- <params>
- <param name='filler' doc='Filler burst settings' />
- <param name='access-burst-delay' doc='Set the delay for Access Burst random fillers. Used only with &apos;random-ab&apos; filler type. (default=0)' />
- <param name='&lt;0-68&gt;' doc='RACH delay in symbols' />
- </params>
- </command>
- <command id='ctr-error-threshold (rx_overruns|tx_underruns|rx_drop_events|rx_drop_samples|tx_drop_events|tx_drop_samples) &lt;0-65535&gt; (per-second|per-minute|per-hour|per-day)'>
- <params>
- <param name='ctr-error-threshold' doc='Threshold rate for error counter' />
- <param name='rx_overruns' doc='Set threshold value for rate_ctr device:rx_overruns' />
- <param name='tx_underruns' doc='Set threshold value for rate_ctr device:tx_underruns' />
- <param name='rx_drop_events' doc='Set threshold value for rate_ctr device:rx_drop_events' />
- <param name='rx_drop_samples' doc='Set threshold value for rate_ctr device:rx_drop_samples' />
- <param name='tx_drop_events' doc='Set threshold value for rate_ctr device:tx_drop_events' />
- <param name='tx_drop_samples' doc='Set threshold value for rate_ctr device:tx_drop_samples' />
- <param name='&lt;0-65535&gt;' doc='Value to set for threshold' />
- <param name='per-second' doc='Threshold value sampled per-second' />
- <param name='per-minute' doc='Threshold value sampled per-minute' />
- <param name='per-hour' doc='Threshold value sampled per-hour' />
- <param name='per-day' doc='Threshold value sampled per-day' />
- </params>
- </command>
- <command id='no ctr-error-threshold (rx_overruns|tx_underruns|rx_drop_events|rx_drop_samples|tx_drop_events|tx_drop_samples) &lt;0-65535&gt; (per-second|per-minute|per-hour|per-day)'>
- <params>
- <param name='no' doc='Negate a command or set its defaults' />
- <param name='ctr-error-threshold' doc='Threshold rate for error counter' />
- <param name='rx_overruns' doc='Set threshold value for rate_ctr device:rx_overruns' />
- <param name='tx_underruns' doc='Set threshold value for rate_ctr device:tx_underruns' />
- <param name='rx_drop_events' doc='Set threshold value for rate_ctr device:rx_drop_events' />
- <param name='rx_drop_samples' doc='Set threshold value for rate_ctr device:rx_drop_samples' />
- <param name='tx_drop_events' doc='Set threshold value for rate_ctr device:tx_drop_events' />
- <param name='tx_drop_samples' doc='Set threshold value for rate_ctr device:tx_drop_samples' />
- <param name='&lt;0-65535&gt;' doc='Value to set for threshold' />
- <param name='per-second' doc='Threshold value sampled per-second' />
- <param name='per-minute' doc='Threshold value sampled per-minute' />
- <param name='per-hour' doc='Threshold value sampled per-hour' />
- <param name='per-day' doc='Threshold value sampled per-day' />
- </params>
- </command>
- <command id='stack-size &lt;0-2147483647&gt;'>
- <params>
- <param name='stack-size' doc='Set the stack size per thread in BYTE, 0 = OS default' />
- <param name='&lt;0-2147483647&gt;' doc='Stack size per thread in BYTE' />
- </params>
- </command>
- <command id='chan &lt;0-100&gt;'>
- <params>
- <param name='chan' doc='Select a channel to configure' />
- <param name='&lt;0-100&gt;' doc='Channel index' />
- </params>
- </command>
- </node>
- <node id='config-trx-chan'>
- <name>config-trx-chan</name>
- <command id='rx-path NAME'>
- <params>
- <param name='rx-path' doc='Set the Rx Path' />
- <param name='NAME' doc='Rx Path name' />
- </params>
- </command>
- <command id='tx-path NAME'>
- <params>
- <param name='tx-path' doc='Set the Tx Path' />
- <param name='NAME' doc='Tx Path name' />
- </params>
- </command>
- </node>
-</vtydoc>
diff --git a/osmocom-bb b/osmocom-bb
new file mode 160000
+Subproject f12b17dffb782c7428a563620aa83ec047fd99c
diff --git a/tests/CommonLibs/InterthreadTest.cpp b/tests/CommonLibs/InterthreadTest.cpp
index 462df08..a00980f 100644
--- a/tests/CommonLibs/InterthreadTest.cpp
+++ b/tests/CommonLibs/InterthreadTest.cpp
@@ -29,9 +29,9 @@
#include "Threads.h"
#include "Interthread.h"
#include <iostream>
+#include <mutex>
-using namespace std;
-
+std::mutex dbg_cout;
InterthreadQueue<int> gQ;
InterthreadMap<int,int> gMap;
@@ -41,6 +41,8 @@ int q_last_write_val;
int m_last_read_val;
int m_last_write_val;
+#define CERR(text) { dbg_cout.lock() ; std::cerr << text; dbg_cout.unlock(); }
+
void* qWriter(void*)
{
int *p;
diff --git a/tests/CommonLibs/LogTest.cpp b/tests/CommonLibs/LogTest.cpp
index 0087070..f203706 100644
--- a/tests/CommonLibs/LogTest.cpp
+++ b/tests/CommonLibs/LogTest.cpp
@@ -59,8 +59,10 @@ int main(int argc, char *argv[])
osmo_init_logging2(tall_ctx, &linfo);
log_set_use_color(osmo_stderr_target, 0);
- log_set_print_filename(osmo_stderr_target, 0);
+ log_set_print_filename2(osmo_stderr_target, LOG_FILENAME_NONE);
log_set_print_level(osmo_stderr_target, 1);
+ log_set_print_category(osmo_stderr_target, 0);
+ log_set_print_category_hex(osmo_stderr_target, 0);
Log(MYCAT, LOGL_FATAL, __BASE_FILE__, __LINE__).get() << "testing the logger.";
Log(MYCAT, LOGL_ERROR, __BASE_FILE__, __LINE__).get() << "testing the logger.";
diff --git a/tests/CommonLibs/Makefile.am b/tests/CommonLibs/Makefile.am
index 26b49e2..cfdbc58 100644
--- a/tests/CommonLibs/Makefile.am
+++ b/tests/CommonLibs/Makefile.am
@@ -1,7 +1,25 @@
include $(top_srcdir)/Makefile.common
-AM_CPPFLAGS = -Wall -I$(top_srcdir)/CommonLibs $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) -g
-AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/CommonLibs \
+ $(STD_DEFINES_AND_INCLUDES) \
+ $(NULL)
+
+AM_CXXFLAGS = \
+ -Wall -g \
+ $(LIBOSMOCORE_CFLAGS) \
+ $(LIBOSMOCTRL_CFLAGS) \
+ $(LIBOSMOVTY_CFLAGS) \
+ $(NULL)
+
+AM_LDFLAGS = -no-install
+
+LDADD = \
+ $(COMMON_LA) \
+ $(LIBOSMOCORE_LIBS) \
+ $(LIBOSMOCTRL_LIBS) \
+ $(LIBOSMOVTY_LIBS) \
+ $(NULL)
EXTRA_DIST = BitVectorTest.ok \
PRBSTest.ok \
@@ -11,7 +29,7 @@ EXTRA_DIST = BitVectorTest.ok \
LogTest.ok \
LogTest.err
-noinst_PROGRAMS = \
+check_PROGRAMS = \
BitVectorTest \
PRBSTest \
InterthreadTest \
@@ -20,21 +38,16 @@ noinst_PROGRAMS = \
LogTest
BitVectorTest_SOURCES = BitVectorTest.cpp
-BitVectorTest_LDADD = $(COMMON_LA)
PRBSTest_SOURCES = PRBSTest.cpp
InterthreadTest_SOURCES = InterthreadTest.cpp
-InterthreadTest_LDADD = $(COMMON_LA)
-InterthreadTest_LDFLAGS = -lpthread $(AM_LDFLAGS)
+InterthreadTest_LDADD = $(LDADD) -lpthread
TimevalTest_SOURCES = TimevalTest.cpp
-TimevalTest_LDADD = $(COMMON_LA)
VectorTest_SOURCES = VectorTest.cpp
-VectorTest_LDADD = $(COMMON_LA)
LogTest_SOURCES = LogTest.cpp
-LogTest_LDADD = $(COMMON_LA)
MOSTLYCLEANFILES += testSource testDestination
diff --git a/tests/CommonLibs/PRBSTest.cpp b/tests/CommonLibs/PRBSTest.cpp
index cb5ed41..8000288 100644
--- a/tests/CommonLibs/PRBSTest.cpp
+++ b/tests/CommonLibs/PRBSTest.cpp
@@ -12,10 +12,6 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
- *
- * You should have received a copy of the GNU Lesser General Public
- * License along with this library; if not, write to the Free Software
- * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "PRBS.h"
diff --git a/tests/Transceiver52M/Makefile.am b/tests/Transceiver52M/Makefile.am
index edb812f..473cec6 100644
--- a/tests/Transceiver52M/Makefile.am
+++ b/tests/Transceiver52M/Makefile.am
@@ -1,10 +1,17 @@
include $(top_srcdir)/Makefile.common
-AM_CFLAGS = -Wall -I$(top_srcdir)/Transceiver52M -I$(top_srcdir)/Transceiver52M/arch/common $(STD_DEFINES_AND_INCLUDES) -g
+AM_CPPFLAGS = \
+ -I$(top_srcdir)/Transceiver52M \
+ -I$(top_srcdir)/Transceiver52M/arch/common \
+ $(STD_DEFINES_AND_INCLUDES) \
+ $(NULL)
+
+AM_CFLAGS = -Wall -g
+AM_LDFLAGS = -no-install
EXTRA_DIST = convolve_test.ok convolve_test_golden.h
-noinst_PROGRAMS = \
+check_PROGRAMS = \
convolve_test
convolve_test_SOURCES = convolve_test.c
@@ -18,12 +25,13 @@ convolve_test_CFLAGS += $(SIMD_FLAGS)
endif
if DEVICE_LMS
-noinst_PROGRAMS += LMSDeviceTest
+check_PROGRAMS += LMSDeviceTest
LMSDeviceTest_SOURCES = LMSDeviceTest.cpp
-LMSDeviceTest_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LMS_LIBS)
LMSDeviceTest_LDADD = \
$(top_builddir)/Transceiver52M/device/lms/libdevice.la \
+ $(LIBOSMOCORE_LIBS) \
$(COMMON_LA) \
- $(LMS_LIBS)
-LMSDeviceTest_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
+ $(LMS_LIBS) \
+ $(NULL)
+LMSDeviceTest_CXXFLAGS = $(AM_CFLAGS) $(LMS_CFLAGS)
endif
diff --git a/utils/Makefile.am b/utils/Makefile.am
index bac9a7f..1818093 100644
--- a/utils/Makefile.am
+++ b/utils/Makefile.am
@@ -1,6 +1,8 @@
AM_CPPFLAGS = $(LIBOSMOCODING_CFLAGS)
AM_CFLAGS = -Wall
+DIST_SUBDIRS = va-test
+
EXTRA_DIST = clockdump.sh matlab
noinst_PROGRAMS = osmo-prbs-tool
diff --git a/utils/va-test/Makefile.am b/utils/va-test/Makefile.am
new file mode 100644
index 0000000..d5a72e5
--- /dev/null
+++ b/utils/va-test/Makefile.am
@@ -0,0 +1,17 @@
+include $(top_srcdir)/Makefile.common
+
+noinst_PROGRAMS = osmo-burst-gen
+
+osmo_burst_gen_SOURCES = burst-gen.cpp \
+ ${top_srcdir}/Transceiver52M/grgsm_vitac/grgsm_vitac.cpp \
+ ${top_srcdir}/Transceiver52M/grgsm_vitac/viterbi_detector.cc
+osmo_burst_gen_LDADD = \
+ ${top_srcdir}/Transceiver52M/libtransceiver_common.la \
+ $(ARCH_LA) \
+ $(GSM_LA) \
+ $(COMMON_LA)
+osmo_burst_gen_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) \
+ -I${top_srcdir}/Transceiver52M/arch/common \
+ -I${top_srcdir}/Transceiver52M/device/common \
+ -I${top_srcdir}/Transceiver52M
+
diff --git a/utils/va-test/burst-gen.cpp b/utils/va-test/burst-gen.cpp
new file mode 100644
index 0000000..bdd0c4b
--- /dev/null
+++ b/utils/va-test/burst-gen.cpp
@@ -0,0 +1,531 @@
+/*
+ * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
+ * All Rights Reserved
+ *
+ * Author: Eric Wild <ewild@sysmocom.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+// this allows messing with the demod to check the detecton offset impact,
+// not intended for actual automated tests.
+
+#include "sigProcLib.h"
+
+extern "C" {
+#include "convert.h"
+#include <convolve.h>
+}
+
+#define _CRT_SECURE_NO_WARNINGS
+#include <algorithm>
+#include <string.h>
+#include <iomanip>
+#include <numeric>
+#include <memory>
+#include <iostream>
+#include <fstream>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <grgsm_vitac/grgsm_vitac.h>
+
+#define DO_RACH
+
+const int SAMPLE_SCALE_FACTOR = 1;
+
+template <typename DST_T, typename SRC_T, typename ST>
+void convert_and_scale(void *dst, void *src, unsigned int src_len, ST scale)
+{
+ for (unsigned int i = 0; i < src_len; i++)
+ reinterpret_cast<DST_T *>(dst)[i] = static_cast<DST_T>((reinterpret_cast<SRC_T *>(src)[i]) * scale);
+}
+template <typename DST_T, typename SRC_T>
+void convert_and_scale_default(void *dst, void *src, unsigned int src_len)
+{
+ return convert_and_scale<DST_T, SRC_T>(dst, src, src_len, SAMPLE_SCALE_FACTOR);
+}
+
+static const unsigned int txFullScale = (float)(1 << 14) - 1;
+// static const unsigned int rxFullScale = (float)(1 << 14) - 1;
+
+static const BitVector
+ gRACHBurstx("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
+
+static const BitVector gTrainingSequencex[] = {
+ BitVector("00100101110000100010010111"), BitVector("00101101110111100010110111"),
+ BitVector("01000011101110100100001110"), BitVector("01000111101101000100011110"),
+ BitVector("00011010111001000001101011"), BitVector("01001110101100000100111010"),
+ BitVector("10100111110110001010011111"), BitVector("11101111000100101110111100"),
+};
+
+struct mrv {
+ std::vector<char> bits;
+ signalVector *rvbuf;
+ std::unique_ptr<std::vector<std::complex<float>>> convolved;
+ // mrv(): bits(), demod_bits() {}
+ CorrType ct;
+};
+
+static mrv genRandNormalBurstx(int tsc, int sps, int tn)
+{
+ mrv retstruct;
+ int i = 0;
+ BitVector bits(148);
+
+ /* Tail bits */
+ for (; i < 3; i++)
+ bits[i] = 0;
+
+ /* Random bits */
+ for (int j = 0; i < 60; i++, j++)
+ bits[i] = rand() % 2;
+
+ /* Stealing bit */
+ bits[i++] = 0;
+
+ /* Training sequence */
+ for (int n = 0; i < 87; i++, n++)
+ bits[i] = gTrainingSequencex[tsc][n];
+
+ /* Stealing bit */
+ bits[i++] = 0;
+
+ /* Random bits */
+ for (; i < 145; i++)
+ bits[i] = rand() % 2;
+
+ /* Tail bits */
+ for (; i < 148; i++)
+ bits[i] = 0;
+
+ int guard = 8 + !(tn % 4);
+ auto r = modulateBurst(bits, guard, sps);
+
+ retstruct.rvbuf = r;
+ for (size_t i = 0; i < bits.size(); i++)
+ retstruct.bits.push_back(bits.bit(i) ? 1 : 0);
+ return retstruct;
+}
+
+static mrv genRandAccessBurstx(int delay, int sps, int tn)
+{
+ mrv retstruct;
+ int i = 0;
+ BitVector bits(88 + delay);
+
+ /* delay */
+ for (; i < delay; i++)
+ bits[i] = 0;
+
+ /* head and synch bits */
+ for (int n = 0; i < 49 + delay; i++, n++)
+ bits[i] = gRACHBurstx[n];
+
+ /* Random bits */
+ for (int j = 0; i < 85 + delay; i++, j++)
+ bits[i] = rand() % 2;
+
+ for (; i < 88 + delay; i++)
+ bits[i] = 0;
+
+ int guard = 68 - delay + !(tn % 4);
+ auto r = modulateBurst(bits, guard, sps);
+
+ retstruct.rvbuf = r;
+ for (size_t i = 0; i < bits.size(); i++)
+ retstruct.bits.push_back(bits.bit(i) ? 1 : 0);
+ return retstruct;
+}
+
+extern gr_complex d_acc_training_seq[N_ACCESS_BITS]; ///<encoded training sequence of a SCH burst
+extern gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a SCH burst
+extern gr_complex d_norm_training_seq[TRAIN_SEQ_NUM]
+ [N_TRAIN_BITS]; ///<encoded training sequences of a normal and dummy burst
+
+void sv_write_helper(signalVector *burst, std::string fname)
+{
+ auto start = burst->begin();
+ auto n = burst->bytes();
+ char *data = reinterpret_cast<char *>(start);
+
+ const int len_in_real = burst->size() * 2;
+ auto cvrtbuf_tx_a = new int16_t[len_in_real];
+ convert_float_short(cvrtbuf_tx_a, (float *)burst->begin(), float(txFullScale), len_in_real);
+
+ std::ofstream fout;
+ fout.open(fname + ".cfile", std::ios::binary | std::ios::out);
+ fout.write(data, n);
+ fout.close();
+
+ fout.open(fname + ".cs16", std::ios::binary | std::ios::out);
+ fout.write((char *)cvrtbuf_tx_a, len_in_real * sizeof(uint16_t));
+ fout.close();
+ delete[] cvrtbuf_tx_a;
+}
+
+// borrowed from a real world burst..
+static std::vector<std::complex<float>> chan_im_resp = {
+ { 4.1588e-05 + -0.000361925 }, { 0.000112728 + -0.000289796 }, { 0.000162952 + -0.000169028 },
+ { 0.000174185 + -2.54575e-05 }, { 0.000142947 + 0.000105992 }, { 8.65919e-05 + 0.000187041 },
+ { 4.15799e-05 + 0.000184346 }, { 5.30207e-05 + 7.84921e-05 }, { 0.000158877 + -0.000128058 },
+ { 0.000373956 + -0.000407954 }, { 0.000680606 + -0.000712065 }, { 0.00102929 + -0.000979604 },
+ { 0.00135049 + -0.00115333 }, { 0.00157434 + -0.0011948 }, { 0.00165098 + -0.00109534 },
+ { 0.00156519 + -0.000878794 }, { 0.0013399 + -0.000594285 }, { 0.00102788 + -0.00030189 },
+ { 0.000694684 + -5.58912e-05 }, { 0.000399328 + 0.000109463 }
+};
+
+// as above, downsampled to 1sps + just magnitude
+static std::vector<float> chan_im_resp_trunc = { 1., 0.20513351, 0.10020305, 0.11490235 };
+
+template <typename A, typename B>
+auto conv(const std::vector<A> &a, const std::vector<B> &b) -> std::unique_ptr<std::vector<A>>
+{
+ int data_len = a.size();
+ int conv_len = b.size();
+ int conv_size = conv_len + data_len - 1;
+ auto retv = std::make_unique<std::vector<A>>(conv_size);
+
+ for (int i = 0; i < data_len; ++i) {
+ for (int j = 0; j < conv_len; ++j) {
+ (*retv)[i + j] += a[i] * b[j];
+ }
+ }
+ return retv;
+}
+
+template <typename A>
+static auto conv(const A *a, int len, std::vector<float> &b)
+{
+ std::vector<A> aa(len);
+ std::copy_n(a, len, aa.begin());
+ std::reverse(b.begin(), b.end());
+ return conv(aa, b);
+}
+template <typename A>
+static auto conv(const A *a, int len, std::vector<A> &b)
+{
+ std::vector<A> aa(len);
+ std::copy_n(a, len, aa.begin());
+ std::reverse(b.begin(), b.end());
+ return conv(aa, b);
+}
+
+// signalvector is owning despite claiming not to, but we can pretend, too..
+static void dummy_free(void *wData){};
+static void *dummy_alloc(size_t newSize)
+{
+ return 0;
+};
+
+template <typename T>
+size_t read_from_file(std::string path, std::vector<T> &outvec)
+{
+ std::ifstream infile;
+ infile.open(path, std::ios::in | std::ios::binary);
+ if (infile.fail()) {
+ std::cout << " not found: " << path << std::endl;
+ exit(0);
+ }
+ infile.seekg(0, std::ios_base::end);
+ size_t fsize = infile.tellg();
+ auto fsize_in_T = fsize / sizeof(T);
+ infile.seekg(0, std::ios_base::beg);
+
+ outvec.resize(fsize_in_T);
+ infile.read(reinterpret_cast<char *>(&outvec[0]), fsize);
+ infile.close();
+ std::cout << "Read " << fsize << " from " << path << std::endl;
+ return fsize;
+}
+void demod_real_burst(int num = 0)
+{
+ auto path = "./nb_chunk_tsc7.cfile";
+ auto bitfile = "./demodbits_tsc7.s8";
+
+ std::vector<std::complex<float>> burstdata;
+ std::vector<char> bitdata;
+ read_from_file(path, burstdata);
+ read_from_file(bitfile, bitdata);
+
+ // print "known good" burst bits
+ std::cerr << "known bits:" << std::endl;
+ std::cerr << std::setw(5) << 0 << " - ";
+ for (auto i : bitdata)
+ std::cout << (i > 0 ? "1" : "0");
+ std::cerr << std::endl;
+ std::cerr << "demod tests sigproclib:" << std::endl;
+
+ auto ct = CorrType::TSC;
+ auto delay = 0;
+ auto tsc = 7;
+ int offset = 0;
+ auto cplx = reinterpret_cast<complex *>(&burstdata[offset]);
+ auto stdcplx = reinterpret_cast<std::complex<float> *>(&burstdata[offset]);
+ signalVector sv(&cplx[0], 0, burstdata.size() - offset, dummy_alloc, dummy_free);
+
+ struct estim_burst_params ebp;
+ auto rc = detectAnyBurst(sv, tsc, BURST_THRESH, 4, ct, 40, &ebp);
+
+ auto rxBurst = std::unique_ptr<SoftVector>(demodAnyBurst(sv, (CorrType)rc, 4, &ebp));
+ // print osmotrx sigproclib demod result
+ std::cerr << std::setw(5) << int(ebp.toa) << " o ";
+ for (ssize_t i = 0 + delay; i < 148 + delay; i++)
+ std::cout << (rxBurst->bit(i) ? "1" : "0");
+ std::cerr << std::endl;
+
+ std::cerr << "demod test va:" << std::endl;
+ std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
+ float ncmax;
+ char demodded_softbits[444];
+
+ // demod at known offset
+ {
+ auto inp = &stdcplx[29]; // known offset
+ auto normal_burst_startX = get_norm_chan_imp_resp(inp, &chan_imp_resp[0], &ncmax, tsc);
+ detect_burst_nb(inp, &chan_imp_resp[0], normal_burst_startX, demodded_softbits);
+
+ std::cerr << std::setw(5) << normal_burst_startX << " v ";
+ for (size_t i = 0; i < 148; i++)
+ std::cerr << (demodded_softbits[i] < 0 ? "1" : "0");
+ std::cerr << std::endl;
+ }
+ {
+ std::cerr << "-- va start offset loop --" << std::endl;
+ std::cerr << "offset/det offset/#errors/known^demod bits" << std::endl;
+ for (int i = 0; i < 34; i++) {
+ auto inp = &stdcplx[i];
+ auto conved_beg = inp;
+
+ auto me = get_norm_chan_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, tsc);
+ detect_burst_nb(conved_beg, &chan_imp_resp[0], me, demodded_softbits);
+ auto bitdiffarr = std::make_unique<char[]>(148);
+ for (size_t i = 0; i < 148; i++)
+ bitdiffarr.get()[i] = (demodded_softbits[i] < 0 ? 1 : 0) ^ (bitdata[i] > 0 ? 1 : 0);
+ auto ber = std::accumulate(bitdiffarr.get(), bitdiffarr.get() + 148, 0);
+
+ std::cerr << std::setw(3) << i << ": " << std::setw(3) << me << " v " << std::setw(3) << ber
+ << " ";
+ for (size_t i = 0; i < 148; i++)
+ std::cerr << (bitdiffarr[i] ? "1" : "0");
+ std::cerr << std::endl;
+ // std::cerr << std::setw(4) << i << " (" << std::setw(4) << 29 - i << "):" << std::setw(4) << org
+ // << " " << std::setw(4) << me << " y " << std::endl;
+ }
+ }
+}
+
+auto gen_burst(CorrType t, int delay, int tsc)
+{
+ mrv rs;
+ if (t == CorrType::RACH) {
+ rs = genRandAccessBurstx(delay, 4, tsc);
+
+ } else if (t == CorrType::TSC) {
+ rs = genRandNormalBurstx(tsc, 4, 0);
+ } else {
+ std::cerr << "wtf?" << std::endl;
+ exit(0);
+ }
+ rs.ct = t;
+
+ signalVector *burst = rs.rvbuf;
+ // sv_write_helper(burst, std::to_string(num));
+ // scaleVector(*burst, {1, 0});
+ const int len_in_real = burst->size() * 2;
+ auto cvrtbuf_tx_a = std::make_unique<short[]>(len_in_real);
+ auto cvrtbuf_rx_a = std::make_unique<float[]>(len_in_real);
+ auto rx_cfloat = reinterpret_cast<std::complex<float> *>(&cvrtbuf_rx_a[0]);
+
+ convert_float_short(cvrtbuf_tx_a.get(), (float *)burst->begin(), float(txFullScale), len_in_real);
+ convert_short_float(cvrtbuf_rx_a.get(), cvrtbuf_tx_a.get(), len_in_real);
+ for (int i = 0; i < len_in_real; i++) // scale properly!
+ cvrtbuf_rx_a[i] *= 1. / txFullScale;
+ auto conved = conv(rx_cfloat, burst->size(), chan_im_resp);
+
+ std::cerr << "-- generated " << (t == CorrType::RACH ? "RACH" : "TSC") << " burst --" << std::endl;
+ for (size_t i = 0; i < rs.bits.size(); i++)
+ std::cerr << (rs.bits[i] ? "1" : "0");
+ std::cerr << std::endl;
+ delete burst;
+ rs.convolved = std::move(conved);
+ return rs;
+}
+
+void demod_generated_burst(CorrType t)
+{
+ int tsc = 0;
+ int delay = 0;
+ auto rs = gen_burst(t, delay, tsc);
+ auto conved_beg = &(*rs.convolved)[0];
+
+ if (rs.ct == CorrType::RACH) {
+ std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
+ float ncmax;
+ char demodded_softbits[444];
+ int normal_burst_start = 0;
+ normal_burst_start = get_access_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, 0);
+ normal_burst_start = std::max(normal_burst_start, 0);
+ for (int j = 0; j < 4; j++) {
+ for (int start_val = 0; start_val < 16; start_val++) {
+ auto bitdiffarr = std::make_unique<char[]>(rs.bits.size());
+ detect_burst_ab(conved_beg, &chan_imp_resp[0], normal_burst_start + j,
+ demodded_softbits, start_val);
+
+ for (size_t i = 0; i < rs.bits.size(); i++)
+ bitdiffarr.get()[i] = (demodded_softbits[i] < 0 ? 1 : 0) ^ rs.bits[i];
+ auto ber = std::accumulate(bitdiffarr.get(), bitdiffarr.get() + rs.bits.size(), 0);
+
+ std::cerr << "ber " << std::setw(4) << ber << " bo:" << std::setw(4) << j
+ << " vas:" << std::setw(4) << start_val << " ";
+ // for (size_t i = 0; i < rs.num_bits; i++)
+ // std::cerr << (demodded_softbits[i] < 0 ? "1" : "0");
+ // std::cerr << std::endl;
+ // std::cerr << "d " << std::setw(4) << ber << " ";
+ for (size_t i = 0; i < rs.bits.size(); i++)
+ std::cerr << (bitdiffarr.get()[i] ? "1" : "0");
+ std::cerr << std::endl;
+
+ // std::cerr << "v " << std::setw(4) << j << std::setw(4) << start_val << " ";
+ // for (size_t i = 0; i < rs.num_bits; i++)
+ // std::cerr << (demodded_softbits[i] < 0 ? "1" : "0");
+ // std::cerr << std::endl;
+ // std::cerr << "d " << std::setw(4) << ber << " ";
+ // for (size_t i = 0; i < rs.num_bits; i++)
+ // std::cerr << (ptr.get()[i] ? "1" : "0");
+ // std::cerr << std::endl;
+ }
+ }
+
+ } else {
+ std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
+ float ncmax;
+ char demodded_softbits[444];
+
+ auto normal_burst_start = get_norm_chan_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, tsc);
+ detect_burst_nb(conved_beg, &chan_imp_resp[0], normal_burst_start + 0, demodded_softbits);
+ std::cerr << "toa " << std::setprecision(2) << normal_burst_start << std::endl;
+
+ std::cerr << "vita ";
+ for (size_t i = 0; i < rs.bits.size(); i++)
+ std::cerr << (demodded_softbits[i] < 0 ? "1" : "0");
+ std::cerr << std::endl;
+ std::cerr << "diff ";
+ for (size_t i = 0; i < rs.bits.size(); i++)
+ std::cerr << ((demodded_softbits[i] < 0 ? 1 : 0) ^ rs.bits[i] ? "1" : "0");
+ std::cerr << std::endl;
+ }
+
+ struct estim_burst_params ebp;
+ char demodded_softbits[444];
+ complex *rx_sigproc_cfloat = reinterpret_cast<complex *>(conved_beg);
+ signalVector sv(rx_sigproc_cfloat, 0, rs.convolved->size(), dummy_alloc, dummy_free);
+
+ auto rc = detectAnyBurst(sv, tsc, BURST_THRESH, 4, rs.ct, 40, &ebp);
+ auto rxBurst = std::unique_ptr<SoftVector>(demodAnyBurst(sv, (CorrType)rc, 4, &ebp));
+
+ std::cerr << "toa " << std::setprecision(2) << ebp.toa << std::endl;
+
+ for (ssize_t i = 0; i < delay; i++) // maybe pad rach op?
+ demodded_softbits[i] = 0;
+ for (size_t i = 0 + delay; i < rs.bits.size() + delay; i++)
+ demodded_softbits[i] = (rxBurst->bit(i) ? 1 : 0);
+
+ std::cerr << "sigp ";
+ for (size_t i = 0; i < rs.bits.size(); i++)
+ std::cerr << (demodded_softbits[i] ? "1" : "0");
+ std::cerr << std::endl;
+
+ std::cerr << "diff ";
+ for (size_t i = 0; i < rs.bits.size(); i++)
+ std::cerr << (demodded_softbits[i] ^ rs.bits[i] ? "1" : "0");
+ std::cerr << std::endl;
+}
+
+void demod_test_offsets()
+{
+ const int tsc = 0;
+ const int delaybuffer_realoffset = 100;
+
+ {
+ auto rs = gen_burst(CorrType::RACH, 0, tsc);
+ typeof(*rs.convolved) delay_buffer(rs.convolved->size() * 2); // plenty of space..
+
+ for (int delay = -10; delay < 60; delay++) {
+ std::fill(delay_buffer.begin(), delay_buffer.end(), 0);
+ std::copy(rs.convolved->begin(), rs.convolved->end(),
+ delay_buffer.begin() + delaybuffer_realoffset + delay);
+
+ auto conved_beg = &delay_buffer[delaybuffer_realoffset];
+
+ std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
+ float ncmax;
+ auto va_burst_start = get_access_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, 60);
+
+ complex *rx_sigproc_cfloat = reinterpret_cast<complex *>(conved_beg);
+ struct estim_burst_params ebp;
+ signalVector sv(rx_sigproc_cfloat, 0, rs.convolved->size(), dummy_alloc, dummy_free);
+ detectAnyBurst(sv, tsc, BURST_THRESH, 4, rs.ct, 60, &ebp);
+ std::cerr << "delay:" << std::setw(3) << std::setprecision(2) << delay;
+ std::cerr << " va: " << std::setw(3) << std::setprecision(2) << va_burst_start;
+ std::cerr << " sg: " << std::setw(3) << std::setprecision(2) << ebp.toa;
+ std::cerr << " d: " << std::setw(3) << std::setprecision(2) << (ebp.toa * 4) - va_burst_start;
+ std::cerr << " ! " << float(va_burst_start + 13) / 4 << std::endl;
+ }
+ }
+ {
+ auto rs = gen_burst(CorrType::TSC, 0, tsc);
+ typeof(*rs.convolved) delay_buffer(rs.convolved->size() * 2); // plenty of space..
+
+ for (int delay = -10; delay < 10; delay++) {
+ std::fill(delay_buffer.begin(), delay_buffer.end(), 0);
+ std::copy(rs.convolved->begin(), rs.convolved->end(),
+ delay_buffer.begin() + delaybuffer_realoffset + delay);
+
+ auto conved_beg = &delay_buffer[delaybuffer_realoffset];
+
+ std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
+ float ncmax;
+ auto va_burst_start = get_norm_chan_imp_resp(conved_beg, &chan_imp_resp[0], &ncmax, tsc);
+
+ complex *rx_sigproc_cfloat = reinterpret_cast<complex *>(conved_beg);
+ struct estim_burst_params ebp;
+ signalVector sv(rx_sigproc_cfloat, 0, rs.convolved->size(), dummy_alloc, dummy_free);
+ detectAnyBurst(sv, tsc, BURST_THRESH, 4, rs.ct, 60, &ebp);
+ std::cerr << "delay:" << std::setw(3) << std::setprecision(2) << delay;
+ std::cerr << " va: " << std::setw(3) << std::setprecision(2) << va_burst_start;
+ std::cerr << " sg: " << std::setw(3) << std::setprecision(2) << ebp.toa;
+ std::cerr << " d: " << std::setw(3) << std::setprecision(2) << (ebp.toa * 4) - va_burst_start;
+ std::cerr << " ! " << float(va_burst_start + 19) / 4 << std::endl;
+ }
+ }
+}
+
+int main()
+{
+ convolve_init();
+ convert_init();
+ sigProcLibSetup();
+ initvita();
+
+ for (int i = 0; i < 1; i++) {
+ demod_real_burst(i);
+ demod_generated_burst(CorrType::RACH);
+ demod_generated_burst(CorrType::TSC);
+ demod_test_offsets();
+ }
+}
diff --git a/utils/va-test/demodbits_tsc7.s8 b/utils/va-test/demodbits_tsc7.s8
new file mode 100644
index 0000000..c91cd6c
--- /dev/null
+++ b/utils/va-test/demodbits_tsc7.s8
Binary files differ
diff --git a/utils/va-test/nb_chunk_tsc7.cfile b/utils/va-test/nb_chunk_tsc7.cfile
new file mode 100644
index 0000000..67e577e
--- /dev/null
+++ b/utils/va-test/nb_chunk_tsc7.cfile
Binary files differ