Compare commits

..

31 Commits

Author SHA1 Message Date
Eric
c03b7e04ee ipc: fix driver test restart
Change-Id: Id21f773d365f2e70129e1d463cf87b1213718be7
2020-08-26 17:35:18 +02:00
Eric
bf64d887a8 ipc: remove uhd dep
Change-Id: I950b9bd3cb52caf83c469fc424783d21750baa2a
2020-08-26 17:35:18 +02:00
Eric
056c5d8fab ipc: manually poll chan sock fds
We can't wait, we're in select, but we need to wait for a response from
the other side of the ipc interface -> only select our channels, so
other registered osmo fds are left alone, and we don't accidentally
handle stuff that should happen afterwards.

Change-Id: I4ffc8d825e5b4eba0fca8ea821b1d84e2b923266
2020-08-26 17:35:18 +02:00
Eric
6ada823b1a devices: reset internal smart sample buffers upon stop
They are too smart, they keep the timestamps.

Change-Id: Idb4b8f03eb5ffdfd6d3fdbc137b20e3ddc4cfa65
2020-08-26 17:35:18 +02:00
Eric
a8d3e91590 ipc:remove unused function
Change-Id: Ie5c3828df5187f0e93970bd05c82d97ef1d51102
2020-08-26 17:35:18 +02:00
Eric
cf9883b131 ipc: print initial ts
Change-Id: I9be02583e54592f27748918c16a7fa502bf88c47
2020-08-26 17:35:18 +02:00
Eric
2336ff6540 ipc: device, reorder includes
Change-Id: Iefb2298bcf89ee6e19a825b4774f9d6de9bff48b
2020-08-26 17:35:18 +02:00
Eric
2016e46064 ipc: shm unused cleanup
Change-Id: I7a68a0325ec788823c5d984efff03ee68a1b6f4d
2020-08-26 17:35:18 +02:00
Eric
443810195a ipc: increase read timeout, flush proper buf number
The other side might want to do a PPS sync on startup which is cutting
it close with one second of read timeout.
Additionally the number of buffers read to ensure proper flushing was
previously not updated with the actual number of buffers, which led to
stale buffers with wrong timestamps being stuck in the ipc interface.

Change-Id: I2ec8d422ef31f45e97d9091e5d814429b101ffe8
2020-08-26 17:35:18 +02:00
Eric
7434d0ec3b ipc: proper tx attenuation
Change-Id: Ifac4d689d1007e021cf6765bba1bbef1fde19bb2

squashme2

Change-Id: I1bb22a8f722b49e7f71d4d993957a6f26c420552
2020-08-26 17:35:18 +02:00
Eric
218d6fccff ipc: fix per channel start/stop, ipc uhd backend
Start/stop was previously fixed to channel 0, so proper multichannel
operation without using the mcbts mode was not possible.
UHD multichannel is rather annoying due to the alignment dance which
returns 0 reads, which may not be submitted to the ipc interface, since
empty buffers are treated as errors.

Change-Id: I441b1977e30a6c6c96b2e0543cedb3ce54d3ce31
2020-08-26 17:35:18 +02:00
Eric
d703fe43f3 ipc: fix the log messages for the channels
Change-Id: If9a4eab0dccfde6740a3669a7c1d34226b3912d1
2020-08-26 17:35:18 +02:00
Oliver Smith
fba23b665f contrib/osmo-trx.spec.in: add osmo-trx-ipc
Change-Id: Ib57980e8e102d6f77d0bffa47faafd929c82a288
2020-08-26 17:35:18 +02:00
Oliver Smith
473ea0afd7 contrib/systemd/osmo-trx-ipc.service: new file
Change-Id: Ic2ba7616c6909bd0a7e642554ecffa4939935b01
2020-08-26 17:35:18 +02:00
Oliver Smith
d1cc8b928e IPCDevice.h: remove limesuite include
Change-Id: I3808dcc69dd3ccc8372852c622660b7056c1be8a
2020-08-26 17:35:18 +02:00
Oliver Smith
a5ac4b272d configure.ac: add --with-ipc
Change-Id: Ie7b1b07479b81366cebaa081d80917ae868992b4
2020-08-26 17:35:18 +02:00
Oliver Smith
6570962dca gitignore: add new binaries
Change-Id: I9fd0b6cdb0b9d66b37e813cc8d35a28b9c9dc539
2020-08-26 17:35:18 +02:00
Eric
7a8b9cdbf0 ipc: add master socket number/path parameters
osmo-trx-ipc needs to know which master socket it should talk to, so
pass this path in the config device args like this:
dev-args ipc_msock=/path/to/socket

Additionally, add a master socket suffix number parameter to the ipc
backend,
ipc-driver-test -n 0 to start serving /tmp/ipc_sock0

Change-Id: I24857fbb74e5fc808b2a218957c723291de2ba04
2020-08-26 17:35:18 +02:00
Eric
6f20791120 shm: fix alignment
Change-Id: I8b593d792a992973e76dd19f7b678c9caa9c5e16
2020-08-26 17:35:18 +02:00
Eric
2845dc741c ipc: gain setting
Change-Id: I674d51fdcab2691853681f661b441364ed75207a
2020-08-26 17:35:18 +02:00
Eric
1067cb70a4 ipc: make ipc server uhd output more verbose
Change-Id: I9f2c15adff96e71a806ef09f17152c11d7c043aa
2020-08-26 17:35:18 +02:00
Eric
eb4a94241b ipc: print received path delay value
Change-Id: Ib072b01451084e055882d2867b558a5f8ede95ea
2020-08-26 17:35:18 +02:00
Eric
572eeda79c ipc: adjust over/underflow reporting
Change-Id: Iec2ca94ed8b6cf96d8797540cff43af6542e6437
2020-08-26 17:35:18 +02:00
Eric
39ae5730cd v1
Change-Id: I057e272623b41421f442206f550bcc8c60f4a747
2020-08-26 17:35:18 +02:00
Eric
4e131b2dd4 spawn the client as a thread
Change-Id: I86cd659e74ebdbf43c4352e83e5adb78f8fe6de5
2020-08-26 17:35:18 +02:00
Eric
37aa2ee85d silence warnings
Change-Id: I01b2dce8e04cdc21872e5bbc3eec7e0a5e3d1b0b
2020-08-26 17:35:18 +02:00
Eric
eed524b09e no undefined behavior by adding/substracting values != 0 from null ptr
Change-Id: Id2dc21e884991b3cca9f617aece12ac35959c1b2
2020-08-26 17:35:18 +02:00
Eric
218f968684 shm: fix warnings
Change-Id: I0e56539f699c39ab6e3546c96973184552da4d5f
2020-08-26 17:35:18 +02:00
Eric
62dfcac2f0 ipc: fix shm size calculation
Total size was previously only as large as sizeof(struct
ipc_shm_raw_stream) + sizeof(uint32_t)*num_buffers....

Change-Id: I1205b56a4b11bdf32fbdbfb82b67da36965a7981
2020-08-26 17:35:18 +02:00
Pau Espin Pedrol
b49c8ab965 WIP: comments
Change-Id: Ia2631a869e23af520b9eade192dfdb032174c689
2020-08-26 17:35:18 +02:00
Pau Espin Pedrol
d5ff10069c WIP: osmo-trx-ipc
Change-Id: Ice63d3499026293ade8aad675ff7a883bcdd5756
2020-08-26 17:35:18 +02:00
140 changed files with 3113 additions and 7790 deletions

View File

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

View File

@@ -25,7 +25,7 @@ AllowShortLoopsOnASingleLine: false
AlwaysBreakAfterDefinitionReturnType: None
AlwaysBreakAfterReturnType: None
AlwaysBreakBeforeMultilineStrings: false
AlwaysBreakTemplateDeclarations: true
AlwaysBreakTemplateDeclarations: false
BinPackArguments: true
BinPackParameters: true
BraceWrapping:
@@ -515,7 +515,7 @@ SpacesInContainerLiterals: false
SpacesInCStyleCastParentheses: false
SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
Standard: Cpp03
TabWidth: 8
UseTab: Always
...

18
.gitignore vendored
View File

@@ -6,17 +6,6 @@ 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
# tests
tests/CommonLibs/BitVectorTest
@@ -73,9 +62,7 @@ doc/manuals/*.pdf
doc/manuals/*__*.png
doc/manuals/*.check
doc/manuals/generated/
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/osmomsc-usermanual.xml
doc/manuals/common
doc/manuals/build
@@ -83,6 +70,3 @@ contrib/osmo-trx.spec
!contrib/osmo-trx.spec.in
utils/osmo-prbs-tool
/.qtc_clangd/*
/.cache/*
/.vscode/*

3
.gitmodules vendored
View File

@@ -1,3 +0,0 @@
[submodule "osmocom-bb"]
path = osmocom-bb
url = https://gitea.osmocom.org/phone-side/osmocom-bb.git

View File

@@ -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)
D* read(const K &key, unsigned timeout) const
{
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)
D* read(const K &key) const
{
ScopedLock lock(mLock);
typename Map::const_iterator iter = mMap.find(key);

View File

@@ -50,19 +50,19 @@ extern "C" {
#endif
#define LOG(level) \
Log(DMAIN, LOGL_##level, __BASE_FILE__, __LINE__).get()
Log(DMAIN, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
#define LOGC(category, level) \
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get()
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
#define LOGLV(category, level) \
Log(category, level, __BASE_FILE__, __LINE__).get()
Log(category, level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "] "
#define LOGSRC(category, level, file, line) \
Log(category, level, file, line).get()
Log(category, level, file, line).get() << "[tid=" << pthread_self() << "] "
#define LOGCHAN(chan, category, level) \
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[chan=" << chan << "] "
Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() << "[tid=" << pthread_self() << "][chan=" << chan << "] "
/**
A C++ stream-based thread-safe logger.

View File

@@ -22,8 +22,8 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
noinst_LTLIBRARIES = libcommon.la
@@ -37,12 +37,7 @@ libcommon_la_SOURCES = \
trx_rate_ctr.cpp \
trx_vty.c \
debug.c
libcommon_la_LIBADD = \
$(LIBOSMOCORE_LIBS) \
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_LIBS) \
-lpthread \
$(NULL)
libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
noinst_HEADERS = \
BitVector.h \

View File

@@ -12,6 +12,10 @@
* 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

View File

@@ -32,9 +32,11 @@
#include "Timeval.h"
#include "Logger.h"
extern "C" {
#include <osmocom/core/thread.h>
}
#ifndef gettid
#include <sys/syscall.h>
#define gettid() syscall(SYS_gettid)
#endif
using namespace std;
@@ -43,11 +45,76 @@ 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 = osmo_gettid();
pid_t tid = gettid();
if (pthread_setname_np(selfid, name) == 0) {
LOG(INFO) << "Thread "<< selfid << " (task " << tid << ") set name: " << name;
} else {

View File

@@ -28,96 +28,143 @@
#ifndef THREADS_H
#define THREADS_H
#include <chrono>
#include <mutex>
#include <condition_variable>
#include "config.h"
#include <pthread.h>
#include <iostream>
#include <cassert>
#include <assert.h>
#include <unistd.h>
#include "config.h"
#include "Timeval.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
//@}
//@}
/**@defgroup C++ wrappers for pthread mechanisms. */
//@{
/** A class for recursive mutexes. */
/** A class for recursive mutexes based on pthread_mutex. */
class Mutex {
std::recursive_mutex m;
public:
private:
void lock() {
m.lock();
}
pthread_mutex_t mMutex;
pthread_mutexattr_t mAttribs;
bool trylock() {
return m.try_lock();
}
public:
void unlock() {
m.unlock();
}
Mutex();
~Mutex();
void lock() { pthread_mutex_lock(&mMutex); }
bool trylock() { return pthread_mutex_trylock(&mMutex)==0; }
void unlock() { pthread_mutex_unlock(&mMutex); }
friend class Signal;
};
class ScopedLock {
Mutex &mMutex;
public:
ScopedLock(Mutex &wMutex) : mMutex(wMutex) {
mMutex.lock();
}
~ScopedLock() {
mMutex.unlock();
}
private:
Mutex& mMutex;
public:
ScopedLock(Mutex& wMutex) :mMutex(wMutex) { mMutex.lock(); }
~ScopedLock() { mMutex.unlock(); }
};
/** A C++ interthread signal. */
/** A C++ interthread signal based on pthread condition variables. */
class Signal {
/* any, because for some reason our mutex is recursive... */
std::condition_variable_any mSignal;
public:
private:
void wait(Mutex &wMutex, unsigned timeout) {
mSignal.wait_for(wMutex.m, std::chrono::milliseconds(timeout));
}
mutable pthread_cond_t mSignal;
void wait(Mutex &wMutex) {
mSignal.wait(wMutex.m);
}
public:
void signal() {
mSignal.notify_one();
}
Signal() { int s = pthread_cond_init(&mSignal,NULL); assert(!s); }
~Signal() { pthread_cond_destroy(&mSignal); }
/**
Block for the signal up to the cancellation timeout.
Under Linux, spurious returns are possible.
*/
void wait(Mutex& wMutex, unsigned timeout) const;
/**
Block for the signal.
Under Linux, spurious returns are possible.
*/
void wait(Mutex& wMutex) const
{ pthread_cond_wait(&mSignal,&wMutex.mMutex); }
void signal() { pthread_cond_signal(&mSignal); }
void broadcast() { pthread_cond_broadcast(&mSignal); }
void broadcast() {
mSignal.notify_all();
}
};
#define START_THREAD(thread,function,argument) \
thread.start((void *(*)(void*))function, (void*)argument);
void set_selfthread_name(const char *name);
void 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;
}
/**
@@ -125,17 +172,14 @@ 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);
@@ -143,10 +187,7 @@ class Thread {
}
/** Send cancellation to thread */
void cancel()
{
pthread_cancel(mThread);
}
void cancel() { pthread_cancel(mThread); }
};
#ifdef HAVE_ATOMIC_OPS

View File

@@ -12,6 +12,10 @@
* 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>

View File

@@ -12,6 +12,10 @@
* 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

View File

@@ -36,11 +36,6 @@
#include <assert.h>
#include <stdlib.h>
#ifndef __OPTIMIZE__
#define assert_no_opt(x) assert(x)
#else
#define assert_no_opt(x)
#endif
// We can't use Logger.h in this file...
extern int gVectorDebug;
#define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;}
@@ -86,8 +81,8 @@ template <class T> class Vector {
/** Return the size of the Vector. */
size_t size() const
{
assert_no_opt(mStart>=mData);
assert_no_opt(mEnd>=mStart);
assert(mStart>=mData);
assert(mEnd>=mStart);
return mEnd - mStart;
}
@@ -117,7 +112,7 @@ template <class T> class Vector {
/** Reduce addressable size of the Vector, keeping content. */
void shrink(size_t newSize)
{
assert_no_opt(newSize <= mEnd - mStart);
assert(newSize <= mEnd - mStart);
mEnd = mStart + newSize;
}
@@ -204,7 +199,7 @@ template <class T> class Vector {
{
T* wStart = mStart + start;
T* wEnd = wStart + span;
assert_no_opt(wEnd<=mEnd);
assert(wEnd<=mEnd);
return Vector<T>(NULL,wStart,wEnd);
}
@@ -213,7 +208,7 @@ template <class T> class Vector {
{
T* wStart = mStart + start;
T* wEnd = wStart + span;
assert_no_opt(wEnd<=mEnd);
assert(wEnd<=mEnd);
return Vector<T>(NULL,wStart,wEnd);
}
@@ -233,8 +228,8 @@ template <class T> class Vector {
unsigned int i;
T* dst = other.mStart + start;
T* src = mStart;
assert_no_opt(dst+span<=other.mEnd);
assert_no_opt(mStart+span<=mEnd);
assert(dst+span<=other.mEnd);
assert(mStart+span<=mEnd);
for (i = 0; i < span; i++, src++, dst++)
*dst = *src;
/*TODO if not non-trivially copiable type class, optimize:
@@ -255,8 +250,8 @@ template <class T> class Vector {
void segmentCopyTo(Vector<T>& other, size_t start, size_t span) const
{
const T* base = mStart + start;
assert_no_opt(base+span<=mEnd);
assert_no_opt(other.mStart+span<=other.mEnd);
assert(base+span<=mEnd);
assert(other.mStart+span<=other.mEnd);
memcpy(other.mStart,base,span*sizeof(T));
}
@@ -270,8 +265,8 @@ template <class T> class Vector {
{
const T* baseFrom = mStart + from;
T* baseTo = mStart + to;
assert_no_opt(baseFrom+span<=mEnd);
assert_no_opt(baseTo+span<=mEnd);
assert(baseFrom+span<=mEnd);
assert(baseTo+span<=mEnd);
memmove(baseTo,baseFrom,span*sizeof(T));
}
@@ -285,7 +280,7 @@ template <class T> class Vector {
{
T* dp=mStart+start;
T* end=dp+length;
assert_no_opt(end<=mEnd);
assert(end<=mEnd);
while (dp<end) *dp++=val;
}
@@ -297,13 +292,13 @@ template <class T> class Vector {
T& operator[](size_t index)
{
assert_no_opt(mStart+index<mEnd);
assert(mStart+index<mEnd);
return mStart[index];
}
const T& operator[](size_t index) const
{
assert_no_opt(mStart+index<mEnd);
assert(mStart+index<mEnd);
return mStart[index];
}

View File

@@ -5,8 +5,6 @@
* osmo-trx (CXX, dir Transceiver52)
*/
#include <stdbool.h>
enum FillerType {
FILLER_DUMMY,
FILLER_ZERO,
@@ -20,52 +18,3 @@ enum ReferenceType {
REF_EXTERNAL,
REF_GPS,
};
/* Maximum number of physical RF channels */
#define TRX_CHAN_MAX 8
struct trx_ctx;
struct trx_chan {
struct trx_ctx *trx; /* backpointer */
unsigned int idx; /* channel index */
char *rx_path;
char *tx_path;
};
struct trx_cfg {
char *bind_addr;
char *remote_addr;
char *dev_args;
unsigned int base_port;
unsigned int tx_sps;
unsigned int rx_sps;
unsigned int rtsc;
unsigned int rach_delay;
enum ReferenceType clock_ref;
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;
bool egprs;
unsigned int sched_rr;
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;
};

View File

@@ -21,6 +21,18 @@
* 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"
@@ -81,3 +93,15 @@ 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
}

View File

@@ -1,6 +1,7 @@
#pragma once
#include <stdbool.h>
#include <sys/types.h>
#include <osmocom/core/logging.h>
@@ -18,6 +19,12 @@ enum {
DCTR,
};
#define CLOGCHAN(chan, category, level, fmt, args...) do { \
LOGP(category, level, "[chan=%zu] " fmt, chan, ##args); \
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); \
} while(0)

View File

@@ -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_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_RX_OVERRUNS);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_RX_OVERRUNS];
rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_overruns - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_TX_UNDERRUNS);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_TX_UNDERRUNS];
rate_ctr_add(ctr, dev_ctrs_pending[chan].tx_underruns - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_RX_DROP_EV);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_RX_DROP_EV];
rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_dropped_events - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_RX_DROP_SMPL);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_RX_DROP_SMPL];
rate_ctr_add(ctr, dev_ctrs_pending[chan].rx_dropped_samples - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_TX_DROP_EV);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_DEV_TX_DROP_EV];
rate_ctr_add(ctr, dev_ctrs_pending[chan].tx_dropped_events - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_DEV_TX_DROP_SMPL);
ctr = &rate_ctrs[chan]->ctr[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_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TX_STALE_BURSTS);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TX_STALE_BURSTS];
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_stale_bursts - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TX_UNAVAILABLE_BURSTS];
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_unavailable_bursts - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TRXD_FN_REPEATED);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TRXD_FN_REPEATED];
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_repeated - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TRXD_FN_OUTOFORDER);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TRXD_FN_OUTOFORDER];
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_outoforder - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_TRXD_FN_SKIPPED);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_TRXD_FN_SKIPPED];
rate_ctr_add(ctr, trx_ctrs_pending[chan].tx_trxd_fn_skipped - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_RX_EMPTY_BURST);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_RX_EMPTY_BURST];
rate_ctr_add(ctr, trx_ctrs_pending[chan].rx_empty_burst - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_RX_CLIPPING);
ctr = &rate_ctrs[chan]->ctr[TRX_CTR_TRX_RX_CLIPPING];
rate_ctr_add(ctr, trx_ctrs_pending[chan].rx_clipping - ctr->current);
ctr = rate_ctr_group_get_ctr(rate_ctrs[chan], TRX_CTR_TRX_RX_NO_BURST_DETECTED);
ctr = &rate_ctrs[chan]->ctr[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,7 +214,6 @@ 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:
@@ -223,8 +222,7 @@ 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_r(errno, err_buf, sizeof(err_buf));
LOGC(DCTR, ERROR) << "Failed to schedule timerfd: " << errno << " = "<< strerror(errno);
}
dev_rate_ctr_mutex.unlock();
break;
@@ -234,8 +232,7 @@ 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_r(errno, err_buf, sizeof(err_buf));
LOGC(DCTR, ERROR) << "Failed to schedule timerfd: " << errno << " = "<< strerror(errno);
}
trx_rate_ctr_mutex.unlock();
break;
@@ -266,7 +263,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_ctr_group_get_ctr(rate_ctrs[chan], ctr_thr->ctr_id);
rate_ctr = &rate_ctrs[chan]->ctr[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) {
@@ -312,7 +309,8 @@ static void threshold_timer_update_intv() {
return;
if (llist_empty(&threshold_list)) {
osmo_timer_del(&threshold_timer);
if (osmo_timer_pending(&threshold_timer))
osmo_timer_del(&threshold_timer);
return;
}

View File

@@ -67,15 +67,6 @@ 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)
{
@@ -121,7 +112,7 @@ DEFUN(cfg_trx, cfg_trx_cmd,
}
DEFUN(cfg_bind_ip, cfg_bind_ip_cmd,
"bind-ip " VTY_IPV4_CMD,
"bind-ip A.B.C.D",
"Set the IP address for the local bind\n"
"IPv4 Address\n")
{
@@ -133,7 +124,7 @@ DEFUN(cfg_bind_ip, cfg_bind_ip_cmd,
}
DEFUN(cfg_remote_ip, cfg_remote_ip_cmd,
"remote-ip " VTY_IPV4_CMD,
"remote-ip A.B.C.D",
"Set the IP address for the remote BTS\n"
"IPv4 Address\n")
{
@@ -171,9 +162,7 @@ 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"
"1 Sample-per-Symbol\n"
"4 Samples-per-Symbol\n")
"Tx Samples-per-Symbol\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -185,9 +174,7 @@ 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"
"1 Sample-per-Symbol\n"
"4 Samples-per-Symbol\n")
"Rx Samples-per-Symbol\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -212,8 +199,7 @@ DEFUN(cfg_clock_ref, cfg_clock_ref_cmd,
DEFUN(cfg_multi_arfcn, cfg_multi_arfcn_cmd,
"multi-arfcn (disable|enable)",
"Multi-ARFCN transceiver mode (default=disable)\n"
"Enable multi-ARFCN mode\n" "Disable multi-ARFCN mode\n")
"Enable multi-ARFCN transceiver (default=disable)\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -244,125 +230,21 @@ 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]",
"rssi-offset FLOAT",
"Set the RSSI to dBm offset in dB (default=0)\n"
"RSSI to dBm offset in dB\n"
"Add to the default rssi-offset value instead of completely replacing it\n")
"RSSI to dBm offset in dB\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
trx->cfg.rssi_offset = atof(argv[0]);
trx->cfg.force_rssi_offset = (argc == 1);
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 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")
"Swap channels (default=disable)\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -379,9 +261,7 @@ DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
DEFUN(cfg_egprs, cfg_egprs_cmd,
"egprs (disable|enable)",
"EGPRS (8-PSK demodulation) support (default=disable)\n"
"Disable EGPRS (8-PSK demodulation) support\n"
"Enable EGPRS (8-PSK demodulation) support\n")
"Enable EDGE receiver (default=disable)\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -398,9 +278,7 @@ DEFUN(cfg_egprs, cfg_egprs_cmd,
DEFUN(cfg_ext_rach, cfg_ext_rach_cmd,
"ext-rach (disable|enable)",
"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")
"Enable extended (11-bit) RACH (default=disable)\n")
{
struct trx_ctx *trx = trx_from_vty(vty);
@@ -439,11 +317,20 @@ DEFUN(cfg_stack_size, cfg_stack_size_cmd,
return CMD_SUCCESS;
}
#define CFG_FILLER_DOC_STR \
"Filler burst settings\n"
DEFUN(cfg_filler, cfg_filler_type_cmd,
"AUTO-GENERATED", "AUTO-GENERATED")
"filler type (zero|dummy|random-nb-gmsk|random-nb-8psk|random-ab)",
"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"
)
{
struct trx_ctx *trx = trx_from_vty(vty);
// trx->cfg.filler is unsigned, so we need an interim int var to detect errors
@@ -460,7 +347,7 @@ DEFUN(cfg_filler, cfg_filler_type_cmd,
DEFUN(cfg_test_rtsc, cfg_filler_tsc_cmd,
"filler tsc <0-7>",
CFG_FILLER_DOC_STR
"Filler burst settings\n"
"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")
@@ -474,7 +361,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>",
CFG_FILLER_DOC_STR
"Filler burst settings\n"
"Set the delay for Access Burst random fillers. Used only with 'random-ab' filler type. (default=0)\n"
"RACH delay in symbols\n")
{
@@ -528,17 +415,17 @@ static int vty_intv_name_2_id(const char* str) {
INTV_STR_VAL(per-hour) \
INTV_STR_VAL(per-day)
DEFUN_ATTR(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,
"ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS,
"Threshold rate for error counter\n"
THRESHOLD_STRS
"Value to set for threshold\n"
INTV_STRS,
CMD_ATTR_IMMEDIATE)
DEFUN(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,
"ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS,
"Threshold rate for error counter\n"
THRESHOLD_STRS
"Value to set for threshold\n"
INTV_STRS)
{
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",
@@ -559,17 +446,17 @@ DEFUN_ATTR(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,
return CMD_SUCCESS;
}
DEFUN_ATTR(cfg_no_ctr_error_threshold, cfg_no_ctr_error_threshold_cmd,
"no ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS,
NO_STR "Threshold rate for error counter\n"
THRESHOLD_STRS
"Value to set for threshold\n"
INTV_STRS,
CMD_ATTR_IMMEDIATE)
DEFUN(cfg_no_ctr_error_threshold, cfg_no_ctr_error_threshold_cmd,
"no ctr-error-threshold " THRESHOLD_ARGS " <0-65535> " INTV_ARGS,
NO_STR "Threshold rate for error counter\n"
THRESHOLD_STRS
"Value to set for threshold\n"
INTV_STRS)
{
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",
@@ -633,12 +520,6 @@ DEFUN(cfg_chan_rx_path, cfg_chan_rx_path_cmd,
{
struct trx_chan *chan = vty->index;
if (chan->trx->cfg.multi_arfcn && chan->idx > 0) {
vty_out(vty, "%% Setting 'rx-path' for chan %u in multi-ARFCN mode "
"does not make sense, because only chan 0 is used%s",
chan->idx, VTY_NEWLINE);
}
osmo_talloc_replace_string(chan->trx, &chan->rx_path, argv[0]);
return CMD_SUCCESS;
@@ -651,12 +532,6 @@ DEFUN(cfg_chan_tx_path, cfg_chan_tx_path_cmd,
{
struct trx_chan *chan = vty->index;
if (chan->trx->cfg.multi_arfcn && chan->idx > 0) {
vty_out(vty, "%% Setting 'tx-path' for chan %u in multi-ARFCN mode "
"does not make sense, because only chan 0 is used%s",
chan->idx, VTY_NEWLINE);
}
osmo_talloc_replace_string(chan->trx, &chan->tx_path, argv[0]);
return CMD_SUCCESS;
@@ -680,7 +555,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 (strlen(trx->cfg.dev_args))
if (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);
@@ -691,11 +566,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);
if (trx->cfg.rssi_offset != 0)
vty_out(vty, " rssi-offset %f%s", trx->cfg.rssi_offset, VTY_NEWLINE);
vty_out(vty, " swap-channels %s%s", trx->cfg.swap_channels ? "enable" : "disable", VTY_NEWLINE);
vty_out(vty, " egprs %s%s", trx->cfg.egprs ? "enable" : "disable", VTY_NEWLINE);
vty_out(vty, " ext-rach %s%s", trx->cfg.ext_rach ? "enable" : "disable", VTY_NEWLINE);
@@ -709,18 +581,6 @@ 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++) {
@@ -842,20 +702,12 @@ struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx)
trx->cfg.tx_sps = DEFAULT_TX_SPS;
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);
@@ -871,7 +723,6 @@ 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);
@@ -885,12 +736,6 @@ 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);

View File

@@ -8,6 +8,8 @@ extern struct vty_app_info g_vty_info;
extern const struct value_string clock_ref_names[];
extern const struct value_string filler_names[];
/* Maximum number of physical RF channels */
#define TRX_CHAN_MAX 8
/* Maximum number of carriers in multi-ARFCN mode */
#define TRX_MCHAN_MAX 3
@@ -33,8 +35,38 @@ extern const struct value_string filler_names[];
#define DEFAULT_TRX_IP "127.0.0.1"
#define DEFAULT_CHANS 1
struct trx_ctx;
struct trx_chan {
struct trx_ctx *trx; /* backpointer */
unsigned int idx; /* channel index */
char *rx_path;
char *tx_path;
};
struct trx_ctx {
struct trx_cfg cfg;
struct {
char *bind_addr;
char *remote_addr;
char *dev_args;
unsigned int base_port;
unsigned int tx_sps;
unsigned int rx_sps;
unsigned int rtsc;
unsigned int rach_delay;
enum ReferenceType clock_ref;
enum FillerType filler;
bool multi_arfcn;
double offset;
double rssi_offset;
bool swap_channels;
bool ext_rach;
bool egprs;
unsigned int sched_rr;
unsigned int stack_size;
unsigned int num_chans;
struct trx_chan chans[TRX_CHAN_MAX];
} cfg;
};
int trx_vty_init(struct trx_ctx* trx);

View File

@@ -26,22 +26,15 @@ 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 += \
SUBDIRS = \
doc \
CommonLibs \
GSM \
Transceiver52M \
contrib \
tests \
utils \
doc \
$(NULL)
utils
EXTRA_DIST = \
LEGAL \

View File

@@ -1,5 +1,5 @@
About OsmoTRX
=============
About OsmTRX
============
OsmoTRX is a software-defined radio transceiver that implements the Layer 1
physical layer of a BTS comprising the following 3GPP specifications:
@@ -9,12 +9,14 @@ physical layer of a BTS comprising the following 3GPP specifications:
* TS 05.04 "Modulation"
* TS 05.10 "Radio subsystem synchronization"
OsmoTRX is originally based on the transceiver code from the
OsmoTRX is 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, specifically within the Osmocom stack. Used together with
[OsmoBTS](https://osmocom.org/projects/osmobts/wiki) you can get a pretty
standard GSM BTS with Abis interface as per the relevant 3GPP specifications.
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.
Homepage
--------
@@ -27,9 +29,9 @@ GIT Repository
You can clone from the official osmo-trx.git repository using
git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-trx`
git clone git://git.osmocom.org/osmo-trx.git
There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-trx>
There is a cgit interface at <http://git.osmocom.org/osmo-trx/>
Documentation
-------------
@@ -37,7 +39,7 @@ 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
<https://ftp.osmocom.org/docs/latest/osmotrx-usermanual.pdf>.
<http://ftp.osmocom.org/docs/latest/osmotrx-usermanual.pdf>.
Mailing List
------------

View File

@@ -0,0 +1,2 @@
* 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.

View File

@@ -244,7 +244,6 @@ ChannelizerBase::~ChannelizerBase()
free(subFilters[i]);
delete[] hist[i];
}
free(subFilters);
fft_free(fftInput);
fft_free(fftOutput);

View File

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

View File

@@ -40,9 +40,7 @@ COMMON_SOURCES = \
ChannelizerBase.cpp \
Channelizer.cpp \
Synthesis.cpp \
proto_trxd.c \
grgsm_vitac/grgsm_vitac.cpp \
grgsm_vitac/viterbi_detector.cc
proto_trxd.c
libtransceiver_common_la_SOURCES = \
$(COMMON_SOURCES) \
@@ -75,45 +73,6 @@ 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/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/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
@@ -124,17 +83,6 @@ 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
@@ -157,32 +105,9 @@ 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
osmo_trx_ipc_LDADD = \
$(builddir)/device/ipc/libdevice.la \
$(COMMON_LDADD)
osmo_trx_ipc_CPPFLAGS = $(AM_CPPFLAGS)
endif

View File

@@ -13,6 +13,10 @@
* 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>
@@ -95,7 +99,6 @@ void Resampler::initFilters(float bw)
reverse(&part[0], &part[filt_len]);
}
#ifndef __OPTIMIZE__
static bool check_vec_len(int in_len, int out_len, int p, int q)
{
if (in_len % q) {
@@ -126,15 +129,14 @@ static bool check_vec_len(int in_len, int out_len, int p, int q)
return true;
}
#endif
int Resampler::rotate(const float *in, size_t in_len, float *out, size_t out_len)
{
int n, path;
#ifndef __OPTIMIZE__
if (!check_vec_len(in_len, out_len, p, q))
return -1;
#endif
/* Generate output from precomputed input/output paths */
for (size_t i = 0; i < out_len; i++) {
n = in_index[i];

View File

@@ -13,6 +13,10 @@
* 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_

View File

@@ -29,7 +29,6 @@
#include <fstream>
#include "Transceiver.h"
#include <Logger.h>
#include <grgsm_vitac/grgsm_vitac.h>
extern "C" {
#include "osmo_signal.h"
@@ -63,8 +62,7 @@ static void dispatch_trx_rate_ctr_change(TransceiverState *state, unsigned int c
}
TransceiverState::TransceiverState()
: mFiller(FILLER_ZERO), mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT),
mPower(0.0), mMuted(false), first_dl_fn_rcv()
: mFiller(FILLER_ZERO), mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
{
for (int i = 0; i < 8; i++) {
chanType[i] = Transceiver::NONE;
@@ -133,22 +131,26 @@ bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t r
return false;
}
Transceiver::Transceiver(const struct trx_cfg *cfg,
Transceiver::Transceiver(int wBasePort,
const char *TRXAddress,
const char *GSMcoreAddress,
size_t tx_sps, size_t rx_sps, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface)
: 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)
RadioInterface *wRadioInterface,
double wRssiOffset, int wStackSize)
: mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress),
mClockSocket(-1), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
rssiOffset(wRssiOffset), stackSize(wStackSize),
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mExtRACH(false), mEdge(false),
mOn(false), mForceClockInterface(false),
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0),
mWriteBurstToDiskMask(0)
{
txFullScale = mRadioInterface->fullScaleInputValue();
rxFullScale = mRadioInterface->fullScaleOutputValue();
for (size_t i = 0; i < ARRAY_SIZE(mHandover); i++) {
for (size_t j = 0; j < ARRAY_SIZE(mHandover[i]); j++)
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++)
mHandover[i][j] = false;
}
}
@@ -196,9 +198,11 @@ int Transceiver::ctrl_sock_cb(struct osmo_fd *bfd, unsigned int flags)
* are still expected to report clock indications through control channel
* activity.
*/
bool Transceiver::init()
bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay,
bool edge, bool ext_rach)
{
int d_srcport, d_dstport, c_srcport, c_dstport;
if (!mChans) {
LOG(FATAL) << "No channels assigned";
return false;
@@ -209,34 +213,41 @@ bool Transceiver::init()
return false;
}
initvita();
mExtRACH = ext_rach;
mEdge = edge;
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)
if (filler == FILLER_DUMMY)
mStates[0].mRetrans = true;
/* Setup sockets */
mClockSocket = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
cfg->bind_addr, cfg->base_port,
cfg->remote_addr, cfg->base_port + 100,
mLocalAddr.c_str(), mBasePort,
mRemoteAddr.c_str(), mBasePort + 100,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (mClockSocket < 0)
return false;
for (size_t i = 0; i < mChans; i++) {
int rv;
FillerType filler = cfg->filler;
c_srcport = cfg->base_port + 2 * i + 1;
c_dstport = cfg->base_port + 2 * i + 101;
d_srcport = cfg->base_port + 2 * i + 2;
d_dstport = cfg->base_port + 2 * i + 102;
c_srcport = mBasePort + 2 * i + 1;
c_dstport = mBasePort + 2 * i + 101;
d_srcport = mBasePort + 2 * i + 2;
d_dstport = mBasePort + 2 * i + 102;
rv = osmo_sock_init2_ofd(&mCtrlSockets[i].conn_bfd, AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
cfg->bind_addr, c_srcport,
cfg->remote_addr, c_dstport,
mLocalAddr.c_str(), c_srcport,
mRemoteAddr.c_str(), c_dstport,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (rv < 0)
return false;
@@ -246,8 +257,8 @@ bool Transceiver::init()
mDataSockets[i] = osmo_sock_init2(AF_UNSPEC, SOCK_DGRAM, IPPROTO_UDP,
cfg->bind_addr, d_srcport,
cfg->remote_addr, d_dstport,
mLocalAddr.c_str(), d_srcport,
mRemoteAddr.c_str(), d_dstport,
OSMO_SOCK_F_BIND | OSMO_SOCK_F_CONNECT);
if (mDataSockets[i] < 0)
return false;
@@ -255,7 +266,7 @@ bool Transceiver::init()
if (i && filler == FILLER_DUMMY)
filler = FILLER_ZERO;
mStates[i].init(filler, cfg->tx_sps, txFullScale, cfg->rtsc, cfg->rach_delay);
mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
}
/* Randomize the central clock */
@@ -297,8 +308,8 @@ bool Transceiver::start()
}
/* Device is running - launch I/O threads */
mRxLowerLoopThread = new Thread(cfg->stack_size);
mTxLowerLoopThread = new Thread(cfg->stack_size);
mRxLowerLoopThread = new Thread(stackSize);
mTxLowerLoopThread = new Thread(stackSize);
mTxLowerLoopThread->start((void * (*)(void*))
TxLowerLoopAdapter,(void*) this);
mRxLowerLoopThread->start((void * (*)(void*))
@@ -309,14 +320,14 @@ bool Transceiver::start()
TrxChanThParams *params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
params->trx = this;
params->num = i;
mRxServiceLoopThreads[i] = new Thread(cfg->stack_size);
mRxServiceLoopThreads[i] = new Thread(stackSize);
mRxServiceLoopThreads[i]->start((void * (*)(void*))
RxUpperLoopAdapter, (void*) params);
params = (TrxChanThParams *)malloc(sizeof(struct TrxChanThParams));
params->trx = this;
params->num = i;
mTxPriorityQueueServiceLoopThreads[i] = new Thread(cfg->stack_size);
mTxPriorityQueueServiceLoopThreads[i] = new Thread(stackSize);
mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
TxUpperLoopAdapter, (void*) params);
}
@@ -389,11 +400,11 @@ void Transceiver::addRadioVector(size_t chan, BitVector &bits,
/* Use the number of bits as the EDGE burst indicator */
if (bits.size() == EDGE_BURST_NBITS)
burst = modulateEdgeBurst(bits, cfg->tx_sps);
burst = modulateEdgeBurst(bits, mSPSTx);
else
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), cfg->tx_sps);
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
scaleVector(*burst, txFullScale * pow(10, (double) -RSSI / 20));
scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
radio_burst = new radioVector(wTime, burst);
@@ -429,7 +440,7 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime)
state = &mStates[i];
ratectr_changed = false;
zeros[i] = state->chanType[TN] == NONE || state->mMuted;
zeros[i] = state->chanType[TN] == NONE;
Mutex *mtx = mTxPriorityQueues[i].getMutex();
mtx->lock();
@@ -554,16 +565,16 @@ CorrType Transceiver::expectedCorrType(GSM::Time currTime,
break;
case IV:
case VI:
return cfg->ext_rach ? EXT_RACH : RACH;
return mExtRACH ? EXT_RACH : RACH;
break;
case V: {
int mod51 = burstFN % 51;
if ((mod51 <= 36) && (mod51 >= 14))
return cfg->ext_rach ? EXT_RACH : RACH;
return mExtRACH ? EXT_RACH : RACH;
else if ((mod51 == 4) || (mod51 == 5))
return cfg->ext_rach ? EXT_RACH : RACH;
return mExtRACH ? EXT_RACH : RACH;
else if ((mod51 == 45) || (mod51 == 46))
return cfg->ext_rach ? EXT_RACH : RACH;
return mExtRACH ? EXT_RACH : RACH;
else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
return RACH;
else
@@ -581,11 +592,11 @@ CorrType Transceiver::expectedCorrType(GSM::Time currTime,
case XIII: {
int mod52 = burstFN % 52;
if ((mod52 == 12) || (mod52 == 38))
return RACH; /* RACH is always 8-bit on PTCCH/U */
return mExtRACH ? EXT_RACH : RACH;
else if ((mod52 == 25) || (mod52 == 51))
return IDLE;
else /* Enable 8-PSK burst detection if EDGE is enabled */
return cfg->egprs ? EDGE : TSC;
return mEdge ? EDGE : TSC;
break;
}
case LOOPBACK:
@@ -610,51 +621,6 @@ void writeToFile(radioVector *radio_burst, size_t chan)
outfile.close();
}
double Transceiver::rssiOffset(size_t chan)
{
if (cfg->force_rssi_offset)
return cfg->rssi_offset;
return mRadioInterface->rssiOffset(chan) + cfg->rssi_offset;
}
static SoftVector *demodAnyBurst_va(const signalVector &burst, CorrType type, int sps, int rach_max_toa, int tsc)
{
auto conved_beg = reinterpret_cast<const std::complex<float> *>(&burst.begin()[0]);
std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
float ncmax;
const unsigned burst_len_bits = 148 + 8;
char demodded_softbits[burst_len_bits];
SoftVector *bits = new SoftVector(burst_len_bits);
if (type == CorrType::TSC) {
auto rach_burst_start = get_norm_chan_imp_resp(conved_beg, chan_imp_resp, &ncmax, tsc);
rach_burst_start = std::max(rach_burst_start, 0);
detect_burst_nb(conved_beg, chan_imp_resp, rach_burst_start, demodded_softbits);
} else {
auto normal_burst_start = get_access_imp_resp(conved_beg, chan_imp_resp, &ncmax, 0);
normal_burst_start = std::max(normal_burst_start, 0);
detect_burst_ab(conved_beg, chan_imp_resp, normal_burst_start, demodded_softbits, rach_max_toa);
}
float *s = &bits->begin()[0];
for (unsigned int i = 0; i < 148; i++)
s[i] = demodded_softbits[i] * -1;
for (unsigned int i = 148; i < burst_len_bits; i++)
s[i] = 0;
return bits;
}
#define USE_VA
#ifdef USE_VA
// signalvector is owning despite claiming not to, but we can pretend, too..
static void dummy_free(void *wData){};
static void *dummy_alloc(size_t newSize)
{
return 0;
};
#endif
/*
* Pull bursts from the FIFO and handle according to the slot
* and burst correlation type. Equalzation is currently disabled.
@@ -674,10 +640,6 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
SoftVector *rxBurst;
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();
@@ -687,7 +649,7 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
}
/* Set time and determine correlation type */
burstTime = radio_burst->getTime() + cfg->ul_fn_offset;
burstTime = radio_burst->getTime();
CorrType type = expectedCorrType(burstTime, chan);
/* Initialize struct bi */
@@ -716,13 +678,9 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
return -ENOENT;
}
/* If TRX RF is locked/muted by BTS, send idle burst indications */
if (state->mMuted)
goto ret_idle;
/* Select the diversity channel with highest energy */
for (size_t i = 0; i < radio_burst->chans(); i++) {
float pow = energyDetect(*radio_burst->getVector(i), 20 * cfg->rx_sps);
float pow = energyDetect(*radio_burst->getVector(i), 20 * mSPSRx);
if (pow > max) {
max = pow;
max_i = i;
@@ -747,9 +705,8 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
state->mNoiseLev = state->mNoises.avg();
}
rssi_offset = rssiOffset(chan);
bi->rssi = 20.0 * log10(rxFullScale / avg) + rssi_offset;
bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssi_offset;
bi->rssi = 20.0 * log10(rxFullScale / avg) + rssiOffset;
bi->noise = 20.0 * log10(rxFullScale / state->mNoiseLev) + rssiOffset;
if (type == IDLE)
goto ret_idle;
@@ -757,15 +714,8 @@ 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(*shvec_ptr, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, mSPSRx, type, max_toa, &ebp);
if (rc <= 0) {
if (rc == -SIGERR_CLIP) {
LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst";
@@ -779,16 +729,11 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
goto ret_idle;
}
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);
}
type = (CorrType) rc;
bi->toa = ebp.toa;
bi->tsc = ebp.tsc;
bi->ci = ebp.ci;
rxBurst = demodAnyBurst(*burst, mSPSRx, ebp.amp, ebp.toa, type);
/* EDGE demodulator returns 444 (gSlotLen * 3) bits */
if (rxBurst->size() == EDGE_BURST_NBITS) {
@@ -856,7 +801,7 @@ void Transceiver::ctrl_sock_send(ctrl_msg& m, int chan)
struct osmo_fd *conn_bfd = &s.conn_bfd;
s.txmsgqueue.push_back(m);
osmo_fd_write_enable(conn_bfd);
conn_bfd->when |= OSMO_FD_WRITE;
}
int Transceiver::ctrl_sock_write(int chan)
@@ -871,7 +816,7 @@ int Transceiver::ctrl_sock_write(int chan)
while (s.txmsgqueue.size()) {
const ctrl_msg m = s.txmsgqueue.front();
osmo_fd_write_disable(&s.conn_bfd);
s.conn_bfd.when &= ~OSMO_FD_WRITE;
/* try to send it over the socket */
rc = write(s.conn_bfd.fd, m.data, strlen(m.data) + 1);
@@ -879,7 +824,7 @@ int Transceiver::ctrl_sock_write(int chan)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
osmo_fd_write_enable(&s.conn_bfd);
s.conn_bfd.when |= OSMO_FD_WRITE;
break;
}
goto close;
@@ -1005,7 +950,7 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
// tune receiver
int freqKhz;
sscanf(params, "%d", &freqKhz);
mRxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3;
mRxFreq = freqKhz * 1e3;
if (!mRadioInterface->tuneRx(mRxFreq, chan)) {
LOGCHAN(chan, DTRXCTRL, FATAL) << "RX failed to tune";
sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
@@ -1016,7 +961,7 @@ int Transceiver::ctrl_sock_handle_rx(int chan)
// tune txmtr
int freqKhz;
sscanf(params, "%d", &freqKhz);
mTxFreq = (freqKhz + cfg->freq_offset_khz) * 1e3;
mTxFreq = freqKhz * 1e3;
if (!mRadioInterface->tuneTx(mTxFreq, chan)) {
LOGCHAN(chan, DTRXCTRL, FATAL) << "TX failed to tune";
sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
@@ -1054,19 +999,13 @@ 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;
mVersionTRXD[chan] = version_recv;
sprintf(response, "RSP SETFORMAT %u %u", version_recv, version_recv);
}
} else if (match_cmd(command, "RFMUTE", &params)) {
// (Un)mute RF TX and RX
unsigned mute;
sscanf(params, "%u", &mute);
mStates[chan].mMuted = mute ? true : false;
sprintf(response, "RSP RFMUTE 0 %u", mute);
} else if (match_cmd(command, "_SETBURSTTODISKMASK", &params)) {
// debug command! may change or disappear without notice
// set a mask which bursts to dump to disk
@@ -1105,8 +1044,8 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
burstLen = gSlotLen;
break;
case sizeof(*dl) + EDGE_BURST_NBITS: /* EDGE burst */
if (cfg->tx_sps != 4) {
LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << cfg->tx_sps;
if (mSPSTx != 4) {
LOGCHAN(chan, DTRXDDL, ERROR) << "EDGE burst received but SPS is set to " << mSPSTx;
return false;
}
burstLen = EDGE_BURST_NBITS;
@@ -1213,13 +1152,11 @@ void Transceiver::logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi)
else os << "-";
}
double rssi_offset = rssiOffset(chan);
LOGCHAN(chan, DTRXDUL, DEBUG) << std::fixed << std::right
<< " time: " << unsigned(bi->tn) << ":" << bi->fn
<< " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssi_offset)
<< " RSSI: " << std::setw(5) << std::setprecision(1) << (bi->rssi - rssiOffset)
<< "dBFS/" << std::setw(6) << -bi->rssi << "dBm"
<< " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssi_offset)
<< " noise: " << std::setw(5) << std::setprecision(1) << (bi->noise - rssiOffset)
<< "dBFS/" << std::setw(6) << -bi->noise << "dBm"
<< " TOA: " << std::setw(5) << std::setprecision(2) << bi->toa
<< " C/I: " << std::setw(5) << std::setprecision(2) << bi->ci << "dB"

View File

@@ -80,14 +80,11 @@ struct TransceiverState {
/* Received noise energy levels */
float mNoiseLev;
avgVector mNoises;
noiseVector mNoises;
/* Shadowed downlink attenuation */
int mPower;
/* RF emission and reception disabled, as per NM Administrative State Locked */
bool mMuted;
/* counters */
struct trx_counters ctrs;
@@ -100,19 +97,27 @@ struct TransceiverState {
class Transceiver {
public:
/** Transceiver constructor
@param cfg VTY populated config
@param wBasePort base port number of UDP sockets
@param TRXAddress IP address of the TRX, as a string
@param GSMcoreAddress IP address of the GSM core, as a string
@param wSPS number of samples per GSM symbol
@param wTransmitLatency initial setting of transmit latency
@param radioInterface associated radioInterface object
*/
Transceiver(const struct trx_cfg *cfg,
Transceiver(int wBasePort,
const char *TRXAddress,
const char *GSMcoreAddress,
size_t tx_sps, size_t rx_sps, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface);
RadioInterface *wRadioInterface,
double wRssiOffset, int stackSize);
/** Destructor */
~Transceiver();
/** Start the control loop */
bool init(void);
bool init(FillerType filler, size_t rtsc, unsigned rach_delay,
bool edge, bool ext_rach);
/** attach the radioInterface receive FIFO */
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
@@ -125,7 +130,7 @@ public:
}
/** accessor for number of channels */
size_t numChans() const { return cfg->num_chans; };
size_t numChans() const { return mChans; };
/** Codes for channel combinations */
typedef enum {
@@ -148,7 +153,7 @@ public:
} ChannelCombination;
private:
size_t mChans;
struct ctrl_msg {
char data[101];
ctrl_msg() {};
@@ -162,14 +167,17 @@ 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);
}
}
};
const struct trx_cfg *cfg; ///< VTY populated config
int mBasePort;
std::string mLocalAddr;
std::string mRemoteAddr;
std::vector<int> mDataSockets; ///< socket for writing to/reading from GSM core
std::vector<ctrl_sock_state> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
int mClockSocket; ///< socket for writing clock updates to GSM core
@@ -191,6 +199,9 @@ struct ctrl_sock_state {
double txFullScale; ///< full scale input to radio
double rxFullScale; ///< full scale output to radio
double rssiOffset; ///< RSSI to dBm conversion offset
int stackSize; ///< stack size for threads, 0 = OS default
/** modulate and add a burst to the transmit queue */
void addRadioVector(size_t chan, BitVector &bits,
int RSSI, GSM::Time &wTime);
@@ -219,7 +230,12 @@ struct ctrl_sock_state {
/** drive handling of control messages from GSM core */
int ctrl_sock_handle_rx(int chan);
int mSPSTx; ///< number of samples per Tx symbol
int mSPSRx; ///< number of samples per Rx symbol
size_t mChans;
bool mExtRACH;
bool mEdge;
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
@@ -261,7 +277,7 @@ protected:
friend void *RxLowerLoopAdapter(Transceiver *transceiver);
friend void *TxLowerLoopAdapter(Transceiver *transceiver);
double rssiOffset(size_t chan);
void reset();
void logRxBurst(size_t chan, const struct trx_ul_burst_ind *bi);

View File

@@ -13,6 +13,10 @@
* 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>

View File

@@ -13,6 +13,10 @@
* 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

View File

@@ -13,6 +13,10 @@
* 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>

View File

@@ -13,6 +13,10 @@
* 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

View File

@@ -13,6 +13,10 @@
* 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>

View File

@@ -13,6 +13,10 @@
* 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

View File

@@ -13,6 +13,10 @@
* 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>

View File

@@ -13,6 +13,10 @@
* 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

View File

@@ -13,6 +13,10 @@
* 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"

View File

@@ -13,6 +13,10 @@
* 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>

View File

@@ -11,6 +11,10 @@
* 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>

View File

@@ -11,6 +11,10 @@
* 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>

View File

@@ -11,6 +11,10 @@
* 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

View File

@@ -11,6 +11,10 @@
* 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>

View File

@@ -11,6 +11,10 @@
* 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

View File

@@ -11,6 +11,10 @@
* 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>
@@ -95,10 +99,9 @@ int convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len, int start, int len)
{
#ifndef __OPTIMIZE__
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
return -1;
#endif
memset(y, 0, len * 2 * sizeof(float));
switch (h_len) {
@@ -135,10 +138,9 @@ int convolve_complex(const float *x, int x_len,
float *y, int y_len,
int start, int len)
{
#ifndef __OPTIMIZE__
if (bounds_check(x_len, h_len, y_len, start, len) < 0)
return -1;
#endif
memset(y, 0, len * 2 * sizeof(float));
if (!(h_len % 8))

View File

@@ -11,6 +11,10 @@
* 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>

View File

@@ -11,6 +11,10 @@
* 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

View File

@@ -2,9 +2,9 @@ include $(top_srcdir)/Makefile.common
SUBDIRS = common
if DEVICE_IPC
#if DEVICE_IPC
SUBDIRS += ipc
endif
#endif
if DEVICE_USRP1
SUBDIRS += usrp1
@@ -17,7 +17,3 @@ endif
if DEVICE_LMS
SUBDIRS += lms
endif
if DEVICE_BLADE
SUBDIRS += bladerf
endif

View File

@@ -1,11 +0,0 @@
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

View File

@@ -1,611 +0,0 @@
/*
* 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);
}

View File

@@ -1,195 +0,0 @@
/*
* 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;
};

View File

@@ -1,10 +1,10 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS)
noinst_HEADERS = radioDevice.h smpl_buf.h bandmanager.h
noinst_HEADERS = radioDevice.h smpl_buf.h
noinst_LTLIBRARIES = libdevice_common.la

View File

@@ -1,137 +0,0 @@
#pragma once
/*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <string>
#include <tuple>
#include "Logger.h"
extern "C" {
#include <osmocom/gsm/gsm_utils.h>
}
template <typename powermapt, typename devmapt>
class band_manager {
using powerkeyt = typename powermapt::key_type;
using powermappedt = typename powermapt::mapped_type;
using devkeyt = typename devmapt::key_type;
devkeyt m_dev_type;
const powermapt &m_power_map;
const devmapt &m_dev_map;
powerkeyt m_fallback;
enum gsm_band m_band;
powermappedt m_band_desc;
bool band_ass_curr_sess{}; /* true if "band" was set after last POWEROFF */
// looks up either first tuple element (->enum) or straight enum
template <typename T, typename std::enable_if<std::is_enum<T>::value>::type *dummy = nullptr>
auto key_helper(T &t) -> T
{
return t;
}
template <typename T>
auto key_helper(T t) -> typename std::tuple_element<0, T>::type
{
return std::get<0>(t);
}
void assign_band_desc(enum gsm_band req_band)
{
auto key = key_helper(m_dev_type);
auto fallback_key = key_helper(m_fallback);
auto it = m_power_map.find({ key, req_band });
if (it == m_power_map.end()) {
auto desc = m_dev_map.at(m_dev_type);
LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device " << desc.desc_str
<< " on band " << gsm_band_name(req_band) << ", using fallback..";
it = m_power_map.find({ fallback_key, req_band });
}
OSMO_ASSERT(it != m_power_map.end());
m_band_desc = it->second;
}
bool set_band(enum gsm_band req_band)
{
if (band_ass_curr_sess && req_band != m_band) {
LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
<< " different from previous band " << gsm_band_name(m_band);
return false;
}
if (req_band != m_band) {
m_band = req_band;
assign_band_desc(m_band);
}
band_ass_curr_sess = true;
return true;
}
public:
band_manager(const devkeyt &dev_type, const powermapt &power_map, const devmapt &dev_map, powerkeyt fallback)
: m_dev_type(dev_type), m_power_map(power_map), m_dev_map(dev_map), m_fallback(fallback),
m_band((enum gsm_band)0)
{
}
band_manager(const powermapt &power_map, const devmapt &dev_map)
: m_dev_type(dev_map.begin()->first), m_power_map(power_map), m_dev_map(dev_map),
m_fallback(m_power_map.begin()->first), m_band((enum gsm_band)0)
{
}
void band_reset()
{
band_ass_curr_sess = false;
}
void update_band_dev(devkeyt dev_type) {
m_dev_type = dev_type;
}
void get_dev_band_desc(powermappedt &desc)
{
if (m_band == 0) {
LOGC(DDEV, ERROR)
<< "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
assign_band_desc(GSM_BAND_900);
}
desc = m_band_desc;
}
bool update_band_from_freq(double wFreq, int chan, bool is_tx)
{
enum gsm_band req_band;
auto dirstr = is_tx ? "Tx" : "Rx";
auto req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, !is_tx);
if (req_arfcn == 0xffff) {
LOGCHAN(chan, DDEV, ALERT)
<< "Unknown ARFCN for " << dirstr << " Frequency " << wFreq / 1000 << " kHz";
return false;
}
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for " << dirstr << " Frequency " << wFreq
<< " Hz (ARFCN " << req_arfcn << " )";
return false;
}
return set_band(req_band);
}
};

View File

@@ -51,10 +51,13 @@ class RadioDevice {
MULTI_ARFCN,
};
static RadioDevice *make(InterfaceType type, const struct trx_cfg *cfg);
static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type,
size_t chans = 1, double offset = 0.0,
const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""),
const std::vector<std::string>& rx_paths = std::vector<std::string>(1, ""));
/** Initialize the USRP */
virtual int open() = 0;
virtual int open(const std::string &args, int ref, bool swap_channels)=0;
virtual ~RadioDevice() { }
@@ -122,9 +125,6 @@ class RadioDevice {
/** return minimum Rx Gain **/
virtual double minRxGain(void) = 0;
/** return base RSSI offset to apply for received samples **/
virtual double rssiOffset(size_t chan) = 0;
/** returns the Nominal transmit output power of the transceiver in dBm, negative on error **/
virtual int getNominalTxPower(size_t chan = 0) = 0;
@@ -161,30 +161,24 @@ class RadioDevice {
double lo_offset;
std::vector<std::string> tx_paths, rx_paths;
std::vector<struct device_counters> m_ctr;
const struct trx_cfg *cfg;
#define charp2str(a) ((a) ? std::string(a) : std::string(""))
RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chan_num, double offset,
const std::vector<std::string>& tx_paths,
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;
}
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;
}
}
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;
}
}
bool set_antennas() {
unsigned int i;
@@ -192,13 +186,6 @@ class RadioDevice {
for (i = 0; i < tx_paths.size(); i++) {
if (tx_paths[i] == "")
continue;
if (iface == MULTI_ARFCN && i > 0) {
LOGCHAN(i, DDEV, NOTICE) << "Not setting Tx antenna "
<< tx_paths[i]
<< " for a logical channel";
continue;
}
LOGCHAN(i, DDEV, DEBUG) << "Configuring Tx antenna " << tx_paths[i];
if (!setTxAntenna(tx_paths[i], i)) {
LOGCHAN(i, DDEV, ALERT) << "Failed configuring Tx antenna " << tx_paths[i];
@@ -209,13 +196,6 @@ class RadioDevice {
for (i = 0; i < rx_paths.size(); i++) {
if (rx_paths[i] == "")
continue;
if (iface == MULTI_ARFCN && i > 0) {
LOGCHAN(i, DDEV, NOTICE) << "Not setting Rx antenna "
<< rx_paths[i]
<< " for a logical channel";
continue;
}
LOGCHAN(i, DDEV, DEBUG) << "Configuring Rx antenna " << rx_paths[i];
if (!setRxAntenna(rx_paths[i], i)) {
LOGCHAN(i, DDEV, ALERT) << "Failed configuring Rx antenna " << rx_paths[i];

View File

@@ -4,33 +4,32 @@
*
* 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.
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 <cstdint>
#include <cstring>
#include <cstdlib>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <map>
#include "trx_vty.h"
#include "Logger.h"
#include "Threads.h"
#include "Utils.h"
#include "IPCDevice.h"
#include "smpl_buf.h"
extern "C" {
#include <sys/mman.h>
@@ -38,6 +37,7 @@ extern "C" {
#include <fcntl.h> /* For O_* constants */
#include "osmo_signal.h"
#include <osmocom/core/application.h>
#include <osmocom/core/talloc.h>
#include <osmocom/core/select.h>
#include <osmocom/core/socket.h>
@@ -46,30 +46,43 @@ extern "C" {
#include <osmocom/core/msgb.h>
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include "ipc_shm.h"
}
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#define SAMPLE_BUF_SZ (1 << 20)
using namespace std;
static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags);
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)
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)
{
LOGC(DDEV, INFO) << "creating IPC device...";
//m_IPC_stream_rx.resize(chans);
//m_IPC_stream_tx.resize(chans);
rx_gains.resize(chans);
tx_gains.resize(chans);
rx_buffers.resize(chans);
/* 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));
memset(&sk_chan_state, 0, sizeof(sk_chan_state));
memset(&trx_is_started, 0, sizeof(trx_is_started));
}
IPCDevice::~IPCDevice()
{
//unsigned int i;
LOGC(DDEV, INFO) << "Closing IPC device";
/* disable all channels */
@@ -78,7 +91,7 @@ IPCDevice::~IPCDevice()
ipc_sock_close(&master_sk_state);
for (unsigned int i = 0; i < sk_chan_state.size(); i++)
for (unsigned int i = 0; i < ARRAY_SIZE(sk_chan_state); i++)
ipc_sock_close(&sk_chan_state[i]);
for (auto i : shm_io_rx_streams)
@@ -86,21 +99,19 @@ IPCDevice::~IPCDevice()
for (auto i : shm_io_tx_streams)
ipc_shm_close(i);
if (shm_dec)
if(shm_dec)
talloc_free(shm_dec);
}
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_r(errno, err_buf, sizeof(err_buf)));
LOGP(DDEV, LOGL_ERROR, "shm_open %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_shm_open;
}
@@ -108,8 +119,7 @@ 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_r(errno, err_buf, sizeof(err_buf)));
LOGP(DDEV, LOGL_ERROR, "fstat %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_mmap;
}
@@ -118,13 +128,12 @@ 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_r(errno, err_buf, sizeof(err_buf)));
LOGP(DDEV, LOGL_ERROR, "mmap %d: %s\n", errno, strerror(errno));
rc = -errno;
goto err_mmap;
}
LOGP(DDEV, LOGL_NOTICE, "mmap'ed shared memory at addr %p\n", shm);
// LOGP(DDEV, LOGL_NOTICE, "%s\n", osmo_hexdump((const unsigned char *)shm, 80));
// LOGP(DDEV, LOGL_NOTICE, "%s\n", osmo_hexdump((const unsigned char *)shm, 80));
/* After a call to mmap(2) the file descriptor may be closed without affecting the memory mapping. */
close(fd);
return 0;
@@ -135,7 +144,7 @@ err_shm_open:
return rc;
}
static int ipc_sock_send(struct ipc_per_trx_sock_state *state, struct msgb *msg);
static int ipc_sock_send(struct ipc_sock_state *state, struct msgb *msg);
static struct msgb *ipc_msgb_alloc(uint8_t msg_type)
{
@@ -152,7 +161,7 @@ static struct msgb *ipc_msgb_alloc(uint8_t msg_type)
return msg;
}
static int ipc_tx_greeting_req(struct ipc_per_trx_sock_state *state, uint8_t req_version)
static int ipc_tx_greeting_req(struct ipc_sock_state *state, uint8_t req_version)
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
@@ -170,7 +179,7 @@ static int ipc_tx_greeting_req(struct ipc_per_trx_sock_state *state, uint8_t req
return ipc_sock_send(state, msg);
}
static int ipc_tx_info_req(struct ipc_per_trx_sock_state *state)
static int ipc_tx_info_req(struct ipc_sock_state *state)
{
struct msgb *msg;
//struct ipc_sk_if *ipc_prim;
@@ -186,7 +195,7 @@ static int ipc_tx_info_req(struct ipc_per_trx_sock_state *state)
return ipc_sock_send(state, msg);
}
int IPCDevice::ipc_tx_open_req(struct ipc_per_trx_sock_state *state, uint32_t num_chans, uint32_t ref)
int IPCDevice::ipc_tx_open_req(struct ipc_sock_state *state, uint32_t num_chans, uint32_t ref)
{
struct msgb *msg;
struct ipc_sk_if *ipc_prim;
@@ -266,20 +275,17 @@ int IPCDevice::ipc_rx_info_cnf(const struct ipc_sk_if_info_cnf *info_cnf)
* cache rx/tx paths per channel, and make sure it matches the one the user wants to set
*/
LOGC(DDEV, NOTICE)
<< "Rx Info CNF:"
<< " name=" << info_cnf->dev_desc << std::endl
<< " max_num_chans=" << info_cnf->max_num_chans << " feature_mask=" << info_cnf->feature_mask;
LOGC(DDEV, NOTICE) << "Rx Info CNF:"
<< " name=" << info_cnf->dev_desc << std::endl
<< " max_num_chans=" << info_cnf->max_num_chans << " feature_mask=" << info_cnf->feature_mask;
for (i = 0; i < info_cnf->max_num_chans; i++) {
int j = 0;
bool rx_found = false, tx_found = false;
while (strcmp(info_cnf->chan_info[i].rx_path[j], "") != 0) {
LOGC(DDEV, NOTICE)
<< "chan " << i << ": RxPath[" << j << "]: " << info_cnf->chan_info[i].rx_path[j]
<< " min_rx_gain=" << info_cnf->chan_info[i].min_rx_gain
<< " max_rx_gain=" << info_cnf->chan_info[i].max_rx_gain
<< " min_tx_gain=" << info_cnf->chan_info[i].min_tx_gain
<< " max_tx_gain=" << info_cnf->chan_info[i].max_tx_gain;
<< " min_rx_gain=" << info_cnf->chan_info[i].min_rx_gain << " max_rx_gain=" << info_cnf->chan_info[i].max_rx_gain
<< " min_tx_gain=" << info_cnf->chan_info[i].min_tx_gain << " max_tx_gain=" << info_cnf->chan_info[i].max_tx_gain;
if (rx_paths.size() < (i + 1) ||
strcmp(rx_paths[i].c_str(), info_cnf->chan_info[i].rx_path[j]) == 0) {
@@ -329,7 +335,8 @@ int IPCDevice::ipc_rx_open_cnf(const struct ipc_sk_if_open_cnf *open_cnf)
/* FIXME: current limit IPC_MAX_NUM_TRX chans, make dynamic */
if (i < IPC_MAX_NUM_TRX) {
struct ipc_per_trx_sock_state *state = &sk_chan_state[i];
struct ipc_sock_state *state = &sk_chan_state[i];
memset(state, 0x00, sizeof(*state));
INIT_LLIST_HEAD(&state->upqueue);
rc = osmo_sock_unix_init_ofd(&state->conn_bfd, SOCK_SEQPACKET, 0,
@@ -366,8 +373,7 @@ int IPCDevice::ipc_rx_open_cnf(const struct ipc_sk_if_open_cnf *open_cnf)
<< "shm: chan" << i << "/ul: buffer_size=" << shm_dec->channels[i]->ul_stream->buffer_size;
shm_io_rx_streams.push_back(ipc_shm_init_consumer(shm_dec->channels[i]->ul_stream));
shm_io_tx_streams.push_back(ipc_shm_init_consumer(shm_dec->channels[i]->dl_stream));
// we should init a producer here, but delegating all producers and therefore lock management
// to the other side is the reasonable approach to circumvent shutdown issues
// shm_io_tx_streams.push_back(ipc_shm_init_producer(shm_dec->channels[i]->dl_stream));
}
tmp_state = IPC_IF_MSG_OPEN_CNF;
@@ -398,23 +404,27 @@ int IPCDevice::ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim)
int IPCDevice::ipc_rx_chan_start_cnf(ipc_sk_chan_if_op_rc *ret, uint8_t chan_nr)
{
if (chan_nr >= chans) {
if(chan_nr >= ARRAY_SIZE(trx_is_started)) {
LOGC(DDEV, NOTICE) << "shm: illegal start response for chan #" << chan_nr << " ?!?";
return 0;
}
trx_is_started[chan_nr] = true;
return 0;
}
int IPCDevice::ipc_rx_chan_stop_cnf(ipc_sk_chan_if_op_rc *ret, uint8_t chan_nr)
{
if (chan_nr >= chans) {
if(chan_nr >= ARRAY_SIZE(trx_is_started)) {
LOGC(DDEV, NOTICE) << "shm: illegal stop response for chan #" << chan_nr << " ?!?";
return 0;
}
trx_is_started[chan_nr] = false;
return 0;
}
int IPCDevice::ipc_rx_chan_setgain_cnf(ipc_sk_chan_if_gain *ret, uint8_t chan_nr)
{
if (chan_nr >= chans) {
if(chan_nr >= ARRAY_SIZE(trx_is_started)) {
LOGC(DDEV, NOTICE) << "shm: illegal setgain response for chan #" << chan_nr << " ?!?";
return 0;
}
@@ -424,17 +434,17 @@ int IPCDevice::ipc_rx_chan_setgain_cnf(ipc_sk_chan_if_gain *ret, uint8_t chan_nr
}
int IPCDevice::ipc_rx_chan_settxattn_cnf(ipc_sk_chan_if_tx_attenuation *ret, uint8_t chan_nr)
{
if (chan_nr >= chans) {
if(chan_nr >= ARRAY_SIZE(trx_is_started)) {
LOGC(DDEV, NOTICE) << "shm: illegal tx attn response for chan #" << chan_nr << " ?!?";
return 0;
}
tx_attenuation[chan_nr] = ret->attenuation;
// trx_is_started[chan_nr] = false;
return 0;
}
int IPCDevice::ipc_rx_chan_setfreq_cnf(ipc_sk_chan_if_freq_cnf *ret, uint8_t chan_nr)
{
if (chan_nr >= chans) {
if(chan_nr >= ARRAY_SIZE(trx_is_started)) {
LOGC(DDEV, NOTICE) << "shm: illegal setfreq response for chan #" << chan_nr << " ?!?";
return 0;
}
@@ -443,7 +453,7 @@ int IPCDevice::ipc_rx_chan_setfreq_cnf(ipc_sk_chan_if_freq_cnf *ret, uint8_t cha
}
int IPCDevice::ipc_rx_chan_notify_underflow(ipc_sk_chan_if_notfiy *ret, uint8_t chan_nr)
{
if (chan_nr >= chans) {
if(chan_nr >= ARRAY_SIZE(trx_is_started)) {
LOGC(DDEV, NOTICE) << "shm: illegal underfloww notification for chan #" << chan_nr << " ?!?";
return 0;
}
@@ -455,7 +465,7 @@ int IPCDevice::ipc_rx_chan_notify_underflow(ipc_sk_chan_if_notfiy *ret, uint8_t
}
int IPCDevice::ipc_rx_chan_notify_overflow(ipc_sk_chan_if_notfiy *ret, uint8_t chan_nr)
{
if (chan_nr >= chans) {
if(chan_nr >= ARRAY_SIZE(trx_is_started)) {
LOGC(DDEV, NOTICE) << "shm: illegal overflow notification for chan #" << chan_nr << " ?!?";
return 0;
}
@@ -499,9 +509,10 @@ int IPCDevice::ipc_chan_rx(uint8_t msg_type, struct ipc_sk_chan_if *ipc_prim, ui
return rc;
}
static int ipc_sock_send(struct ipc_per_trx_sock_state *state, struct msgb *msg)
static int ipc_sock_send(struct ipc_sock_state *state, struct msgb *msg)
{
struct osmo_fd *conn_bfd;
//struct ipc_sk_if *ipc_prim = (struct ipc_sk_if *) msg->data;
if (!state) {
LOGP(DMAIN, LOGL_INFO,
@@ -519,12 +530,12 @@ static int ipc_sock_send(struct ipc_per_trx_sock_state *state, struct msgb *msg)
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
osmo_fd_write_enable(conn_bfd);
conn_bfd->when |= BSC_FD_WRITE;
return 0;
}
void IPCDevice::ipc_sock_close(struct ipc_per_trx_sock_state *state)
void IPCDevice::ipc_sock_close(struct ipc_sock_state *state)
{
if (state == 0)
return;
@@ -536,9 +547,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)) {
@@ -627,9 +638,6 @@ int IPCDevice::ipc_chan_sock_read(struct osmo_fd *bfd)
return 0;
}
/* store mask of last received messages so we can check later */
sk_chan_state[bfd->priv_nr].messages_processed_mask |= (1 << (ipc_prim->msg_type - IPC_IF_CHAN_MSG_OFFSET));
rc = ipc_chan_rx(ipc_prim->msg_type, ipc_prim, bfd->priv_nr);
/* as we always synchronously process the message in IPC_rx() and
@@ -656,7 +664,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;
osmo_fd_write_disable(bfd);
bfd->when &= ~BSC_FD_WRITE;
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
@@ -673,7 +681,7 @@ int IPCDevice::ipc_sock_write(struct osmo_fd *bfd)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
osmo_fd_write_enable(bfd);
bfd->when |= BSC_FD_WRITE;
break;
}
goto close;
@@ -703,7 +711,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;
osmo_fd_write_disable(bfd);
bfd->when &= ~BSC_FD_WRITE;
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
LOGP(DDEV, LOGL_ERROR,
@@ -719,14 +727,14 @@ int IPCDevice::ipc_chan_sock_write(struct osmo_fd *bfd)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
osmo_fd_write_enable(bfd);
bfd->when |= BSC_FD_WRITE;
break;
}
goto close;
}
dontsend:
/* _after_ we send it, we can dequeue */
/* _after_ we send it, we can deueue */
msg2 = msgb_dequeue(&sk_chan_state[bfd->priv_nr].upqueue);
assert(msg == msg2);
msgb_free(msg);
@@ -743,12 +751,12 @@ static int ipc_sock_cb(struct osmo_fd *bfd, unsigned int flags)
IPCDevice *device = static_cast<IPCDevice *>(bfd->data);
int rc = 0;
if (flags & OSMO_FD_READ)
if (flags & BSC_FD_READ)
rc = device->ipc_sock_read(bfd);
if (rc < 0)
return rc;
if (flags & OSMO_FD_WRITE)
if (flags & BSC_FD_WRITE)
rc = device->ipc_sock_write(bfd);
return rc;
@@ -759,39 +767,39 @@ static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags)
IPCDevice *device = static_cast<IPCDevice *>(bfd->data);
int rc = 0;
if (flags & OSMO_FD_READ)
if (flags & BSC_FD_READ)
rc = device->ipc_chan_sock_read(bfd);
if (rc < 0)
return rc;
if (flags & OSMO_FD_WRITE)
if (flags & BSC_FD_WRITE)
rc = device->ipc_chan_sock_write(bfd);
return rc;
}
int IPCDevice::open()
int IPCDevice::open(const std::string &args, int ref, bool swap_channels)
{
std::string k, v;
std::string k,v;
std::string::size_type keyend;
int rc;
std::string args(cfg->dev_args);
if ((keyend = args.find('=')) != std::string::npos) {
k = args.substr(0, keyend++);
v = args.substr(keyend);
}
if (k != "ipc_msock" || !v.length()) {
LOGC(DDEV, ERROR) << "Invalid device args provided, expected \"dev-args ipc_msock=/path/to/socket\"\n";
return -1;
if(k != "ipc_msock" || !v.length()) {
LOGC(DDEV, ERROR) << "Invalid device args provided, expected \"dev-args ipc_msock=/path/to/socket\"\n";
return -1;
}
LOGC(DDEV, INFO) << "Opening IPC device" << v << "..";
memset(&master_sk_state, 0x00, sizeof(master_sk_state));
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 IPC device (" << v << "). "
LOGC(DDEV, ERROR) << "Failed to connect to the BTS (" << v << "). "
<< "Retrying...\n";
osmo_timer_setup(&master_sk_state.timer, ipc_sock_timeout, NULL);
osmo_timer_schedule(&master_sk_state.timer, 5, 0);
@@ -810,7 +818,7 @@ int IPCDevice::open()
while (tmp_state != IPC_IF_MSG_INFO_CNF)
osmo_select_main(0);
ipc_tx_open_req(&master_sk_state, chans, cfg->clock_ref);
ipc_tx_open_req(&master_sk_state, chans, ref);
/* Wait until confirmation is recieved */
while (tmp_state != IPC_IF_MSG_OPEN_CNF)
osmo_select_main(0);
@@ -829,34 +837,28 @@ out_close:
return -1;
}
void IPCDevice::manually_poll_sock_fds()
{
struct timeval wait = { 0, 100000 };
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);
FD_ZERO(&cwfds);
for (unsigned int i = 0; i < chans; i++) {
struct osmo_fd *curr_fd = &sk_chan_state[i].conn_bfd;
for(int i = 0; i < chans; i++) {
struct osmo_fd* curr_fd = &sk_chan_state[i].conn_bfd;
max_fd = curr_fd->fd > max_fd ? curr_fd->fd : max_fd;
if (curr_fd->when & OSMO_FD_READ)
FD_SET(curr_fd->fd, &crfds);
if (curr_fd->when & OSMO_FD_WRITE)
FD_SET(curr_fd->fd, &cwfds);
if(curr_fd->when & OSMO_FD_READ)
FD_SET(curr_fd->fd, &crfds);
if(curr_fd->when & OSMO_FD_WRITE)
FD_SET(curr_fd->fd, &cwfds);
}
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;
}
select(max_fd+1, &crfds, &cwfds, 0, &wait);
for (unsigned int i = 0; i < chans; i++) {
for(int i = 0; i < chans; i++) {
int flags = 0;
struct osmo_fd *ofd = &sk_chan_state[i].conn_bfd;
struct osmo_fd* ofd = &sk_chan_state[i].conn_bfd;
if (FD_ISSET(ofd->fd, &crfds)) {
flags |= OSMO_FD_READ;
@@ -867,74 +869,10 @@ void IPCDevice::manually_poll_sock_fds()
flags |= OSMO_FD_WRITE;
FD_CLR(ofd->fd, &cwfds);
}
if (flags)
ipc_chan_sock_cb(ofd, flags);
if(flags)
ipc_chan_sock_cb(ofd, flags);
}
}
bool IPCDevice::send_chan_wait_rsp(uint32_t chan, struct msgb *msg_to_send, uint32_t expected_rsp_msg_id)
{
struct timeval timer_now, timeout;
sk_chan_state[chan].messages_processed_mask = 0;
ipc_sock_send(&sk_chan_state[chan], msg_to_send);
gettimeofday(&timeout, 0);
timeout.tv_sec += 2;
while (!(sk_chan_state[chan].messages_processed_mask & (1 << (expected_rsp_msg_id - IPC_IF_CHAN_MSG_OFFSET)))) {
/* just poll here, we're already in select, so there is no other way to drive
* the fds and "wait" for a response or retry */
manually_poll_sock_fds();
gettimeofday(&timer_now, 0);
if (timercmp(&timer_now, &timeout, >))
return false;
}
return true;
}
bool IPCDevice::send_all_chan_wait_rsp(uint32_t msgid_to_send, uint32_t msgid_to_expect)
{
struct msgb *msg;
struct ipc_sk_chan_if *ipc_prim;
struct timeval timer_now, timeout;
for (unsigned int i = 0; i < chans; i++) {
msg = ipc_msgb_alloc(msgid_to_send);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
ipc_prim->u.start_req.dummy = 0;
sk_chan_state[i].messages_processed_mask = 0;
ipc_sock_send(&sk_chan_state[i], msg);
}
gettimeofday(&timeout, 0);
timeout.tv_sec += 2;
unsigned int msg_received_count = 0;
while (msg_received_count != chans) {
msg_received_count = 0;
/* just poll here, we're already in select, so there is no other way to drive
* the fds and "wait" for a response or retry */
manually_poll_sock_fds();
for (unsigned int i = 0; i < sk_chan_state.size(); i++)
if (sk_chan_state[i].messages_processed_mask &
(1 << (msgid_to_expect - IPC_IF_CHAN_MSG_OFFSET)))
msg_received_count++;
gettimeofday(&timer_now, 0);
if (timercmp(&timer_now, &timeout, >))
return false;
}
return true;
}
/* the call stack is rather difficult here, we're already in select:
>~"#0 IPCDevice::start (this=<optimized out>) at IPCDevice.cpp:789\n"
>~"#1 in RadioInterface::start (this=0x614000001640) at radioInterface.cpp:187\n"
@@ -955,13 +893,43 @@ bool IPCDevice::start()
return true;
}
if (!(send_all_chan_wait_rsp(IPC_IF_MSG_START_REQ, IPC_IF_MSG_START_CNF))) {
LOGC(DDEV, ERR) << "start timeout!";
return false;
struct msgb *msg;
struct ipc_sk_chan_if *ipc_prim;
struct timeval timer_now, timeout;
for(int i = 0; i < chans; i++) {
msg = ipc_msgb_alloc(IPC_IF_MSG_START_REQ);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
ipc_prim->u.start_req.dummy = 0;
ipc_sock_send(&sk_chan_state[i], msg);
}
gettimeofday(&timeout, 0);
timeout.tv_sec += 2;
int chan_started_count = 0;
while (chan_started_count != chans) {
chan_started_count = 0;
/* just poll here, we're already in select, so there is no other way to drive
* the fds and "wait" for a response or retry */
manually_poll_sock_fds();
for(unsigned int i = 0; i < ARRAY_SIZE(trx_is_started); i++)
if(trx_is_started[i] == true)
chan_started_count++;
gettimeofday(&timer_now, 0);
if(timercmp(&timer_now, &timeout, >))
return false;
}
int max_bufs_to_flush = 0;
for (unsigned int i = 0; i < shm_dec->num_chans; i++) {
for(unsigned int i = 0; i < shm_dec->num_chans; i++) {
int buf_per_chan = shm_dec->channels[i]->ul_stream->num_buffers;
max_bufs_to_flush = max_bufs_to_flush < buf_per_chan ? buf_per_chan : max_bufs_to_flush;
}
@@ -973,15 +941,48 @@ bool IPCDevice::start()
bool IPCDevice::stop()
{
struct msgb *msg;
struct ipc_sk_chan_if *ipc_prim;
struct timeval timer_now, timeout;
if (!started)
return true;
if (!(send_all_chan_wait_rsp(IPC_IF_MSG_STOP_REQ, IPC_IF_MSG_STOP_CNF))) {
LOGC(DDEV, ERR) << "stop timeout!";
return false;
for(int i = 0; i < chans; i++) {
if(trx_is_started[i] == true) {
msg = ipc_msgb_alloc(IPC_IF_MSG_STOP_REQ);
if (!msg)
return -ENOMEM;
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
ipc_prim->u.start_req.dummy = 0;
ipc_sock_send(&sk_chan_state[i], msg);
}
}
LOGC(DDEV, NOTICE) << "All channels stopped, terminating...";
gettimeofday(&timeout, 0);
timeout.tv_sec += 2;
int chan_started_count = 0;
do {
chan_started_count = 0;
/* just poll here, we're already in select, so there is no other way to drive
* the fds and "wait" for a response or retry */
manually_poll_sock_fds();
for(unsigned int i = 0; i < ARRAY_SIZE(trx_is_started); i++)
if(trx_is_started[i] == true)
chan_started_count++;
gettimeofday(&timer_now, 0);
if(timercmp(&timer_now, &timeout, >)) {
LOGC(DDEV, ERR) << "No response to stop msg received, terminating anyway...";
break;
}
} while (chan_started_count > 0);
LOGC(DDEV, NOTICE) << "All chanels stopped, terminating...";
/* reset internal buffer timestamps */
for (size_t i = 0; i < rx_buffers.size(); i++)
@@ -1001,21 +1002,20 @@ double IPCDevice::minRxGain()
return current_info_cnf.chan_info[0].min_rx_gain;
}
int IPCDevice::getNominalTxPower(size_t chan)
{
return current_info_cnf.chan_info[chan].nominal_tx_power;
}
double IPCDevice::setPowerAttenuation(int atten, size_t chan)
{
double IPCDevice::setPowerAttenuation(int atten, size_t chan) {
struct msgb *msg;
struct ipc_sk_chan_if *ipc_prim;
if (chan >= chans)
if(chan >= chans)
return 0;
LOGCHAN(chan, DDEV, NOTICE) << "Setting TX attenuation to " << atten << " dB"
<< " chan " << chan;
LOGCHAN(chan, DDEV, NOTICE) << "Setting TX attenuation to " << atten << " dB" << " chan " << chan;
msg = ipc_msgb_alloc(IPC_IF_MSG_SETTXATTN_REQ);
if (!msg)
@@ -1023,15 +1023,15 @@ double IPCDevice::setPowerAttenuation(int atten, size_t chan)
ipc_prim = (struct ipc_sk_chan_if *)msg->data;
ipc_prim->u.txatten_req.attenuation = atten;
if (!send_chan_wait_rsp(chan, msg, IPC_IF_MSG_SETTXATTN_CNF))
LOGCHAN(chan, DDEV, ERROR) << "Setting TX attenuation timeout! ";
ipc_sock_send(&sk_chan_state[chan], msg);
tx_attenuation[chan] = atten;
return atten;
}
double IPCDevice::getPowerAttenuation(size_t chan)
{
if (chan >= chans)
double IPCDevice::getPowerAttenuation(size_t chan) {
if(chan >= chans)
return 0;
return tx_attenuation[chan];
@@ -1056,9 +1056,9 @@ double IPCDevice::setRxGain(double dB, size_t chan)
ipc_prim->u.set_gain_req.is_tx = 0;
ipc_prim->u.set_gain_req.gain = dB;
if (!send_chan_wait_rsp(chan, msg, IPC_IF_MSG_SETGAIN_CNF))
LOGCHAN(chan, DDEV, ERROR) << "Setting RX gain timeout! ";
ipc_sock_send(&sk_chan_state[chan], msg);
rx_gains[chan] = dB;
return rx_gains[chan];
}
@@ -1066,7 +1066,7 @@ bool IPCDevice::flush_recv(size_t num_pkts)
{
std::vector<uint16_t> tmp(4096);
uint64_t tmps;
uint32_t read = 0;
uint32_t read;
for (uint32_t j = 0; j < num_pkts; j++) {
for (unsigned int i = 0; i < chans; i++)
@@ -1124,7 +1124,7 @@ TIMESTAMP IPCDevice::initialReadTimestamp(void)
// NOTE: Assumes sequential reads
int IPCDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun)
{
int rc, num_smpls; //, expect_smpls;
int rc, num_smpls, expect_smpls;
ssize_t avail_smpls;
TIMESTAMP expect_timestamp;
unsigned int i;
@@ -1153,17 +1153,25 @@ int IPCDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, T
uint64_t recv_timestamp = 0;
thread_enable_cancel(false);
num_smpls = ipc_shm_read(shm_io_rx_streams[i], (uint16_t *)bufs[i], len - avail_smpls,
&recv_timestamp, 1);
expect_timestamp = timestamp + avail_smpls;
thread_enable_cancel(true);
if (num_smpls == -ETIMEDOUT)
if(num_smpls == -ETIMEDOUT)
continue;
LOGCHAN(i, DDEV, DEBUG)
"Received timestamp = " << (TIMESTAMP)recv_timestamp << " (" << num_smpls << ")";
expect_smpls = len - avail_smpls;
// if (expect_smpls != num_smpls)
// LOGCHAN(i, DDEV, NOTICE)
// << "Unexpected recv buffer len: expect " << expect_smpls << " got " << num_smpls
// << ", diff=" << expect_smpls - num_smpls;
//expect_timestamp = timestamp + avail_smpls;
if (expect_timestamp != (TIMESTAMP)recv_timestamp)
LOGCHAN(i, DDEV, ERROR) << "Unexpected recv buffer timestamp: expect "
<< expect_timestamp << " got " << recv_timestamp << ", diff="
@@ -1173,8 +1181,7 @@ int IPCDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, T
rc = rx_buffers[i]->write(bufs[i], num_smpls, (TIMESTAMP)recv_timestamp);
if (rc < 0) {
LOGCHAN(i, DDEV, ERROR)
<< rx_buffers[i]->str_code(rc) << " num smpls: " << num_smpls << " chan: " << i;
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_code(rc) << " num smpls: " << num_smpls << " chan: " << i;
LOGCHAN(i, DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
if (rc != smpl_buf::ERROR_OVERFLOW)
return 0;
@@ -1209,10 +1216,12 @@ int IPCDevice::writeSamples(std::vector<short *> &bufs, int len, bool *underrun,
for (i = 0; i < chans; i++) {
LOGCHAN(i, DDEV, DEBUG) << "send buffer of len " << len << " timestamp " << std::hex << timestamp;
thread_enable_cancel(false);
// thread_enable_cancel(false);
rc = ipc_shm_enqueue(shm_io_tx_streams[i], timestamp, len, (uint16_t *)bufs[i]);
thread_enable_cancel(true);
// rc = LMS_SendStream(&m_lms_stream_tx[i], bufs[i], len, &tx_metadata, 100);
// update_stream_stats_tx(i, underrun);
// thread_enable_cancel(true);
if (rc != len) {
LOGCHAN(i, DDEV, ERROR) << "LMS: Device Tx timed out (" << rc << " vs exp " << len << ").";
return -1;
@@ -1240,7 +1249,7 @@ bool IPCDevice::setTxFreq(double wFreq, size_t chan)
ipc_prim->u.set_freq_req.is_tx = 1;
ipc_prim->u.set_freq_req.freq = wFreq;
return send_chan_wait_rsp(chan, msg, IPC_IF_MSG_SETFREQ_CNF);
return ipc_sock_send(&sk_chan_state[chan], msg) < 0 ? false : true;
}
bool IPCDevice::setRxFreq(double wFreq, size_t chan)
@@ -1256,18 +1265,19 @@ bool IPCDevice::setRxFreq(double wFreq, size_t chan)
ipc_prim->u.set_freq_req.is_tx = 0;
ipc_prim->u.set_freq_req.freq = wFreq;
return send_chan_wait_rsp(chan, msg, IPC_IF_MSG_SETFREQ_CNF);
return ipc_sock_send(&sk_chan_state[chan], msg) < 0 ? false : true;
}
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
{
if (cfg->tx_sps != cfg->rx_sps) {
if (tx_sps != rx_sps) {
LOGC(DDEV, ERROR) << "IPC Requires tx_sps == rx_sps";
return NULL;
}
if (cfg->offset != 0.0) {
if (lo_offset != 0.0) {
LOGC(DDEV, ERROR) << "IPC doesn't support lo_offset";
return NULL;
}
return new IPCDevice(type, cfg);
return new IPCDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
}

View File

@@ -4,29 +4,21 @@
*
* 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 software is distributed under multiple licenses; see the COPYING file in
* the main directory for licensing information for this specific distribution.
*
* 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.
* This use of this software may be subject to additional restrictions.
* See the LEGAL file in the main directory for details.
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.
*/
#ifndef _IPC_DEVICE_H_
#define _IPC_DEVICE_H_
#include <cstdint>
#include <cstddef>
#include <climits>
#include <string>
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
@@ -34,32 +26,36 @@
extern "C" {
#include <osmocom/core/select.h>
#include <osmocom/core/timer.h>
#include <osmocom/core/utils.h>
#include "shm.h"
#include "ipc_shm.h"
}
#include "radioDevice.h"
#include "smpl_buf.h"
class smpl_buf;
#include <sys/time.h>
#include <math.h>
#include <limits.h>
#include <string>
#include <iostream>
#define IPC_MAX_NUM_TRX 8
struct ipc_per_trx_sock_state {
struct ipc_sock_state {
struct osmo_fd conn_bfd; /* fd for connection to the BTS */
struct osmo_timer_list timer; /* socket connect retry timer */
struct llist_head upqueue; /* queue for sending messages */
uint32_t messages_processed_mask; // (=| IPC_IF_MSG_xxx-IPC_IF_CHAN_MSG_OFFSET) bitmask
ipc_per_trx_sock_state() : conn_bfd(), timer(), upqueue(), messages_processed_mask()
{
conn_bfd.fd = -1;
}
};
#define IPC_MAX_NUM_TRX 8
/** A class to handle a LimeSuite supported device */
class IPCDevice : public RadioDevice {
protected:
struct ipc_per_trx_sock_state master_sk_state;
std::vector<struct ipc_per_trx_sock_state> sk_chan_state;
struct ipc_sock_state master_sk_state;
/* FIXME: current limit IPC_MAX_NUM_TRX chans, make dynamic */
struct ipc_sock_state sk_chan_state[IPC_MAX_NUM_TRX];
bool trx_is_started[IPC_MAX_NUM_TRX];
uint32_t tx_attenuation[IPC_MAX_NUM_TRX];
uint8_t tmp_state;
char shm_name[SHM_NAME_MAX];
@@ -68,9 +64,9 @@ class IPCDevice : public RadioDevice {
struct ipc_shm_region *shm_dec;
std::vector<smpl_buf *> rx_buffers;
double actualSampleRate;
double actualSampleRate; ///< the actual USRP sampling rate
bool started;
bool started; ///< flag indicates LMS has started
TIMESTAMP ts_initial, ts_offset;
@@ -83,48 +79,33 @@ class IPCDevice : public RadioDevice {
std::vector<struct ipc_shm_io *> shm_io_rx_streams;
std::vector<struct ipc_shm_io *> shm_io_tx_streams;
bool flush_recv(size_t num_pkts);
virtual bool flush_recv(size_t num_pkts);
void update_stream_stats_rx(size_t chan, bool *overrun);
void update_stream_stats_tx(size_t chan, bool *underrun);
void manually_poll_sock_fds();
void ipc_sock_close(ipc_per_trx_sock_state *state);
int ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim);
int ipc_rx_greeting_cnf(const struct ipc_sk_if_greeting *greeting_cnf);
int ipc_rx_info_cnf(const struct ipc_sk_if_info_cnf *info_cnf);
int ipc_rx_open_cnf(const struct ipc_sk_if_open_cnf *open_cnf);
int ipc_tx_open_req(struct ipc_per_trx_sock_state *state, uint32_t num_chans, uint32_t ref);
int ipc_chan_rx(uint8_t msg_type, ipc_sk_chan_if *ipc_prim, uint8_t chan_nr);
int ipc_rx_chan_start_cnf(ipc_sk_chan_if_op_rc *ret, uint8_t chan_nr);
int ipc_rx_chan_stop_cnf(ipc_sk_chan_if_op_rc *ret, uint8_t chan_nr);
int ipc_rx_chan_setgain_cnf(ipc_sk_chan_if_gain *ret, uint8_t chan_nr);
int ipc_rx_chan_setfreq_cnf(ipc_sk_chan_if_freq_cnf *ret, uint8_t chan_nr);
int ipc_rx_chan_notify_underflow(ipc_sk_chan_if_notfiy *ret, uint8_t chan_nr);
int ipc_rx_chan_notify_overflow(ipc_sk_chan_if_notfiy *ret, uint8_t chan_nr);
int ipc_rx_chan_settxattn_cnf(ipc_sk_chan_if_tx_attenuation *ret, uint8_t chan_nr);
bool send_chan_wait_rsp(uint32_t chan, struct msgb *msg_to_send, uint32_t expected_rsp_msg_id);
bool send_all_chan_wait_rsp(uint32_t msgid_to_send, uint32_t msgid_to_expect);
public:
int ipc_sock_read(struct osmo_fd *bfd);
int ipc_sock_write(struct osmo_fd *bfd);
int ipc_chan_sock_read(osmo_fd *bfd);
int ipc_chan_sock_write(osmo_fd *bfd);
public:
virtual void ipc_sock_close(ipc_sock_state *state);
virtual int ipc_sock_read(struct osmo_fd *bfd);
virtual int ipc_sock_write(struct osmo_fd *bfd);
virtual int ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim);
virtual int ipc_rx_greeting_cnf(const struct ipc_sk_if_greeting *greeting_cnf);
virtual int ipc_rx_info_cnf(const struct ipc_sk_if_info_cnf *info_cnf);
virtual int ipc_rx_open_cnf(const struct ipc_sk_if_open_cnf *open_cnf);
virtual int ipc_tx_open_req(struct ipc_sock_state *state, uint32_t num_chans, uint32_t ref);
/** Object constructor */
IPCDevice(InterfaceType iface, const struct trx_cfg *cfg);
IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths);
virtual ~IPCDevice() override;
/** Instantiate the IPC */
virtual int open() override;
/** Instantiate the LMS */
virtual int open(const std::string &args, int ref, bool swap_channels) override;
/** Start the IPC */
/** Start the LMS */
virtual bool start() override;
/** Stop the IPC */
/** Stop the LMS */
virtual bool stop() override;
/* FIXME: any != USRP1 will do for now... */
@@ -134,21 +115,21 @@ class IPCDevice : public RadioDevice {
}
/**
Read samples from the IPC.
Read samples from the LMS.
@param buf preallocated buf to contain read result
@param len number of samples desired
@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough
@param timestamp The timestamp of the first samples to be read
@param underrun Set if IPC does not have data to transmit, e.g. data not being sent fast enough
@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
@return The number of samples actually read
*/
virtual int readSamples(std::vector<short *> &buf, int len, bool *overrun, TIMESTAMP timestamp = 0xffffffff,
bool *underrun = NULL) override;
/**
Write samples to the IPC.
Write samples to the LMS.
@param buf Contains the data to be written.
@param len number of samples to write.
@param underrun Set if IPC does not have data to transmit, e.g. data not being sent fast enough
@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough
@param timestamp The timestamp of the first sample of the data buffer.
@return The number of samples actually written
*/
@@ -197,9 +178,6 @@ class IPCDevice : public RadioDevice {
/** return minimum Rx Gain **/
virtual double minRxGain(void) override;
/* FIXME: return rx_gains[chan] ? receive factor from IPC Driver? */
double rssiOffset(size_t chan) override { return 0.0f; };
double setPowerAttenuation(int atten, size_t chan) override;
double getPowerAttenuation(size_t chan = 0) override;
@@ -236,6 +214,16 @@ class IPCDevice : public RadioDevice {
{
return actualSampleRate;
}
int ipc_chan_sock_read(osmo_fd *bfd);
int ipc_chan_sock_write(osmo_fd *bfd);
int ipc_chan_rx(uint8_t msg_type, ipc_sk_chan_if *ipc_prim, uint8_t chan_nr);
int ipc_rx_chan_start_cnf(ipc_sk_chan_if_op_rc *ret, uint8_t chan_nr);
int ipc_rx_chan_stop_cnf(ipc_sk_chan_if_op_rc *ret, uint8_t chan_nr);
int ipc_rx_chan_setgain_cnf(ipc_sk_chan_if_gain *ret, uint8_t chan_nr);
int ipc_rx_chan_setfreq_cnf(ipc_sk_chan_if_freq_cnf *ret, uint8_t chan_nr);
int ipc_rx_chan_notify_underflow(ipc_sk_chan_if_notfiy *ret, uint8_t chan_nr);
int ipc_rx_chan_notify_overflow(ipc_sk_chan_if_notfiy *ret, uint8_t chan_nr);
int ipc_rx_chan_settxattn_cnf(ipc_sk_chan_if_tx_attenuation *ret, uint8_t chan_nr);
};
#endif // _IPC_DEVICE_H_

View File

@@ -1,34 +1,29 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(UHD_CFLAGS)
AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(UHD_CFLAGS)
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
AM_LDFLAGS = -lpthread -lrt
noinst_HEADERS = IPCDevice.h shm.h ipc_shm.h ipc_chan.h ipc_sock.h
if DEVICE_UHD
noinst_HEADERS += ../uhd/UHDDevice.h uhdwrap.h ipc-driver-test.h
endif
noinst_HEADERS = IPCDevice.h shm.h ../uhd/UHDDevice.h uhdwrap.h
noinst_LTLIBRARIES = libdevice.la
libdevice_la_SOURCES = IPCDevice.cpp shm.c ipc_shm.c ipc_chan.c ipc_sock.c
libdevice_la_CPPFLAGS = $(AM_CPPFLAGS) -DIPCMAGIC
libdevice_la_LIBADD = \
$(top_builddir)/Transceiver52M/device/common/libdevice_common.la \
-lpthread \
-lrt \
$(NULL)
if DEVICE_UHD
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
bin_PROGRAMS = ipc-driver-test
#ipc_driver_test_SHORTNAME = drvt
ipc_driver_test_SOURCES = ipc-driver-test.c uhdwrap.cpp ../uhd/UHDDevice.cpp
ipc_driver_test_SOURCES = ipc-driver-test.c uhdwrap.cpp ipc_shm.c ipc_chan.c ipc_sock.c ../uhd/UHDDevice.cpp
ipc_driver_test_LDADD = \
libdevice.la \
$(COMMON_LA) \
shm.lo \
$(LIBOSMOCORE_LIBS) \
$(UHD_LIBS) \
$(NULL)
endif
$(LIBOSMOCTRL_LIBS) \
$(LIBOSMOVTY_LIBS)
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

View File

@@ -4,19 +4,21 @@
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef __cplusplus
extern "C" {
#endif
#define _GNU_SOURCE
#include <pthread.h>
#include <debug.h>
#include <stdio.h>
@@ -49,7 +51,6 @@
#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;
@@ -64,19 +65,17 @@ void *global_dev;
static struct ipc_shm_region *decoded_region;
static struct {
int msocknum;
char *ud_prefix_dir;
} cmdline_cfg;
/* Debug Areas of the code */
//enum { DMAIN,
//};
static const struct log_info_cat default_categories[] = {
[DMAIN] = {
.name = "DMAIN",
.color = NULL,
.color = NULL,
.description = "Main generic category",
.loglevel = LOGL_DEBUG,.enabled = 1,
.loglevel = LOGL_DEBUG,.enabled = 1,
},
[DDEV] = {
[DDEV] = {
.name = "DDEV",
.description = "Device/Driver specific code",
.color = NULL,
@@ -88,6 +87,9 @@ const struct log_info log_infox = {
.cat = default_categories,
.num_cat = ARRAY_SIZE(default_categories),
};
#ifdef __cplusplus
}
#endif
#include "uhdwrap.h"
@@ -191,8 +193,7 @@ 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/ipc_sock%d_%d",
cmdline_cfg.ud_prefix_dir, cmdline_cfg.msocknum, i);
snprintf(chan_info->chan_ipc_sk_path, sizeof(chan_info->chan_ipc_sk_path), "%s_%d", IPC_SOCK_PATH_PREFIX, 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);
@@ -221,7 +222,6 @@ int ipc_rx_open_req(struct ipc_sk_if_open_req *open_req)
{
/* calculate size needed */
unsigned int len;
unsigned int i;
global_dev = uhdwrap_open(open_req);
@@ -232,14 +232,14 @@ int ipc_rx_open_req(struct ipc_sk_if_open_req *open_req)
/* Here we verify num_chans, rx_path, tx_path, clockref, etc. */
int rc = ipc_shm_setup(DEFAULT_SHM_NAME, len);
len = ipc_shm_encode_region((struct ipc_shm_raw_region *)shm, open_req->num_chans, 4, shmbuflen);
// LOGP(DMAIN, LOGL_NOTICE, "%s\n", osmo_hexdump((const unsigned char *)shm, 80));
// LOGP(DMAIN, LOGL_NOTICE, "%s\n", osmo_hexdump((const unsigned char *)shm, 80));
/* set up our own copy of the decoded area, we have to do it here,
* since the uhd wrapper does not allow starting single channels
* since the uhd wrapper does not allow starting single channels
* additionally go for the producer init for both, so only we are responsible for the init, instead
* of splitting it with the client and causing potential races if one side uses it too early */
* of splitting it with the client and causing potential races if one side uses it too early */
decoded_region = ipc_shm_decode_region(0, (struct ipc_shm_raw_region *)shm);
for (i = 0; i < open_req->num_chans; i++) {
for (unsigned int i = 0; i < open_req->num_chans; i++) {
// ios_tx_to_device[i] = ipc_shm_init_consumer(decoded_region->channels[i]->dl_stream);
ios_tx_to_device[i] = ipc_shm_init_producer(decoded_region->channels[i]->dl_stream);
ios_rx_from_device[i] = ipc_shm_init_producer(decoded_region->channels[i]->ul_stream);
@@ -249,14 +249,14 @@ int ipc_rx_open_req(struct ipc_sk_if_open_req *open_req)
return 0;
}
volatile bool ul_running = false;
volatile bool dl_running = false;
volatile int ul_running = 0;
volatile int dl_running = 0;
void *uplink_thread(void *x_void_ptr)
{
uint32_t chann = decoded_region->num_chans;
ul_running = true;
pthread_setname_np(pthread_self(), "uplink_rx");
ul_running = 1;
pthread_setname_np(pthread_self(), "uplink rx");
while (!ipc_exit_requested) {
int32_t read = uhdwrap_read(global_dev, chann);
@@ -269,8 +269,8 @@ void *uplink_thread(void *x_void_ptr)
void *downlink_thread(void *x_void_ptr)
{
int chann = decoded_region->num_chans;
dl_running = true;
pthread_setname_np(pthread_self(), "downlink_tx");
dl_running = 1;
pthread_setname_np(pthread_self(), "downlink tx");
while (!ipc_exit_requested) {
bool underrun;
@@ -288,9 +288,10 @@ int ipc_rx_chan_start_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr)
rc = uhdwrap_start(global_dev, chan_nr);
/* no per-chan start/stop */
if (!dl_running || !ul_running) {
if(!dl_running || !ul_running) {
/* chan != first chan start will "fail", which is fine, usrp can't start/stop chans independently */
if (rc) {
if(rc) {
LOGP(DMAIN, LOGL_INFO, "starting rx/tx threads.. req for chan:%d\n", chan_nr);
pthread_t rx, tx;
pthread_create(&rx, NULL, uplink_thread, 0);
@@ -400,7 +401,10 @@ int ipc_sock_init(const char *path, struct ipc_sock_state **global_state_var,
return -1;
}
osmo_fd_setup(bfd, bfd->fd, OSMO_FD_READ, sock_callback_fn, state, n);
bfd->when = BSC_FD_READ;
bfd->cb = sock_callback_fn;
bfd->data = state;
bfd->priv_nr = n;
rc = osmo_fd_register(bfd);
if (rc < 0) {
@@ -410,6 +414,8 @@ int ipc_sock_init(const char *path, struct ipc_sock_state **global_state_var,
return rc;
}
//osmo_signal_register_handler(SS_GLOBAL, IPC_if_signal_cb, NULL);
LOGP(DMAIN, LOGL_INFO, "Started listening on IPC socket: %s\n", path);
return 0;
@@ -417,22 +423,26 @@ int ipc_sock_init(const char *path, struct ipc_sock_state **global_state_var,
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");
printf( "ipc-driver-test Usage:\n"
" -h --help This message\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 } };
const struct option long_options[] = {
{ "help", 0, 0, 'h' },
{ "sock-num", 1, 0, 'n' },
{0,0,0,0}
};
c = getopt_long(argc, argv, "hu:n:", long_options, &option_index);
c = getopt_long(argc, argv, "hn:",
long_options, &option_index);
if (c == -1)
break;
@@ -441,13 +451,9 @@ 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':
cmdline_cfg.msocknum = atoi(optarg);
msocknum = atoi(optarg);
break;
default:
exit(2);
break;
@@ -460,33 +466,36 @@ static void handle_options(int argc, char **argv)
}
}
#if defined(IPCMAGIC) && defined(__cplusplus)
extern "C" int osmo_ctx_init(const char *id);
extern "C" int magicmain(int argc, char **argv)
{
osmo_ctx_init("main");
osmo_select_init();
#else
int main(int argc, char **argv)
{
char ipc_msock_path[128];
#endif
char ipc_msock_path[sizeof(IPC_SOCK_PATH_PREFIX)+3];
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);
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);
snprintf(ipc_msock_path,sizeof(ipc_msock_path), "%s%d", IPC_SOCK_PATH_PREFIX, msocknum);
LOGP(DMAIN, LOGL_INFO, "Starting %s\n", argv[0]);
ipc_sock_init(ipc_msock_path, &global_ipc_sock_state, ipc_sock_accept, 0);
while (!ipc_exit_requested)
osmo_select_main(0);
if (global_dev) {
unsigned int i;
for (i = 0; i < decoded_region->num_chans; i++)
if (global_dev)
for (unsigned int i = 0; i < decoded_region->num_chans; i++)
uhdwrap_stop(global_dev, i);
}
ipc_sock_close(global_ipc_sock_state);
//ipc_sock_close()
return 0;
}

View File

@@ -4,14 +4,12 @@
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
@@ -27,7 +25,7 @@ extern struct ipc_shm_io *ios_rx_from_device[8];
struct ipc_sock_state {
struct osmo_fd listen_bfd; /* fd for listen socket */
struct osmo_fd conn_bfd; /* fd for connection */
struct osmo_fd conn_bfd; /* fd for connection to lcr */
struct llist_head upqueue; /* queue for sending messages */
};
@@ -43,3 +41,4 @@ int ipc_rx_chan_stop_req(struct ipc_sk_chan_if_op_void *req, uint8_t chan_nr);
int ipc_rx_chan_setgain_req(struct ipc_sk_chan_if_gain *req, uint8_t chan_nr);
int ipc_rx_chan_setfreq_req(struct ipc_sk_chan_if_freq_req *req, uint8_t chan_nr);
int ipc_rx_chan_settxatten_req(struct ipc_sk_chan_if_tx_attenuation *req, uint8_t chan_nr);

View File

@@ -4,14 +4,12 @@
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
@@ -49,23 +47,29 @@ static int ipc_chan_rx(uint8_t msg_type, struct ipc_sk_chan_if *ipc_prim, uint8_
switch (msg_type) {
case IPC_IF_MSG_START_REQ:
rc = ipc_rx_chan_start_req(&ipc_prim->u.start_req, chan_nr);
fprintf(stderr, "%s:%d: IPC_IF_MSG_START_REQ chan priv no %d\n", __FILE__, __LINE__, chan_nr);
break;
case IPC_IF_MSG_STOP_REQ:
rc = ipc_rx_chan_stop_req(&ipc_prim->u.stop_req, chan_nr);
fprintf(stderr, "%s:%d: IPC_IF_MSG_STOP_REQ chan priv no %d\n", __FILE__, __LINE__, chan_nr);
break;
case IPC_IF_MSG_SETGAIN_REQ:
rc = ipc_rx_chan_setgain_req(&ipc_prim->u.set_gain_req, chan_nr);
fprintf(stderr, "%s:%d: IPC_IF_MSG_SETGAIN_REQ chan priv no %d\n", __FILE__, __LINE__, chan_nr);
break;
case IPC_IF_MSG_SETFREQ_REQ:
rc = ipc_rx_chan_setfreq_req(&ipc_prim->u.set_freq_req, chan_nr);
fprintf(stderr, "%s:%d: IPC_IF_MSG_SETFREQ_REQ chan priv no %d\n", __FILE__, __LINE__, chan_nr);
break;
case IPC_IF_MSG_SETTXATTN_REQ:
rc = ipc_rx_chan_settxatten_req(&ipc_prim->u.txatten_req, chan_nr);
fprintf(stderr, "%s:%d: IPC_IF_MSG_SETTXATTN_REQ chan priv no %d\n", __FILE__, __LINE__, chan_nr);
break;
default:
LOGP(DDEV, LOGL_ERROR, "Received unknown IPC msg type 0x%02x on chan %d\n", msg_type, chan_nr);
fprintf(stderr, "Received unknown IPC msg type %d\n", msg_type);
rc = -EINVAL;
}
fflush(stderr);
return rc;
}
@@ -96,7 +100,7 @@ static int ipc_chan_sock_read(struct osmo_fd *bfd)
}
if (rc < (int)sizeof(*ipc_prim)) {
LOGP(DDEV, LOGL_ERROR,
LOGP(DMAIN, LOGL_ERROR,
"Received %d bytes on Unix Socket, but primitive size "
"is %zu, discarding\n",
rc, sizeof(*ipc_prim));
@@ -127,7 +131,7 @@ int ipc_chan_sock_send(struct msgb *msg, uint8_t chan_nr)
return -EINVAL;
if (!state) {
LOGP(DDEV, LOGL_INFO,
LOGP(DMAIN, LOGL_INFO,
"IPC socket not created, "
"dropping message\n");
msgb_free(msg);
@@ -135,14 +139,14 @@ int ipc_chan_sock_send(struct msgb *msg, uint8_t chan_nr)
}
conn_bfd = &state->conn_bfd;
if (conn_bfd->fd <= 0) {
LOGP(DDEV, LOGL_NOTICE,
LOGP(DMAIN, LOGL_NOTICE,
"IPC socket not connected, "
"dropping message\n");
msgb_free(msg);
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
osmo_fd_write_enable(conn_bfd);
conn_bfd->when |= BSC_FD_WRITE;
return 0;
}
@@ -160,11 +164,11 @@ 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;
osmo_fd_write_disable(bfd);
bfd->when &= ~BSC_FD_WRITE;
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
LOGP(DDEV, LOGL_ERROR,
LOGP(DMAIN, LOGL_ERROR,
"message type (%d) with ZERO "
"bytes!\n",
ipc_prim->msg_type);
@@ -177,14 +181,14 @@ static int ipc_chan_sock_write(struct osmo_fd *bfd)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
osmo_fd_write_enable(bfd);
bfd->when |= BSC_FD_WRITE;
break;
}
goto close;
}
dontsend:
/* _after_ we send it, we can dequeue */
/* _after_ we send it, we can deueue */
msg2 = msgb_dequeue(&state->upqueue);
assert(msg == msg2);
msgb_free(msg);
@@ -200,12 +204,12 @@ static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags)
{
int rc = 0;
if (flags & OSMO_FD_READ)
if (flags & BSC_FD_READ)
rc = ipc_chan_sock_read(bfd);
if (rc < 0)
return rc;
if (flags & OSMO_FD_WRITE)
if (flags & BSC_FD_WRITE)
rc = ipc_chan_sock_write(bfd);
return rc;
@@ -222,25 +226,30 @@ int ipc_chan_sock_accept(struct osmo_fd *bfd, unsigned int flags)
len = sizeof(un_addr);
rc = accept(bfd->fd, (struct sockaddr *)&un_addr, &len);
if (rc < 0) {
LOGP(DDEV, LOGL_ERROR, "Failed to accept a new connection\n");
LOGP(DMAIN, LOGL_ERROR, "Failed to accept a new connection\n");
return -1;
}
if (conn_bfd->fd >= 0) {
LOGP(DDEV, LOGL_NOTICE,
LOGP(DMAIN, LOGL_NOTICE,
"osmo-trx connects but we already have "
"another active connection ?!?\n");
/* We already have one IPC connected, this is all we support */
osmo_fd_read_disable(&state->listen_bfd);
state->listen_bfd.when &= ~BSC_FD_READ;
close(rc);
return 0;
}
conn_bfd->fd = rc;
conn_bfd->when = BSC_FD_READ;
conn_bfd->cb = ipc_chan_sock_cb;
conn_bfd->data = state;
/* copy chan nr, required for proper bfd<->chan # mapping */
osmo_fd_setup(conn_bfd, rc, OSMO_FD_READ, ipc_chan_sock_cb, state, bfd->priv_nr);
conn_bfd->priv_nr = bfd->priv_nr;
if (osmo_fd_register(conn_bfd) != 0) {
LOGP(DDEV, LOGL_ERROR,
LOGP(DMAIN, LOGL_ERROR,
"Failed to register new connection "
"fd\n");
close(conn_bfd->fd);
@@ -248,7 +257,10 @@ int ipc_chan_sock_accept(struct osmo_fd *bfd, unsigned int flags)
return -1;
}
LOGP(DDEV, LOGL_NOTICE, "Unix socket connected to external osmo-trx\n");
LOGP(DMAIN, LOGL_NOTICE, "Unix socket connected to external osmo-trx\n");
/* send current info */
//IPC_tx_info_ind();
return 0;
}

View File

@@ -4,14 +4,12 @@
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef IPC_CHAN_H
#define IPC_CHAN_H

View File

@@ -4,14 +4,12 @@
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#ifdef __cplusplus
extern "C" {
@@ -26,15 +24,12 @@ extern "C" {
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <osmocom/core/panic.h>
#include <debug.h>
#ifdef __cplusplus
}
#endif
#define SAMPLE_SIZE_BYTE (sizeof(uint16_t) * 2)
#define SAMPLE_SIZE_BYTE sizeof(uint16_t) * 2
struct ipc_shm_io *ipc_shm_init_consumer(struct ipc_shm_stream *s)
{
@@ -61,44 +56,52 @@ struct ipc_shm_io *ipc_shm_init_producer(struct ipc_shm_stream *s)
struct ipc_shm_io *r = ipc_shm_init_consumer(s);
rv = pthread_mutexattr_init(&att);
if (rv != 0) {
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
rv = pthread_mutexattr_setrobust(&att, PTHREAD_MUTEX_ROBUST);
if (rv != 0) {
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
rv = pthread_mutexattr_setpshared(&att, PTHREAD_PROCESS_SHARED);
if (rv != 0) {
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
rv = pthread_mutex_init((pthread_mutex_t *)&r->this_stream->lock, &att);
if (rv != 0) {
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
pthread_mutexattr_destroy(&att);
rv = pthread_condattr_setpshared(&t1, PTHREAD_PROCESS_SHARED);
if (rv != 0) {
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
rv = pthread_condattr_setpshared(&t2, PTHREAD_PROCESS_SHARED);
if (rv != 0) {
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
rv = pthread_cond_init((pthread_cond_t *)&r->this_stream->cf, &t1);
if (rv != 0) {
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
rv = pthread_cond_init((pthread_cond_t *)&r->this_stream->ce, &t2);
if (rv != 0) {
osmo_panic("%s:%d rv:%d", __FILE__, __LINE__, rv);
fprintf(stderr, "%s:%d rv:%d", __FILE__, __LINE__, rv);
exit(EXIT_FAILURE);
}
pthread_condattr_destroy(&t1);
@@ -112,7 +115,8 @@ struct ipc_shm_io *ipc_shm_init_producer(struct ipc_shm_stream *s)
void ipc_shm_close(struct ipc_shm_io *r)
{
if (r) {
free(r->buf_ptrs);
if (r->buf_ptrs)
free(r->buf_ptrs);
free(r);
}
}

View File

@@ -4,14 +4,12 @@
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef IPC_SHM_H
#define IPC_SHM_H
@@ -28,7 +26,7 @@ extern "C" {
#endif
struct ipc_shm_io {
volatile struct ipc_shm_raw_stream *this_stream;
volatile struct ipc_shm_raw_stream *this_stream; // plus num_buffers at end
volatile struct ipc_shm_raw_smpl_buf **volatile buf_ptrs;
uint32_t partial_read_begin_ptr;
};

View File

@@ -4,14 +4,12 @@
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdio.h>
#include <stdlib.h>
@@ -56,7 +54,7 @@ static int ipc_rx(uint8_t msg_type, struct ipc_sk_if *ipc_prim)
rc = ipc_rx_open_req(&ipc_prim->u.open_req);
break;
default:
LOGP(DDEV, LOGL_ERROR, "Received unknown IPC msg type 0x%02x\n", msg_type);
LOGP(DMAIN, LOGL_ERROR, "Received unknown IPC msg type %d\n", msg_type);
rc = -EINVAL;
}
@@ -67,9 +65,10 @@ int ipc_sock_send(struct msgb *msg)
{
struct ipc_sock_state *state = global_ipc_sock_state;
struct osmo_fd *conn_bfd;
//struct ipc_sk_if *ipc_prim = (struct ipc_sk_if *) msg->data;
if (!state) {
LOGP(DDEV, LOGL_INFO,
LOGP(DMAIN, LOGL_INFO,
"IPC socket not created, "
"dropping message\n");
msgb_free(msg);
@@ -77,14 +76,14 @@ int ipc_sock_send(struct msgb *msg)
}
conn_bfd = &state->conn_bfd;
if (conn_bfd->fd <= 0) {
LOGP(DDEV, LOGL_NOTICE,
LOGP(DMAIN, LOGL_NOTICE,
"IPC socket not connected, "
"dropping message\n");
msgb_free(msg);
return -EIO;
}
msgb_enqueue(&state->upqueue, msg);
osmo_fd_write_enable(conn_bfd);
conn_bfd->when |= BSC_FD_WRITE;
return 0;
}
@@ -93,16 +92,16 @@ void ipc_sock_close(struct ipc_sock_state *state)
{
struct osmo_fd *bfd = &state->conn_bfd;
LOGP(DDEV, LOGL_NOTICE, "IPC socket has LOST connection\n");
LOGP(DMAIN, LOGL_NOTICE, "IPC socket has LOST connection\n");
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 */
osmo_fd_read_enable(&state->listen_bfd);
state->listen_bfd.when |= BSC_FD_READ;
/* flush the queue */
while (!llist_empty(&state->upqueue)) {
@@ -137,7 +136,7 @@ int ipc_sock_read(struct osmo_fd *bfd)
}
if (rc < (int)sizeof(*ipc_prim)) {
LOGP(DDEV, LOGL_ERROR,
LOGP(DMAIN, LOGL_ERROR,
"Received %d bytes on Unix Socket, but primitive size "
"is %zu, discarding\n",
rc, sizeof(*ipc_prim));
@@ -172,11 +171,11 @@ 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;
osmo_fd_write_disable(bfd);
bfd->when &= ~BSC_FD_WRITE;
/* bug hunter 8-): maybe someone forgot msgb_put(...) ? */
if (!msgb_length(msg)) {
LOGP(DDEV, LOGL_ERROR,
LOGP(DMAIN, LOGL_ERROR,
"message type (%d) with ZERO "
"bytes!\n",
ipc_prim->msg_type);
@@ -189,7 +188,7 @@ static int ipc_sock_write(struct osmo_fd *bfd)
goto close;
if (rc < 0) {
if (errno == EAGAIN) {
osmo_fd_write_enable(bfd);
bfd->when |= BSC_FD_WRITE;
break;
}
goto close;
@@ -212,12 +211,12 @@ static int ipc_sock_cb(struct osmo_fd *bfd, unsigned int flags)
{
int rc = 0;
if (flags & OSMO_FD_READ)
if (flags & BSC_FD_READ)
rc = ipc_sock_read(bfd);
if (rc < 0)
return rc;
if (flags & OSMO_FD_WRITE)
if (flags & BSC_FD_WRITE)
rc = ipc_sock_write(bfd);
return rc;
@@ -235,24 +234,27 @@ int ipc_sock_accept(struct osmo_fd *bfd, unsigned int flags)
len = sizeof(un_addr);
rc = accept(bfd->fd, (struct sockaddr *)&un_addr, &len);
if (rc < 0) {
LOGP(DDEV, LOGL_ERROR, "Failed to accept a new connection\n");
LOGP(DMAIN, LOGL_ERROR, "Failed to accept a new connection\n");
return -1;
}
if (conn_bfd->fd >= 0) {
LOGP(DDEV, LOGL_NOTICE,
LOGP(DMAIN, LOGL_NOTICE,
"ip clent connects but we already have "
"another active connection ?!?\n");
/* We already have one IPC connected, this is all we support */
osmo_fd_read_disable(&state->listen_bfd);
state->listen_bfd.when &= ~BSC_FD_READ;
close(rc);
return 0;
}
osmo_fd_setup(conn_bfd, rc, OSMO_FD_READ, ipc_sock_cb, state, 0);
conn_bfd->fd = rc;
conn_bfd->when = BSC_FD_READ;
conn_bfd->cb = ipc_sock_cb;
conn_bfd->data = state;
if (osmo_fd_register(conn_bfd) != 0) {
LOGP(DDEV, LOGL_ERROR,
LOGP(DMAIN, LOGL_ERROR,
"Failed to register new connection "
"fd\n");
close(conn_bfd->fd);
@@ -260,7 +262,10 @@ int ipc_sock_accept(struct osmo_fd *bfd, unsigned int flags)
return -1;
}
LOGP(DDEV, LOGL_NOTICE, "Unix socket connected to external osmo-trx\n");
LOGP(DMAIN, LOGL_NOTICE, "Unix socket connected to external osmo-trx\n");
/* send current info */
//IPC_tx_info_ind();
return 0;
}

View File

@@ -4,14 +4,12 @@
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef IPC_SOCK_H
#define IPC_SOCK_H

View File

@@ -4,23 +4,23 @@
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#include <stdint.h>
#include <string.h>
#include <stdlib.h>
#include <stddef.h>
#include <osmocom/core/talloc.h>
#include "shm.h"
#define ENCDECDEBUG(...) //fprintf(stderr, __VA_ARGS__)
//#define ENCDECDEBUG
/* Convert offsets to pointers */
struct ipc_shm_stream *ipc_shm_decode_stream(void *tall_ctx, struct ipc_shm_raw_region *root_raw,
@@ -37,7 +37,9 @@ struct ipc_shm_stream *ipc_shm_decode_stream(void *tall_ctx, struct ipc_shm_raw_
stream->buffer_size = stream_raw->buffer_size;
stream->raw = stream_raw;
for (i = 0; i < stream->num_buffers; i++) {
ENCDECDEBUG("decode: smpl_buf %d at offset %u\n", i, stream_raw->buffer_offset[i]);
#ifdef ENCDECDEBUG
fprintf(stderr, "decode: smpl_buf %d at offset %u\n", i, stream_raw->buffer_offset[i]);
#endif
stream->buffers[i] =
(struct ipc_shm_raw_smpl_buf *)(((uint8_t *)root_raw) + stream_raw->buffer_offset[i]);
}
@@ -51,7 +53,9 @@ struct ipc_shm_channel *ipc_shm_decode_channel(void *tall_ctx, struct ipc_shm_ra
chan = talloc_zero(tall_ctx, struct ipc_shm_channel);
if (!chan)
return NULL;
ENCDECDEBUG("decode: streams at offset %u and %u\n", chan_raw->dl_buf_offset, chan_raw->ul_buf_offset);
#ifdef ENCDECDEBUG
fprintf(stderr, "decode: streams at offset %u and %u\n", chan_raw->dl_buf_offset, chan_raw->ul_buf_offset);
#endif
chan->dl_stream = ipc_shm_decode_stream(
chan, root_raw, (struct ipc_shm_raw_stream *)(((uint8_t *)root_raw) + chan_raw->dl_buf_offset));
chan->ul_stream = ipc_shm_decode_stream(
@@ -69,7 +73,9 @@ struct ipc_shm_region *ipc_shm_decode_region(void *tall_ctx, struct ipc_shm_raw_
root->num_chans = root_raw->num_chans;
for (i = 0; i < root->num_chans; i++) {
ENCDECDEBUG("decode: channel %d at offset %u\n", i, root_raw->chan_offset[i]);
#ifdef ENCDECDEBUG
fprintf(stderr, "decode: channel %d at offset %u\n", i, root_raw->chan_offset[i]);
#endif
root->channels[i] = ipc_shm_decode_channel(
root, root_raw,
(struct ipc_shm_raw_channel *)(((uint8_t *)root_raw) + root_raw->chan_offset[i]));
@@ -82,7 +88,9 @@ unsigned int ipc_shm_encode_smpl_buf(struct ipc_shm_raw_region *root_raw, struct
{
unsigned int offset = sizeof(struct ipc_shm_raw_smpl_buf);
offset = (((uintptr_t)offset + 7) & ~0x07ULL);
ENCDECDEBUG("encode: smpl_buf at offset %u\n", offset);
#ifdef ENCDECDEBUG
fprintf(stderr, "encode: smpl_buf at offset %lu\n", (start - (uint8_t *)root_raw));
#endif
offset += buffer_size * sizeof(uint16_t) * 2; /* samples */
return offset;
}
@@ -94,7 +102,9 @@ unsigned int ipc_shm_encode_stream(struct ipc_shm_raw_region *root_raw, struct i
ptrdiff_t start = (ptrdiff_t)stream_raw;
unsigned int offset = sizeof(struct ipc_shm_raw_stream) + sizeof(uint32_t) * num_buffers;
offset = (((uintptr_t)offset + 7) & ~0x07ULL);
ENCDECDEBUG("encode: stream at offset %lu\n", (start - (ptrdiff_t)root_raw));
#ifdef ENCDECDEBUG
fprintf(stderr, "encode: stream at offset %lu\n", (start - (ptrdiff_t)root_raw));
#endif
if (root_raw) {
stream_raw->num_buffers = num_buffers;
stream_raw->buffer_size = buffer_size;
@@ -115,7 +125,9 @@ unsigned int ipc_shm_encode_channel(struct ipc_shm_raw_region *root_raw, struct
uint8_t *start = (uint8_t *)chan_raw;
unsigned int offset = sizeof(struct ipc_shm_raw_channel);
offset = (((uintptr_t)offset + 7) & ~0x07ULL);
ENCDECDEBUG("encode: channel at offset %lu\n", (start - (uint8_t *)root_raw));
#ifdef ENCDECDEBUG
fprintf(stderr, "encode: channel at offset %lu\n", (start - (uint8_t *)root_raw));
#endif
if (root_raw)
chan_raw->dl_buf_offset = (start + offset - (uint8_t *)root_raw);
offset += ipc_shm_encode_stream(root_raw, (struct ipc_shm_raw_stream *)(start + offset), num_buffers,
@@ -140,7 +152,9 @@ unsigned int ipc_shm_encode_region(struct ipc_shm_raw_region *root_raw, uint32_t
for (i = 0; i < num_chans; i++) {
if (root_raw)
root_raw->chan_offset[i] = (start + offset - (uintptr_t)root_raw);
ENCDECDEBUG("encode: channel %d chan_offset[i]=%lu\n", i, start + offset - (uintptr_t)root_raw);
#ifdef ENCDECDEBUG
fprintf(stderr, "encode: channel %d chan_offset[i]=%u\n", i, ofs);
#endif
offset += ipc_shm_encode_channel(root_raw, (struct ipc_shm_raw_channel *)(start + offset), num_buffers,
buffer_size);
}

View File

@@ -4,21 +4,19 @@
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#pragma once
#include <stdint.h>
#include <unistd.h>
#include <limits.h>
#include <pthread.h>
//#include <pthread.h>
#include <semaphore.h>
/* RAW structures */
@@ -29,9 +27,9 @@ struct ipc_shm_raw_smpl_buf {
};
struct ipc_shm_raw_stream {
pthread_mutex_t lock; /* protects this struct */
pthread_cond_t cf; /* signals fill to reader */
pthread_cond_t ce; /* signals empty nbuf to writer */
pthread_mutex_t lock;
pthread_cond_t cf;
pthread_cond_t ce;
uint32_t num_buffers;
uint32_t buffer_size; /* In samples */
uint32_t read_next;
@@ -79,6 +77,8 @@ 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 */

View File

@@ -4,14 +4,12 @@
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
extern "C" {
#include <osmocom/core/application.h>
@@ -31,16 +29,14 @@ extern "C" {
#include "../uhd/UHDDevice.h"
#include "uhdwrap.h"
#include "trx_vty.h"
#include "Logger.h"
#include "Threads.h"
#include "Utils.h"
// 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 uhd_wrap::open(const std::string &args, int ref, bool swap_channels)
{
int rv = uhd_device::open();
int rv = uhd_device::open(args, ref, swap_channels);
samps_per_buff_rx = rx_stream->get_max_num_samps();
samps_per_buff_tx = tx_stream->get_max_num_samps();
channel_count = usrp_dev->get_rx_num_channels();
@@ -87,33 +83,36 @@ int uhd_wrap::wrap_read(TIMESTAMP *timestamp)
extern "C" void *uhdwrap_open(struct ipc_sk_if_open_req *open_req)
{
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;
unsigned int rx_sps, tx_sps;
/* FIXME: dev arg string* */
/* FIXME: rx frontend bw? */
/* FIXME: tx frontend bw? */
ReferenceType cref;
switch (open_req->clockref) {
case FEATURE_MASK_CLOCKREF_EXTERNAL:
actual_cfg.clock_ref = ReferenceType::REF_EXTERNAL;
cref = ReferenceType::REF_EXTERNAL;
break;
case FEATURE_MASK_CLOCKREF_INTERNAL:
default:
actual_cfg.clock_ref = ReferenceType::REF_INTERNAL;
cref = 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++) {
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;
tx_paths.push_back(open_req->chan_info[i].tx_path);
rx_paths.push_back(open_req->chan_info[i].rx_path);
}
uhd_wrap *uhd_wrap_dev = new uhd_wrap(RadioDevice::NORMAL, &actual_cfg);
uhd_wrap_dev->open();
/* 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);
return uhd_wrap_dev;
}
@@ -155,7 +154,7 @@ extern "C" int32_t uhdwrap_write(void *dev, uint32_t num_chans, bool *underrun)
uhd_wrap *d = (uhd_wrap *)dev;
uint64_t timestamp;
int32_t len = -1;
int32_t len;
for (uint32_t i = 0; i < num_chans; i++) {
len = ipc_shm_read(ios_tx_to_device[i], (uint16_t *)&d->wrap_tx_buffs[i].front(), 5000, &timestamp, 1);
if (len < 0)
@@ -177,13 +176,13 @@ extern "C" double uhdwrap_set_freq(void *dev, double f, size_t chan, bool for_tx
extern "C" double uhdwrap_set_gain(void *dev, double f, size_t chan, bool for_tx)
{
uhd_wrap *d = (uhd_wrap *)dev;
// if (for_tx)
// return d->setTxGain(f, chan);
// else
return d->setRxGain(f, chan);
// if (for_tx)
// return d->setTxGain(f, chan);
// else
return d->setRxGain(f, chan);
}
extern "C" double uhdwrap_set_txatt(void *dev, double a, size_t chan)
extern "C" double uhdwrap_set_txatt(void *dev, double a, size_t chan)
{
uhd_wrap *d = (uhd_wrap *)dev;
return d->setPowerAttenuation(a, chan);

View File

@@ -4,14 +4,12 @@
*
* SPDX-License-Identifier: 0BSD
*
* Permission to use, copy, modify, and/or distribute this software for any purpose
* with or without fee is hereby granted.THE SOFTWARE IS PROVIDED "AS IS" AND THE
* AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR
* BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF
* CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
* USE OR PERFORMANCE OF THIS SOFTWARE.
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted.
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
PERFORMANCE OF THIS SOFTWARE.
*/
#ifndef IPC_UHDWRAP_H
#define IPC_UHDWRAP_H
@@ -41,7 +39,7 @@ class uhd_wrap : public uhd_device {
// void ipc_sock_close() override {};
int wrap_read(TIMESTAMP *timestamp);
virtual int open() override;
virtual int open(const std::string &args, int ref, bool swap_channels) override;
// bool start() override;
// bool stop() override;

View File

@@ -23,6 +23,7 @@
#include <map>
#include "trx_vty.h"
#include "Logger.h"
#include "Threads.h"
#include "LMSDevice.h"
@@ -31,7 +32,6 @@
#include <lime/LimeSuite.h>
extern "C" {
#include "trx_vty.h"
#include "osmo_signal.h"
#include <osmocom/core/utils.h>
}
@@ -40,6 +40,8 @@ extern "C" {
#include "config.h"
#endif
using namespace std;
#define MAX_ANTENNA_LIST_SIZE 10
#define GSM_CARRIER_BW 270000.0 /* 270kHz */
#define LMS_MIN_BW_SUPPORTED 2.5e6 /* 2.5mHz, minimum supported by LMS */
@@ -52,28 +54,58 @@ 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 dev_map_t dev_param_map {
static const std::map<enum lms_dev_type, struct dev_desc> 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" } },
};
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) */
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_1900), { 73.0, 1.7, -17.0 } }, /* FIXME: OS#4583: 1900MHz is failing in all TxGain values */
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_850), { 66.0, 3.1, -6.0 } }, /* FIXME: OS#4583: Ensure BAND2 is used at startup */
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_900), { 66.0, 2.8, -6.0 } }, /* FIXME: OS#4583: Ensure BAND2 is used at startup */
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_1800), { 66.0, -11.6, -17.0 } }, /* OS#4583: Any of BAND1 or BAND2 is fine */
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_1900), { 66.0, -9.2, -17.0 } }, /* FIXME: OS#4583: Ensure BAND1 is used at startup */
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_850), { 71.0, 6.8, -6.0 } },
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_900), { 71.0, 6.8, -6.0 } },
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_1800), { 65.0, -10.5, -17.0 } }, /* OS#4583: TxGain=71 (-4.4dBm) FAIL rms phase errors ~10° */
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_1900), { 71.0, -6.3, -17.0 } }, /* FIXME: OS#4583: all FAIL, BAND1/BAND2 rms phase errors >23° */
typedef std::tuple<lms_dev_type, enum gsm_band> dev_band_key;
/* Maximum LimeSuite Tx Gain which can be set/used without distorting the output
* signal, and the resulting real output power measured when that gain is used.
*/
struct dev_band_desc {
double nom_lms_tx_gain; /* dB */
double nom_out_tx_power; /* dBm */
};
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_850), { 73.0, 11.2 } },
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_900), { 73.0, 10.8 } },
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_1800), { 65.0, -3.5 } }, /* FIXME: OS#4583: 1800Mhz is failing above TxGain=65, which is around -3.5dBm (already < 0 dBm) */
{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_1900), { 73.0, 1.7 } }, /* FIXME: OS#4583: 1900MHz is failing in all TxGain values */
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_850), { 66.0, 3.1 } }, /* FIXME: OS#4583: Ensure BAND2 is used at startup */
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_900), { 66.0, 2.8 } }, /* FIXME: OS#4583: Ensure BAND2 is used at startup */
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_1800), { 66.0, -11.6 } }, /* OS#4583: Any of BAND1 or BAND2 is fine */
{ std::make_tuple(LMS_DEV_SDR_MINI, GSM_BAND_1900), { 66.0, -9.2 } }, /* FIXME: OS#4583: Ensure BAND1 is used at startup */
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_850), { 71.0, 6.8 } },
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_900), { 71.0, 6.8 } },
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_1800), { 65.0, -10.5 } }, /* OS#4583: TxGain=71 (-4.4dBm) FAIL rms phase errors ~10° */
{ std::make_tuple(LMS_DEV_NET_MICRO, GSM_BAND_1900), { 71.0, -6.3 } }, /* FIXME: OS#4583: all FAIL, BAND1/BAND2 rms phase errors >23° */
};
/* So far measurements done for B210 show really close to linear relationship
@@ -99,8 +131,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.desc_str.c_str(), desc.desc_str.length()) == 0) {
LOGC(DDEV, INFO) << "Device identified as " << desc.desc_str;
if (strncmp(device_info->deviceName, desc.name_prefix.c_str(), desc.name_prefix.length()) == 0) {
LOGC(DDEV, INFO) << "Device identified as " << desc.name_prefix;
return dev_type;
}
it++;
@@ -108,10 +140,11 @@ static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
return 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)
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)
{
LOGC(DDEV, INFO) << "creating LMS device...";
@@ -177,7 +210,7 @@ static void print_range(const char* name, lms_range_t *range)
int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::string &args)
{
unsigned int i, j;
std::vector<std::string> filters;
std::vector<string> filters;
filters = comma_delimited_to_vector(args.c_str());
@@ -198,7 +231,28 @@ int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::str
return -1;
}
int LMSDevice::open()
void LMSDevice::get_dev_band_desc(dev_band_desc& desc)
{
dev_band_map_it it;
enum gsm_band req_band = band;
if (req_band == 0) {
LOGC(DDEV, ERROR) << "Nominal Tx Power requested before Tx Frequency was set! Providing band 900 by default... ";
req_band = GSM_BAND_900;
}
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())
desc = it->second;
}
int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
{
lms_info_str_t* info_list;
lms_range_t range_sr;
@@ -211,12 +265,11 @@ int LMSDevice::open()
LMS_RegisterLogHandler(&lms_log_callback);
if ((rc = LMS_GetDeviceList(NULL)) < 0)
if ((n = LMS_GetDeviceList(NULL)) < 0)
LOGC(DDEV, ERROR) << "LMS_GetDeviceList(NULL) failed";
LOGC(DDEV, INFO) << "Devices found: " << rc;
if (rc < 1)
LOGC(DDEV, INFO) << "Devices found: " << n;
if (n < 1)
return -1;
n = rc;
info_list = new lms_info_str_t[n];
@@ -226,9 +279,9 @@ int LMSDevice::open()
for (i = 0; i < n; i++)
LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i];
dev_id = info_list_find(info_list, n, cfg->dev_args);
dev_id = info_list_find(info_list, n, args);
if (dev_id == -1) {
LOGC(DDEV, ERROR) << "No LMS device found with address '" << cfg->dev_args << "'";
LOGC(DDEV, ERROR) << "No LMS device found with address '" << args << "'";
delete[] info_list;
return -1;
}
@@ -245,15 +298,14 @@ int LMSDevice::open()
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 ((cfg->clock_ref != REF_EXTERNAL) && (cfg->clock_ref != REF_INTERNAL)) {
if ((ref != REF_EXTERNAL) && (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 (cfg->clock_ref == REF_EXTERNAL) {
if (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 */
@@ -268,7 +320,7 @@ int LMSDevice::open()
}
/* if reference clock is internal, setup must happen _after_ calling LMS_Init */
if (cfg->clock_ref == REF_INTERNAL) {
if (ref == REF_INTERNAL) {
LOGC(DDEV, INFO) << "Setting Internal clock reference";
/* Internal freq param is not used */
if (!do_clock_src_freq(REF_INTERNAL, 0))
@@ -404,8 +456,6 @@ bool LMSDevice::stop()
LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]);
}
band_reset();
started = false;
return true;
}
@@ -421,8 +471,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.desc_str << " doesn't support internal reference clock";
LOGC(DDEV, ERROR) << "Device type " << dev_desc.name_prefix
<< " doesn't support internal reference clock";
return false;
}
/* According to lms using LMS_CLOCK_EXTREF with a
@@ -440,8 +490,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.desc_str << " doesn't support switching clock source through SW";
LOGC(DDEV, INFO) << "Device type " << dev_desc.name_prefix
<< " doesn't support switching clock source through SW";
}
return true;
@@ -511,21 +561,6 @@ double LMSDevice::setRxGain(double dB, size_t chan)
return rx_gains[chan];
}
double LMSDevice::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 LMSDevice::setPowerAttenuation(int atten, size_t chan)
{
double tx_power, dB;
@@ -934,6 +969,9 @@ 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;
@@ -941,14 +979,29 @@ bool LMSDevice::setTxFreq(double wFreq, size_t chan)
LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz";
if (!update_band_from_freq(wFreq, chan, true))
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
if (req_arfcn == 0xffff) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
return false;
}
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
<< " Hz (ARFCN " << req_arfcn << " )";
return false;
}
if (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;
}
@@ -956,9 +1009,6 @@ 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;
@@ -967,15 +1017,18 @@ bool LMSDevice::setRxFreq(double wFreq, size_t chan)
return true;
}
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector < std::string > &tx_paths,
const std::vector < std::string > &rx_paths)
{
if (cfg->tx_sps != cfg->rx_sps) {
LOGC(DDEV, ERROR) << "LMS requires tx_sps == rx_sps";
if (tx_sps != rx_sps) {
LOGC(DDEV, ERROR) << "LMS Requires tx_sps == rx_sps";
return NULL;
}
if (cfg->offset != 0.0) {
if (lo_offset != 0.0) {
LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset";
return NULL;
}
return new LMSDevice(type, cfg);
return new LMSDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
}

View File

@@ -18,13 +18,11 @@
#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>
@@ -54,52 +52,10 @@ enum lms_dev_type {
LMS_DEV_UNKNOWN,
};
struct dev_band_desc {
/* Maximum LimeSuite Tx Gain which can be set/used without distorting
the output * signal, and the resulting real output power measured
when that gain is used.
*/
double nom_lms_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.
Correct measured values only provided for LimeSDR-USB so far.
rssiOffset = rxGain + rxgain2rssioffset_rel;
*/
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>;
struct dev_band_desc;
/** A class to handle a LimeSuite supported device */
class LMSDevice:public RadioDevice, public band_manager<power_map_t, dev_map_t> {
class LMSDevice:public RadioDevice {
private:
lms_device_t *m_lms_dev;
@@ -116,6 +72,7 @@ private:
TIMESTAMP ts_initial, ts_offset;
std::vector<double> tx_gains, rx_gains;
enum gsm_band band;
enum lms_dev_type m_dev_type;
@@ -127,25 +84,28 @@ 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);
public:
/** Object constructor */
LMSDevice(InterfaceType iface, const struct trx_cfg *cfg);
~LMSDevice();
LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
~LMSDevice();
/** Instantiate the LMS */
int open();
/** Instantiate the LMS */
int open(const std::string &args, int ref, bool swap_channels);
/** 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.
@@ -213,7 +173,6 @@ public:
/** return minimum Rx Gain **/
double minRxGain(void);
double rssiOffset(size_t chan);
double setPowerAttenuation(int atten, size_t chan);
double getPowerAttenuation(size_t chan = 0);

View File

@@ -1,7 +1,7 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LMS_CFLAGS)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS)
noinst_HEADERS = LMSDevice.h

View File

@@ -1,7 +1,7 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS)
noinst_HEADERS = UHDDevice.h

View File

@@ -91,7 +91,19 @@ extern "C" {
* USRP1 with timestamps is not supported by UHD.
*/
static const dev_map_t dev_param_map {
/* Device Type, Tx-SPS, Rx-SPS */
typedef std::tuple<uhd_dev_type, int, int> dev_key;
/* Device parameter descriptor */
struct dev_desc {
unsigned channels;
double mcr;
double rate;
double offset;
std::string str;
};
static const std::map<dev_key, dev_desc> dev_param_map {
{ std::make_tuple(USRP2, 1, 1), { 1, 0.0, 390625, 1.2184e-4, "N2XX 1 SPS" } },
{ std::make_tuple(USRP2, 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" } },
@@ -117,21 +129,30 @@ static const dev_map_t dev_param_map {
{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
};
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 } },
{ std::make_tuple(B200, GSM_BAND_1900), { 89.75, 7.7, -11.0 } },
{ std::make_tuple(B210, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
{ std::make_tuple(B210, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
{ std::make_tuple(B210, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
{ std::make_tuple(B210, GSM_BAND_1900), { 89.75, 7.7, -11.0 } },
typedef std::tuple<uhd_dev_type, enum gsm_band> dev_band_key;
/* 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. */
struct dev_band_desc {
double nom_uhd_tx_gain; /* dB */
double nom_out_tx_power; /* dBm */
};
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
{ std::make_tuple(B200, GSM_BAND_850), { 89.75, 13.3 } },
{ std::make_tuple(B200, GSM_BAND_900), { 89.75, 13.3 } },
{ std::make_tuple(B200, GSM_BAND_1800), { 89.75, 7.5 } },
{ std::make_tuple(B200, GSM_BAND_1900), { 89.75, 7.7 } },
{ std::make_tuple(B210, GSM_BAND_850), { 89.75, 13.3 } },
{ std::make_tuple(B210, GSM_BAND_900), { 89.75, 13.3 } },
{ std::make_tuple(B210, GSM_BAND_1800), { 89.75, 7.5 } },
{ std::make_tuple(B210, GSM_BAND_1900), { 89.75, 7.7 } },
};
void *async_event_loop(uhd_device *dev)
{
set_selfthread_name("UHDAsyncEvent");
osmo_cpu_sched_vty_apply_localthread();
OSMO_ASSERT(osmo_cpu_sched_vty_apply_localthread() == 0);
while (1) {
dev->recv_async_msg();
@@ -206,10 +227,15 @@ static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
}
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)
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)
{
}
@@ -221,6 +247,27 @@ uhd_device::~uhd_device()
delete rx_buffers[i];
}
void uhd_device::get_dev_band_desc(dev_band_desc& desc)
{
dev_band_map_it it;
enum gsm_band req_band = band;
if (req_band == 0) {
LOGC(DDEV, ERROR) << "Nominal Tx Power requested before Tx Frequency was set! Providing band 900 by default... ";
req_band = GSM_BAND_900;
}
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 Tx Power measurements 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())
desc = it->second;
}
void uhd_device::init_gains()
{
double tx_gain_min, tx_gain_max;
@@ -285,7 +332,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.desc_str;
LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
}
double uhd_device::setRxGain(double db, size_t chan)
@@ -295,9 +342,6 @@ 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);
@@ -316,21 +360,6 @@ double uhd_device::getRxGain(size_t chan)
return rx_gains[chan];
}
double uhd_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 uhd_device::setPowerAttenuation(int atten, size_t chan) {
double tx_power, db;
dev_band_desc desc;
@@ -340,9 +369,6 @@ 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);
@@ -494,10 +520,9 @@ void uhd_device::set_channels(bool swap)
}
}
int uhd_device::open()
int uhd_device::open(const std::string &args, int ref, bool swap_channels)
{
const char *refstr;
int clock_lock_attempts = 15;
/* Register msg handler. Different APIs depending on UHD version */
#ifdef USE_UHD_3_11
@@ -510,10 +535,10 @@ int uhd_device::open()
#endif
// Find UHD devices
uhd::device_addr_t addr(cfg->dev_args);
uhd::device_addr_t addr(args);
uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
if (dev_addrs.size() == 0) {
LOGC(DDEV, ALERT) << "No UHD devices found with address '" << cfg->dev_args << "'";
LOGC(DDEV, ALERT) << "No UHD devices found with address '" << args << "'";
return -1;
}
@@ -522,7 +547,7 @@ int uhd_device::open()
try {
usrp_dev = uhd::usrp::multi_usrp::make(addr);
} catch(uhd::key_error::exception &e) {
LOGC(DDEV, ALERT) << "UHD make failed, device " << cfg->dev_args << ", exception:\n" << e.what();
LOGC(DDEV, ALERT) << "UHD make failed, device " << args << ", exception:\n" << e.what();
return -1;
}
@@ -530,16 +555,14 @@ int uhd_device::open()
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(cfg->swap_channels);
} catch (const std::exception &e) {
set_channels(swap_channels);
} catch (const std::exception &e) {
LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
return -1;
}
@@ -555,7 +578,7 @@ int uhd_device::open()
rx_gains.resize(chans);
rx_buffers.resize(chans);
switch (cfg->clock_ref) {
switch (ref) {
case REF_INTERNAL:
refstr = "internal";
break;
@@ -572,19 +595,6 @@ int uhd_device::open()
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) {
@@ -634,32 +644,6 @@ int uhd_device::open()
// 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;
@@ -770,8 +754,6 @@ bool uhd_device::stop()
for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i]->reset();
band_reset();
started = false;
return true;
}
@@ -1010,22 +992,17 @@ 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;
@@ -1055,12 +1032,37 @@ 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);
return set_freq(wFreq, chan, true);
req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
if (req_arfcn == 0xffff) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
return false;
}
if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
<< " Hz (ARFCN " << req_arfcn << " )";
return false;
}
if (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 (!set_freq(wFreq, chan, true))
return false;
band = req_band;
return true;
}
bool uhd_device::setRxFreq(double wFreq, size_t chan)
@@ -1069,6 +1071,7 @@ 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);
}
@@ -1318,8 +1321,11 @@ std::string uhd_device::str_code(uhd::async_metadata_t metadata)
}
#ifndef IPCMAGIC
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths)
{
return new uhd_device(type, cfg);
return new uhd_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
}
#endif

View File

@@ -30,7 +30,6 @@
#include "config.h"
#endif
#include "bandmanager.h"
#include "radioDevice.h"
#include "smpl_buf.h"
@@ -57,33 +56,7 @@ enum uhd_dev_type {
LIMESDR,
};
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 */
};
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>;
struct dev_band_desc;
/*
uhd_device - UHD implementation of the Device interface. Timestamped samples
@@ -92,19 +65,19 @@ using dev_map_t = std::map<dev_key, dev_desc>;
Events and errors such as underruns are reported asynchronously
by the device and received in a separate thread.
*/
class uhd_device : public RadioDevice, public band_manager<power_map_t, dev_map_t> {
class uhd_device : public RadioDevice {
public:
uhd_device(InterfaceType iface, const struct trx_cfg *cfg);
~uhd_device();
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();
bool start();
bool stop();
bool restart();
enum TxWindowType getWindowType()
{
return tx_window;
}
int open(const std::string &args, int ref, bool swap_channels);
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);
@@ -127,7 +100,6 @@ public:
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);
@@ -174,6 +146,7 @@ protected:
std::vector<double> tx_gains, rx_gains;
std::vector<double> tx_freqs, rx_freqs;
enum gsm_band band;
size_t tx_spp, rx_spp;
bool started;
@@ -202,6 +175,8 @@ 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);
Thread *async_event_thrd;
Mutex tune_lock;
};

View File

@@ -1,7 +1,7 @@
include $(top_srcdir)/Makefile.common
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(USRP_CFLAGS)
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(USRP_CFLAGS)
rev2dir = $(datadir)/usrp/rev2
rev4dir = $(datadir)/usrp/rev4

View File

@@ -60,7 +60,11 @@ const dboardConfigType dboardConfig = TXA_RXB;
const double USRPDevice::masterClockRate = 52.0e6;
USRPDevice::USRPDevice(InterfaceType iface, const struct trx_cfg *cfg) : RadioDevice(iface, cfg)
USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
size_t chan_num, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths):
RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths)
{
LOGC(DDEV, INFO) << "creating USRP device...";
@@ -90,7 +94,7 @@ USRPDevice::USRPDevice(InterfaceType iface, const struct trx_cfg *cfg) : RadioDe
#endif
}
int USRPDevice::open()
int USRPDevice::open(const std::string &, int, bool)
{
writeLock.unlock();
@@ -583,7 +587,8 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
{
#ifndef SWLOOPBACK
short data[] = {0x00,0x02,0x00,0x00};
/* FIXME: big endian */
uint32_t *wordPtr = (uint32_t *) data;
*wordPtr = host_to_usrp_u32(*wordPtr);
bool tmpUnderrun;
std::vector<short *> buf(1, data);
@@ -654,19 +659,22 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
bool USRPDevice::setRxFreq(double wFreq) { return true;};
#endif
RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
InterfaceType iface, size_t chans, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths)
{
if (cfg->tx_sps != cfg->rx_sps) {
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);
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);
}

View File

@@ -104,21 +104,20 @@ private:
public:
/** Object constructor */
USRPDevice(InterfaceType iface, const struct trx_cfg *cfg);
USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
const std::vector<std::string>& tx_paths,
const std::vector<std::string>& rx_paths);
/** Instantiate the USRP */
int open();
/** Instantiate the USRP */
int open(const std::string &, int, bool);
/** 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.
@@ -175,8 +174,6 @@ private:
/** return minimum Rx Gain **/
double minRxGain(void);
double rssiOffset(size_t chan) { return 0.0f; } /* FIXME: not implemented */
double setPowerAttenuation(int atten, size_t chan);
double getPowerAttenuation(size_t chan=0);

View File

@@ -1,149 +0,0 @@
#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
};

View File

@@ -1,305 +0,0 @@
/* -*- 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] = (char)(output[i] * -127); // pre flip bits!
}
NO_UBSAN void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary,
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;
}

View File

@@ -1,89 +0,0 @@
#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;
};

View File

@@ -1,392 +0,0 @@
/* -*- 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;
}
}

View File

@@ -1,64 +0,0 @@
/* -*- 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 */

View File

@@ -1,473 +0,0 @@
#pragma once
/*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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);
blade_check(bladerf_set_pll_refclk, dev, 10000000UL);
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)
{
auto fn = [this] {
int status = 0;
if (!stop_lower_threads_flag)
status = bladerf_stream(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)
{
auto fn = [this] {
int status = 0;
if (!stop_lower_threads_flag)
status = bladerf_stream(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);
}
};

View File

@@ -1,249 +0,0 @@
#pragma once
/*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <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> {};

View File

@@ -1,275 +0,0 @@
/*
* 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);
}

View File

@@ -1,71 +0,0 @@
/*
* (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;
}

View File

@@ -1,98 +0,0 @@
/*
* 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));
}

View File

@@ -1,160 +0,0 @@
/*
* (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"
}
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 = std::thread(fn);
set_name_aff_sched(lower_rx_task.native_handle(), sched_params::thread_names::RXRUN);
usleep(1000);
auto fn2 = get_tx_burst_handler_fn(tx_bh());
lower_tx_task = std::thread(fn2);
set_name_aff_sched(lower_tx_task.native_handle(), sched_params::thread_names::TXRUN);
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;
lower_rx_task.join();
std::cerr << "L rx dead..." << std::endl;
lower_tx_task.join();
std::cerr << "L tx dead..." << std::endl;
}
void ms_trx::submit_burst(blade_sample_type *buffer, int len, GSM::Time target)
{
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 && (now_time.TN() - target_tn < 1))) {
std::cerr << "## TX too late?! fn DIFF:" << diff_fn << " tn LOCAL: " << now_time.TN()
<< " tn OTHER: " << target_tn << std::endl;
return;
}
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
}

View File

@@ -1,396 +0,0 @@
#pragma once
/*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <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"
const unsigned int ONE_TS_BURST_LEN = (3 + 58 + 26 + 58 + 3 + 8.25) * 4 /*sps*/;
const unsigned int NUM_RXQ_FRAMES = 1; // rx thread <-> upper rx queue
const unsigned int 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<8 * NUM_RXQ_FRAMES, 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;
}
};
static struct sched_params {
enum thread_names { U_CTL = 0, U_RX, U_TX, SCH_SEARCH, MAIN, LEAKCHECK, RXRUN, TXRUN, _THRD_NAME_COUNT };
enum target { ODROID = 0, PI4 };
const char *name;
int core;
int schedtype;
int prio;
} schdp[][sched_params::_THRD_NAME_COUNT]{
{
{ "upper_ctrl", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) },
{ "upper_rx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
{ "upper_tx", 2, SCHED_RR, sched_get_priority_max(SCHED_RR) - 1 },
{ "sch_search", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
{ "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
{ "leakcheck", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
{ "rxrun", 4, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
{ "txrun", 5, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
},
{
{ "upper_ctrl", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) },
{ "upper_rx", 1, SCHED_RR, sched_get_priority_max(SCHED_RR) - 5 },
{ "upper_tx", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
{ "sch_search", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
{ "main", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 5 },
{ "leakcheck", 1, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 10 },
{ "rxrun", 2, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 2 },
{ "txrun", 3, SCHED_FIFO, sched_get_priority_max(SCHED_FIFO) - 1 },
},
};
using ts_hitter_q_t = spsc_cond<64, GSM::Time, true, false>;
struct ms_trx : public BASET {
using base = BASET;
static dummylog dummy_log;
unsigned int mTSC;
unsigned int mBSIC;
int timing_advance;
bool do_auto_gain;
std::thread lower_rx_task;
std::thread 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;
int hw_cpus;
sched_params::target hw_target;
single_thread_pool worker_thread;
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 }, hw_cpus(std::thread::hardware_concurrency()),
hw_target(hw_cpus > 4 ? sched_params::target::ODROID : sched_params::target::PI4),
upper_is_ready(false)
{
std::cerr << "scheduling for: " << (hw_cpus > 4 ? "odroid" : "pi4") << std::endl;
set_name_aff_sched(worker_thread.get_handle(), sched_params::thread_names::SCH_SEARCH);
}
virtual ~ms_trx()
{
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;
}
void set_name_aff_sched(sched_params::thread_names name)
{
set_name_aff_sched(pthread_self(), name);
}
void set_name_aff_sched(std::thread::native_handle_type h, sched_params::thread_names name)
{
auto tgt = schdp[hw_target][name];
// std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << std::endl;
set_name_aff_sched(h, tgt.name, tgt.core, tgt.schedtype, tgt.prio);
}
using pt_sig = void *(*)(void *);
pthread_t spawn_worker_thread(sched_params::thread_names name, pt_sig fun, void *arg)
{
auto tgt = schdp[hw_target][name];
// std::cerr << "scheduling for: " << tgt.name << ":" << tgt.core << " prio:" << tgt.prio << std::endl;
return do_spawn_thr(tgt.name, tgt.core, tgt.schedtype, tgt.prio, fun, arg);
}
private:
void set_name_aff_sched(std::thread::native_handle_type h, const char *name, int cpunum, int schedtype,
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);
}
}
pthread_t do_spawn_thr(const char *name, int cpunum, int schedtype, int prio, pt_sig fun, void *arg)
{
pthread_t thread;
pthread_attr_t attr;
pthread_attr_init(&attr);
sched_param sch_params;
sch_params.sched_priority = prio;
cpu_set_t cpuset;
CPU_ZERO(&cpuset);
CPU_SET(cpunum, &cpuset);
auto a = pthread_attr_setaffinity_np(&attr, sizeof(cpu_set_t), &cpuset);
a |= pthread_attr_setschedpolicy(&attr, schedtype);
a |= pthread_attr_setschedparam(&attr, &sch_params);
a |= pthread_attr_setinheritsched(&attr, PTHREAD_EXPLICIT_SCHED);
if(a)
std::cerr << "thread arg rc:" << a << std::endl;
pthread_create(&thread, &attr, fun, arg);
pthread_setname_np(thread, name);
pthread_attr_destroy(&attr);
return thread;
}
};

View File

@@ -1,314 +0,0 @@
/*
* (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 <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;
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;
memset((void *)&sch_acq_buffer[0], 0, sizeof(sch_acq_buffer));
convert_and_scale(which_out_buffer, which_in_buffer, buf_len * 2, 1.f / float(rxFullScale));
if (is_first_sch_acq) {
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 (sch_decode_success) {
const auto ts_offset_symb = 0;
if (is_first_sch_acq) {
// update ts to first sample in sch buffer, to allow delay calc for current ts
first_sch_ts_start = first_sch_buf_rcv_ts + start - (ts_offset_symb * 4) - 1;
} 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;
}
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;
}

View File

@@ -1,78 +0,0 @@
/*
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <atomic>
#include "ms_trxcon_if.h"
extern "C" {
#include <osmocom/bb/trxcon/trxcon.h>
#include <osmocom/bb/trxcon/l1ctl_server.h>
#include <osmocom/core/panic.h>
}
extern tx_queue_t txq;
extern cmd_queue_t cmdq_to_phy;
extern cmdr_queue_t cmdq_from_phy;
extern std::atomic<bool> g_exit_flag;
// trxcon C call(back) if
extern "C" {
int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon_phyif_burst_req *br)
{
if (br->burst_len == 0) // dummy/nope
return 0;
OSMO_ASSERT(br->burst != 0);
internal_q_tx_buf b(br);
if (!g_exit_flag)
txq.spsc_push(&b);
return 0;
}
int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon_phyif_cmd *cmd)
{
#ifdef TXDEBUG
DBGLG() << "TOP C: " << cmd2str(cmd->type) << std::endl;
#endif
if (!g_exit_flag)
cmdq_to_phy.spsc_push(cmd);
// q for resp polling happens in main loop
return 0;
}
void trxcon_phyif_close(void *phyif)
{
}
void trxcon_l1ctl_close(struct trxcon_inst *trxcon)
{
/* Avoid use-after-free: both *fi and *trxcon are children of
* the L2IF (L1CTL connection), so we need to re-parent *fi
* to NULL before calling l1ctl_client_conn_close(). */
talloc_steal(NULL, trxcon->fi);
l1ctl_client_conn_close((struct l1ctl_client *)trxcon->l2if);
}
int trxcon_l1ctl_send(struct trxcon_inst *trxcon, struct msgb *msg)
{
struct l1ctl_client *l1c = (struct l1ctl_client *)trxcon->l2if;
return l1ctl_client_send(l1c, msg);
}
}

View File

@@ -1,42 +0,0 @@
#pragma once
/*
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include "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>;

View File

@@ -1,473 +0,0 @@
/*
* (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"
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;
}
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
}
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);
}
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;
}
trx->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;
}

View File

@@ -1,48 +0,0 @@
#pragma once
/*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <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){};
};

View File

@@ -1,329 +0,0 @@
/*
* (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

View File

@@ -1,48 +0,0 @@
#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);

View File

@@ -1,92 +0,0 @@
#pragma once
/*
* (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <functional>
#include <thread>
#include <atomic>
#include <vector>
#include <future>
#include <mutex>
#include <queue>
struct single_thread_pool {
std::mutex m;
std::condition_variable cv;
std::atomic<bool> stop_flag;
std::atomic<bool> is_ready;
std::deque<std::function<void()>> wq;
std::thread worker_thread;
template <class F>
void add_task(F &&f)
{
std::unique_lock<std::mutex> l(m);
wq.emplace_back(std::forward<F>(f));
cv.notify_one();
return;
}
single_thread_pool() : stop_flag(false), is_ready(false), worker_thread(std::thread([this] { thread_loop(); }))
{
}
~single_thread_pool()
{
stop();
}
std::thread::native_handle_type get_handle()
{
return worker_thread.native_handle();
}
private:
void stop()
{
{
std::unique_lock<std::mutex> l(m);
wq.clear();
stop_flag = true;
cv.notify_one();
}
worker_thread.join();
}
void thread_loop()
{
while (true) {
is_ready = true;
std::function<void()> f;
{
std::unique_lock<std::mutex> l(m);
if (wq.empty()) {
cv.wait(l, [&] { return !wq.empty() || stop_flag; });
}
if (stop_flag)
return;
is_ready = false;
f = std::move(wq.front());
wq.pop_front();
}
f();
}
}
};

View File

@@ -1,274 +0,0 @@
#pragma once
/*
* (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
* All Rights Reserved
*
* Author: Eric Wild <ewild@sysmocom.de>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
#include <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)
{
auto fn = [this, burst_handler] {
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_stream->issue_stream_cmd(stream_cmd);
while (!stop_lower_threads_flag) {
rx_cb(burst_handler);
}
};
return fn;
}
auto get_tx_burst_handler_fn(bh_fn_t burst_handler)
{
auto fn = [] {
// dummy
};
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
}
};

View File

@@ -12,6 +12,10 @@
* 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
@@ -77,24 +81,6 @@ 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
@@ -109,17 +95,17 @@ RadioInterface *makeRadioInterface(struct trx_ctx *trx,
switch (type) {
case RadioDevice::NORMAL:
radio = new rif_va_wrapper<RadioInterface>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps,
trx->cfg.num_chans);
radio = new RadioInterface(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps, trx->cfg.num_chans);
break;
case RadioDevice::RESAMP_64M:
case RadioDevice::RESAMP_100M:
radio = new rif_va_wrapper<RadioInterfaceResamp>(trx->cfg.use_va, usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps);
radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps);
break;
case RadioDevice::MULTI_ARFCN:
radio = new rif_va_wrapper<RadioInterfaceMulti>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps,
trx->cfg.num_chans);
radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
trx->cfg.rx_sps, trx->cfg.num_chans);
break;
default:
LOG(ALERT) << "Unsupported radio interface configuration";
@@ -158,8 +144,12 @@ int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio)
{
VectorFIFO *fifo;
transceiver = new Transceiver(&trx->cfg, GSM::Time(3,0), radio);
if (!transceiver->init()) {
transceiver = new Transceiver(trx->cfg.base_port, trx->cfg.bind_addr,
trx->cfg.remote_addr, trx->cfg.tx_sps,
trx->cfg.rx_sps, trx->cfg.num_chans, GSM::Time(3,0),
radio, trx->cfg.rssi_offset, trx->cfg.stack_size);
if (!transceiver->init(trx->cfg.filler, trx->cfg.rtsc,
trx->cfg.rach_delay, trx->cfg.egprs, trx->cfg.ext_rach)) {
LOG(ALERT) << "Failed to initialize transceiver";
return -1;
}
@@ -191,17 +181,6 @@ 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);
@@ -267,13 +246,10 @@ static void setup_signal_handlers()
static void print_help()
{
printf( "Some useful options:\n"
" -h, --help This text\n"
" -C, --config Filename The config file to use\n"
" -V, --version Print the version of OsmoTRX\n"
"\nVTY reference generation:\n"
" --vty-ref-mode MODE VTY reference generation mode (e.g. 'expert').\n"
" --vty-ref-xml Generate the VTY reference XML output and exit.\n"
fprintf(stdout, "Options:\n"
" -h, --help This text\n"
" -C, --config Filename The config file to use\n"
" -V, --version Print the version of OsmoTRX\n"
);
}
@@ -284,44 +260,16 @@ static void print_deprecated(char opt)
<< " All cmd line options are already being overridden by VTY options if set.";
}
static void handle_long_options(const char *prog_name, const int long_option)
{
static int vty_ref_mode = VTY_REF_GEN_MODE_DEFAULT;
switch (long_option) {
case 1:
vty_ref_mode = get_string_value(vty_ref_gen_mode_names, optarg);
if (vty_ref_mode < 0) {
fprintf(stderr, "%s: Unknown VTY reference generation "
"mode '%s'\n", prog_name, optarg);
exit(2);
}
break;
case 2:
fprintf(stderr, "Generating the VTY reference in mode '%s' (%s)\n",
get_value_string(vty_ref_gen_mode_names, vty_ref_mode),
get_value_string(vty_ref_gen_mode_desc, vty_ref_mode));
vty_dump_xml_ref_mode(stdout, (enum vty_ref_gen_mode) vty_ref_mode);
exit(0);
default:
fprintf(stderr, "%s: error parsing cmdline options\n", prog_name);
exit(2);
}
}
static void handle_options(int argc, char **argv, struct trx_ctx* trx)
{
int option;
unsigned int i;
std::vector<std::string> rx_paths, tx_paths;
bool rx_paths_set = false, tx_paths_set = false;
static int long_option = 0;
static struct option long_options[] = {
{"help", 0, 0, 'h'},
{"config", 1, 0, 'C'},
{"version", 0, 0, 'V'},
{"vty-ref-mode", 1, &long_option, 1},
{"vty-ref-xml", 0, &long_option, 2},
{NULL, 0, 0, 0}
};
@@ -332,9 +280,6 @@ static void handle_options(int argc, char **argv, struct trx_ctx* trx)
print_help();
exit(0);
break;
case 0:
handle_long_options(argv[0], long_option);
break;
case 'a':
print_deprecated(option);
osmo_talloc_replace_string(trx, &trx->cfg.dev_args, optarg);
@@ -401,7 +346,6 @@ static void handle_options(int argc, char **argv, struct trx_ctx* trx)
case 'R':
print_deprecated(option);
trx->cfg.rssi_offset = atof(optarg);
trx->cfg.force_rssi_offset = true;
break;
case 'S':
print_deprecated(option);
@@ -483,12 +427,6 @@ 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;
}
@@ -509,38 +447,6 @@ 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;
@@ -562,10 +468,8 @@ 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 << " 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 << " Tuning offset........... " << trx->cfg.offset << std::endl;
ost << " RSSI to dBm offset...... " << trx->cfg.rssi_offset << std::endl;
ost << " Swap channels........... " << trx->cfg.swap_channels << std::endl;
ost << " Tx Antennas.............";
for (i = 0; i < trx->cfg.num_chans; i++) {
@@ -595,14 +499,24 @@ 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;
usrp = RadioDevice::make(iface, &trx->cfg);
type = usrp->open();
/* 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);
if (type < 0) {
LOG(ALERT) << "Failed to create radio device" << std::endl;
goto shutdown;
@@ -640,6 +554,35 @@ 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();
@@ -665,11 +608,11 @@ int main(int argc, char *argv[])
exit(2);
}
rc = telnet_init_default(tall_trx_ctx, NULL, OSMO_VTY_PORT_TRX);
rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX);
if (rc < 0)
exit(1);
g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL);
g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_TRX, NULL);
if (!g_ctrlh) {
LOG(ERROR) << "Failed to create CTRL interface.\n";
exit(1);
@@ -686,7 +629,6 @@ 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) {

Some files were not shown because too many files have changed in this diff Show More