mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
				synced 2025-11-03 21:53:18 +00:00 
			
		
		
		
	Compare commits
	
		
			86 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					9269aaa9cb | ||
| 
						 | 
					f97c53bc9c | ||
| 
						 | 
					2ada887367 | ||
| 
						 | 
					5042156437 | ||
| 
						 | 
					70ed3d586e | ||
| 
						 | 
					3dfbb6d9f6 | ||
| 
						 | 
					58294fde4b | ||
| 
						 | 
					d372eb2f0b | ||
| 
						 | 
					287ae681b7 | ||
| 
						 | 
					cca5d93f66 | ||
| 
						 | 
					a5a2275a08 | ||
| 
						 | 
					1499f0343a | ||
| 
						 | 
					8c1d59086e | ||
| 
						 | 
					f57a86131e | ||
| 
						 | 
					7d5c16590c | ||
| 
						 | 
					f8c7a52521 | ||
| 
						 | 
					c0f78a37ed | ||
| 
						 | 
					19e134a626 | ||
| 
						 | 
					a98521ac05 | ||
| 
						 | 
					df4520df77 | ||
| 
						 | 
					2f40abd8f5 | ||
| 
						 | 
					6a3e4b32f0 | ||
| 
						 | 
					c3e515a28b | ||
| 
						 | 
					bcaafcaa9d | ||
| 
						 | 
					ea7bd5fb91 | ||
| 
						 | 
					135d64b1a9 | ||
| 
						 | 
					e44cf44af4 | ||
| 
						 | 
					c0f0a6105a | ||
| 
						 | 
					da5ffd6e01 | ||
| 
						 | 
					40978041ad | ||
| 
						 | 
					3e7f4b0da9 | ||
| 
						 | 
					4080cd05ba | ||
| 
						 | 
					b3157b91bb | ||
| 
						 | 
					805e0d9c6b | ||
| 
						 | 
					4b2b98b067 | ||
| 
						 | 
					2e6c362b9c | ||
| 
						 | 
					7e47a521ef | ||
| 
						 | 
					f35515c015 | ||
| 
						 | 
					874542ca7c | ||
| 
						 | 
					f476a6755b | ||
| 
						 | 
					1ddd727bb4 | ||
| 
						 | 
					8a4362459d | ||
| 
						 | 
					5ba130c381 | ||
| 
						 | 
					fed58d97b8 | ||
| 
						 | 
					0e13bfd18c | ||
| 
						 | 
					cf1ca2e92e | ||
| 
						 | 
					20ecc4f531 | ||
| 
						 | 
					097a16e384 | ||
| 
						 | 
					c9af0b0ba0 | ||
| 
						 | 
					621a49eb69 | ||
| 
						 | 
					f538397826 | ||
| 
						 | 
					8c4336dba9 | ||
| 
						 | 
					fe1b9cef40 | ||
| 
						 | 
					5e63151f9f | ||
| 
						 | 
					10b4e31655 | ||
| 
						 | 
					0c433350da | ||
| 
						 | 
					d0c1055051 | ||
| 
						 | 
					0ce64705e0 | ||
| 
						 | 
					d0b947a0c4 | ||
| 
						 | 
					4a867be165 | ||
| 
						 | 
					fdcce1fc6e | ||
| 
						 | 
					2ca77d7ff2 | ||
| 
						 | 
					b6fd0709f4 | ||
| 
						 | 
					d3e3bba2cd | ||
| 
						 | 
					5561f1129d | ||
| 
						 | 
					b7253c6fdc | ||
| 
						 | 
					5a23a24bb1 | ||
| 
						 | 
					b60fb8e1e3 | ||
| 
						 | 
					934e1016ff | ||
| 
						 | 
					ac726b1147 | ||
| 
						 | 
					508270d83d | ||
| 
						 | 
					7d897cb5b0 | ||
| 
						 | 
					94dcf6d29c | ||
| 
						 | 
					040497e0a4 | ||
| 
						 | 
					8984d7f2ca | ||
| 
						 | 
					aa7a40ee84 | ||
| 
						 | 
					f2f35fc592 | ||
| 
						 | 
					00ddcfaf50 | ||
| 
						 | 
					2f20c564bf | ||
| 
						 | 
					19faae85c6 | ||
| 
						 | 
					1c6a3459cd | ||
| 
						 | 
					32311d8635 | ||
| 
						 | 
					71c46e91df | ||
| 
						 | 
					a39fa875a3 | ||
| 
						 | 
					9a3e3fceb8 | ||
| 
						 | 
					1a19caf002 | 
							
								
								
									
										4
									
								
								.checkpatch.conf
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								.checkpatch.conf
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,4 @@
 | 
				
			|||||||
 | 
					--exclude osmocom-bb/.*
 | 
				
			||||||
 | 
					--exclude .*h
 | 
				
			||||||
 | 
					--exclude Transceiver52M/grgsm_vitac/.*
 | 
				
			||||||
 | 
					--ignore FUNCTION_WITHOUT_ARGS
 | 
				
			||||||
@@ -25,7 +25,7 @@ AllowShortLoopsOnASingleLine: false
 | 
				
			|||||||
AlwaysBreakAfterDefinitionReturnType: None
 | 
					AlwaysBreakAfterDefinitionReturnType: None
 | 
				
			||||||
AlwaysBreakAfterReturnType: None
 | 
					AlwaysBreakAfterReturnType: None
 | 
				
			||||||
AlwaysBreakBeforeMultilineStrings: false
 | 
					AlwaysBreakBeforeMultilineStrings: false
 | 
				
			||||||
AlwaysBreakTemplateDeclarations: false
 | 
					AlwaysBreakTemplateDeclarations: true
 | 
				
			||||||
BinPackArguments: true
 | 
					BinPackArguments: true
 | 
				
			||||||
BinPackParameters: true
 | 
					BinPackParameters: true
 | 
				
			||||||
BraceWrapping:
 | 
					BraceWrapping:
 | 
				
			||||||
@@ -515,7 +515,7 @@ SpacesInContainerLiterals: false
 | 
				
			|||||||
SpacesInCStyleCastParentheses: false
 | 
					SpacesInCStyleCastParentheses: false
 | 
				
			||||||
SpacesInParentheses: false
 | 
					SpacesInParentheses: false
 | 
				
			||||||
SpacesInSquareBrackets: false
 | 
					SpacesInSquareBrackets: false
 | 
				
			||||||
Standard: Cpp03
 | 
					Standard: Cpp11
 | 
				
			||||||
TabWidth: 8
 | 
					TabWidth: 8
 | 
				
			||||||
UseTab: Always
 | 
					UseTab: Always
 | 
				
			||||||
...
 | 
					...
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										12
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							@@ -6,6 +6,15 @@ Transceiver52M/osmo-trx-uhd
 | 
				
			|||||||
Transceiver52M/osmo-trx-usrp1
 | 
					Transceiver52M/osmo-trx-usrp1
 | 
				
			||||||
Transceiver52M/osmo-trx-lms
 | 
					Transceiver52M/osmo-trx-lms
 | 
				
			||||||
Transceiver52M/osmo-trx-ipc
 | 
					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
 | 
					.clang-format
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -74,3 +83,6 @@ contrib/osmo-trx.spec
 | 
				
			|||||||
!contrib/osmo-trx.spec.in
 | 
					!contrib/osmo-trx.spec.in
 | 
				
			||||||
 | 
					
 | 
				
			||||||
utils/osmo-prbs-tool
 | 
					utils/osmo-prbs-tool
 | 
				
			||||||
 | 
					/.qtc_clangd/*
 | 
				
			||||||
 | 
					/.cache/*
 | 
				
			||||||
 | 
					/.vscode/*
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								.gitmodules
									
									
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					[submodule "osmocom-bb"]
 | 
				
			||||||
 | 
						path = osmocom-bb
 | 
				
			||||||
 | 
						url = https://gitea.osmocom.org/phone-side/osmocom-bb.git
 | 
				
			||||||
@@ -517,7 +517,7 @@ public:
 | 
				
			|||||||
		@param timeout The blocking timeout in ms.
 | 
							@param timeout The blocking timeout in ms.
 | 
				
			||||||
		@return Pointer at key or NULL on timeout.
 | 
							@return Pointer at key or NULL on timeout.
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
	D* read(const K &key, unsigned timeout) const
 | 
						D* read(const K &key, unsigned timeout)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		if (timeout==0) return readNoBlock(key);
 | 
							if (timeout==0) return readNoBlock(key);
 | 
				
			||||||
		ScopedLock lock(mLock);
 | 
							ScopedLock lock(mLock);
 | 
				
			||||||
@@ -537,7 +537,7 @@ public:
 | 
				
			|||||||
		@param key The key to read from.
 | 
							@param key The key to read from.
 | 
				
			||||||
		@return Pointer at key.
 | 
							@return Pointer at key.
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
	D* read(const K &key) const
 | 
						D* read(const K &key)
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		ScopedLock lock(mLock);
 | 
							ScopedLock lock(mLock);
 | 
				
			||||||
		typename Map::const_iterator iter = mMap.find(key);
 | 
							typename Map::const_iterator iter = mMap.find(key);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -22,8 +22,8 @@
 | 
				
			|||||||
include $(top_srcdir)/Makefile.common
 | 
					include $(top_srcdir)/Makefile.common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
 | 
					AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
 | 
				
			||||||
AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
 | 
					AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
 | 
				
			||||||
AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
 | 
					AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
noinst_LTLIBRARIES = libcommon.la
 | 
					noinst_LTLIBRARIES = libcommon.la
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -37,7 +37,12 @@ libcommon_la_SOURCES = \
 | 
				
			|||||||
	trx_rate_ctr.cpp \
 | 
						trx_rate_ctr.cpp \
 | 
				
			||||||
	trx_vty.c \
 | 
						trx_vty.c \
 | 
				
			||||||
	debug.c
 | 
						debug.c
 | 
				
			||||||
libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
 | 
					libcommon_la_LIBADD = \
 | 
				
			||||||
 | 
						$(LIBOSMOCORE_LIBS) \
 | 
				
			||||||
 | 
						$(LIBOSMOCTRL_LIBS) \
 | 
				
			||||||
 | 
						$(LIBOSMOVTY_LIBS) \
 | 
				
			||||||
 | 
						-lpthread \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
noinst_HEADERS = \
 | 
					noinst_HEADERS = \
 | 
				
			||||||
	BitVector.h \
 | 
						BitVector.h \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -43,71 +43,6 @@ using namespace std;
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Mutex gStreamLock;		///< Global lock to control access to cout and cerr.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void lockCout()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	gStreamLock.lock();
 | 
					 | 
				
			||||||
	Timeval entryTime;
 | 
					 | 
				
			||||||
	cout << entryTime << " " << osmo_gettid() << ": ";
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void unlockCout()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	cout << dec << endl << flush;
 | 
					 | 
				
			||||||
	gStreamLock.unlock();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void lockCerr()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	gStreamLock.lock();
 | 
					 | 
				
			||||||
	Timeval entryTime;
 | 
					 | 
				
			||||||
	cerr << entryTime << " " << osmo_gettid() << ": ";
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void unlockCerr()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	cerr << dec << endl << flush;
 | 
					 | 
				
			||||||
	gStreamLock.unlock();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Mutex::Mutex()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	bool res;
 | 
					 | 
				
			||||||
	res = pthread_mutexattr_init(&mAttribs);
 | 
					 | 
				
			||||||
	assert(!res);
 | 
					 | 
				
			||||||
	res = pthread_mutexattr_settype(&mAttribs,PTHREAD_MUTEX_RECURSIVE);
 | 
					 | 
				
			||||||
	assert(!res);
 | 
					 | 
				
			||||||
	res = pthread_mutex_init(&mMutex,&mAttribs);
 | 
					 | 
				
			||||||
	assert(!res);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Mutex::~Mutex()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	pthread_mutex_destroy(&mMutex);
 | 
					 | 
				
			||||||
	bool res = pthread_mutexattr_destroy(&mAttribs);
 | 
					 | 
				
			||||||
	assert(!res);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** Block for the signal up to the cancellation timeout. */
 | 
					 | 
				
			||||||
void Signal::wait(Mutex& wMutex, unsigned timeout) const
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	Timeval then(timeout);
 | 
					 | 
				
			||||||
	struct timespec waitTime = then.timespec();
 | 
					 | 
				
			||||||
	pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
void set_selfthread_name(const char *name)
 | 
					void set_selfthread_name(const char *name)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,143 +28,96 @@
 | 
				
			|||||||
#ifndef THREADS_H
 | 
					#ifndef THREADS_H
 | 
				
			||||||
#define THREADS_H
 | 
					#define THREADS_H
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "config.h"
 | 
					#include <chrono>
 | 
				
			||||||
 | 
					#include <mutex>
 | 
				
			||||||
 | 
					#include <condition_variable>
 | 
				
			||||||
#include <pthread.h>
 | 
					#include <pthread.h>
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
#include <assert.h>
 | 
					#include <cassert>
 | 
				
			||||||
#include <unistd.h>
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					#include "Timeval.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Mutex;
 | 
					class Mutex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
/**@name Multithreaded access for standard streams. */
 | 
					 | 
				
			||||||
//@{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**@name Functions for gStreamLock. */
 | 
					 | 
				
			||||||
//@{
 | 
					 | 
				
			||||||
extern Mutex gStreamLock;	///< global lock for cout and cerr
 | 
					 | 
				
			||||||
void lockCerr();		///< call prior to writing cerr
 | 
					 | 
				
			||||||
void unlockCerr();		///< call after writing cerr
 | 
					 | 
				
			||||||
void lockCout();		///< call prior to writing cout
 | 
					 | 
				
			||||||
void unlockCout();		///< call after writing cout
 | 
					 | 
				
			||||||
//@}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**@name Macros for standard messages. */
 | 
					 | 
				
			||||||
//@{
 | 
					 | 
				
			||||||
#define COUT(text) { lockCout(); std::cout << text; unlockCout(); }
 | 
					 | 
				
			||||||
#define CERR(text) { lockCerr(); std::cerr << __FILE__ << ":" << __LINE__ << ": " << text; unlockCerr(); }
 | 
					 | 
				
			||||||
#ifdef NDEBUG
 | 
					 | 
				
			||||||
#define DCOUT(text) {}
 | 
					 | 
				
			||||||
#define OBJDCOUT(text) {}
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
#define DCOUT(text) { COUT(__FILE__ << ":" << __LINE__ << " " << text); }
 | 
					 | 
				
			||||||
#define OBJDCOUT(text) { DCOUT(this << " " << text); }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
//@}
 | 
					 | 
				
			||||||
//@}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/**@defgroup C++ wrappers for pthread mechanisms. */
 | 
					/**@defgroup C++ wrappers for pthread mechanisms. */
 | 
				
			||||||
//@{
 | 
					//@{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** A class for recursive mutexes based on pthread_mutex. */
 | 
					/** A class for recursive mutexes. */
 | 
				
			||||||
class Mutex {
 | 
					class Mutex {
 | 
				
			||||||
 | 
						std::recursive_mutex m;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private:
 | 
					    public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pthread_mutex_t mMutex;
 | 
						void lock() {
 | 
				
			||||||
	pthread_mutexattr_t mAttribs;
 | 
							m.lock();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						bool trylock() {
 | 
				
			||||||
 | 
							return m.try_lock();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Mutex();
 | 
						void unlock() {
 | 
				
			||||||
 | 
							m.unlock();
 | 
				
			||||||
	~Mutex();
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	void lock() { pthread_mutex_lock(&mMutex); }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bool trylock() { return pthread_mutex_trylock(&mMutex)==0; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	void unlock() { pthread_mutex_unlock(&mMutex); }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	friend class Signal;
 | 
						friend class Signal;
 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
class ScopedLock {
 | 
					class ScopedLock {
 | 
				
			||||||
 | 
						Mutex &mMutex;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private:
 | 
					    public:
 | 
				
			||||||
	Mutex& mMutex;
 | 
						ScopedLock(Mutex &wMutex) : mMutex(wMutex) {
 | 
				
			||||||
 | 
							mMutex.lock();
 | 
				
			||||||
	public:
 | 
						}
 | 
				
			||||||
	ScopedLock(Mutex& wMutex) :mMutex(wMutex) { mMutex.lock(); }
 | 
						~ScopedLock() {
 | 
				
			||||||
	~ScopedLock() { mMutex.unlock(); }
 | 
							mMutex.unlock();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** A C++ interthread signal. */
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** A C++ interthread signal based on pthread condition variables. */
 | 
					 | 
				
			||||||
class Signal {
 | 
					class Signal {
 | 
				
			||||||
 | 
						/* any, because for some reason our mutex is recursive... */
 | 
				
			||||||
 | 
						std::condition_variable_any mSignal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private:
 | 
					    public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mutable pthread_cond_t mSignal;
 | 
						void wait(Mutex &wMutex, unsigned timeout) {
 | 
				
			||||||
 | 
							mSignal.wait_for(wMutex.m, std::chrono::milliseconds(timeout));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	public:
 | 
						void wait(Mutex &wMutex) {
 | 
				
			||||||
 | 
							mSignal.wait(wMutex.m);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Signal() { int s = pthread_cond_init(&mSignal,NULL); assert(!s); }
 | 
						void signal() {
 | 
				
			||||||
 | 
							mSignal.notify_one();
 | 
				
			||||||
	~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 set_selfthread_name(const char *name);
 | 
				
			||||||
void thread_enable_cancel(bool cancel);
 | 
					void thread_enable_cancel(bool cancel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** A C++ wrapper for pthread threads.  */
 | 
					/** A C++ wrapper for pthread threads.  */
 | 
				
			||||||
class Thread {
 | 
					class Thread {
 | 
				
			||||||
 | 
					    private:
 | 
				
			||||||
	private:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pthread_t mThread;
 | 
						pthread_t mThread;
 | 
				
			||||||
	pthread_attr_t mAttrib;
 | 
						pthread_attr_t mAttrib;
 | 
				
			||||||
	// FIXME -- Can this be reduced now?
 | 
						// FIXME -- Can this be reduced now?
 | 
				
			||||||
	size_t mStackSize;
 | 
						size_t mStackSize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
	public:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/** Create a thread in a non-running state. */
 | 
						/** Create a thread in a non-running state. */
 | 
				
			||||||
	Thread(size_t wStackSize = 0):mThread((pthread_t)0) {
 | 
						Thread(size_t wStackSize = 0) : mThread((pthread_t)0)
 | 
				
			||||||
		pthread_attr_init(&mAttrib);	// (pat) moved this here.
 | 
						{
 | 
				
			||||||
		mStackSize=wStackSize;
 | 
							pthread_attr_init(&mAttrib); // (pat) moved this here.
 | 
				
			||||||
 | 
							mStackSize = wStackSize;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
@@ -172,14 +125,17 @@ class Thread {
 | 
				
			|||||||
		It should be stopped and joined.
 | 
							It should be stopped and joined.
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
	// (pat) If the Thread is destroyed without being started, then mAttrib is undefined.  Oops.
 | 
						// (pat) If the Thread is destroyed without being started, then mAttrib is undefined.  Oops.
 | 
				
			||||||
	~Thread() { pthread_attr_destroy(&mAttrib); }
 | 
						~Thread()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							pthread_attr_destroy(&mAttrib);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Start the thread on a task. */
 | 
						/** Start the thread on a task. */
 | 
				
			||||||
	void start(void *(*task)(void*), void *arg);
 | 
						void start(void *(*task)(void *), void *arg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Join a thread that will stop on its own. */
 | 
						/** Join a thread that will stop on its own. */
 | 
				
			||||||
	void join() {
 | 
						void join()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
		if (mThread) {
 | 
							if (mThread) {
 | 
				
			||||||
			int s = pthread_join(mThread, NULL);
 | 
								int s = pthread_join(mThread, NULL);
 | 
				
			||||||
			assert(!s);
 | 
								assert(!s);
 | 
				
			||||||
@@ -187,7 +143,10 @@ class Thread {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Send cancellation to thread */
 | 
						/** Send cancellation to thread */
 | 
				
			||||||
	void cancel() { pthread_cancel(mThread); }
 | 
						void cancel()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							pthread_cancel(mThread);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef HAVE_ATOMIC_OPS
 | 
					#ifdef HAVE_ATOMIC_OPS
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -57,4 +57,15 @@ struct trx_cfg {
 | 
				
			|||||||
	unsigned int stack_size;
 | 
						unsigned int stack_size;
 | 
				
			||||||
	unsigned int num_chans;
 | 
						unsigned int num_chans;
 | 
				
			||||||
	struct trx_chan chans[TRX_CHAN_MAX];
 | 
						struct trx_chan chans[TRX_CHAN_MAX];
 | 
				
			||||||
 | 
						struct {
 | 
				
			||||||
 | 
							bool ul_freq_override;
 | 
				
			||||||
 | 
							bool dl_freq_override;
 | 
				
			||||||
 | 
							bool ul_gain_override;
 | 
				
			||||||
 | 
							bool dl_gain_override;
 | 
				
			||||||
 | 
							double ul_freq;
 | 
				
			||||||
 | 
							double dl_freq;
 | 
				
			||||||
 | 
							double ul_gain;
 | 
				
			||||||
 | 
							double dl_gain;
 | 
				
			||||||
 | 
						} overrides;
 | 
				
			||||||
 | 
						bool use_va;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -312,8 +312,7 @@ static void threshold_timer_update_intv() {
 | 
				
			|||||||
		return;
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (llist_empty(&threshold_list)) {
 | 
						if (llist_empty(&threshold_list)) {
 | 
				
			||||||
		if (osmo_timer_pending(&threshold_timer))
 | 
							osmo_timer_del(&threshold_timer);
 | 
				
			||||||
			osmo_timer_del(&threshold_timer);
 | 
					 | 
				
			||||||
		return;
 | 
							return;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -285,6 +285,79 @@ DEFUN_ATTR(cfg_ul_fn_offset, cfg_ul_fn_offset_cmd,
 | 
				
			|||||||
	return CMD_SUCCESS;
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN_ATTR(cfg_ul_freq_override, cfg_ul_freq_override_cmd,
 | 
				
			||||||
 | 
						   "ul-freq-override FLOAT",
 | 
				
			||||||
 | 
						   "Overrides Rx carrier frequency\n"
 | 
				
			||||||
 | 
						   "Frequency in Hz (e.g. 145300000)\n",
 | 
				
			||||||
 | 
						   CMD_ATTR_HIDDEN)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct trx_ctx *trx = trx_from_vty(vty);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						trx->cfg.overrides.ul_freq_override = true;
 | 
				
			||||||
 | 
						trx->cfg.overrides.ul_freq = atof(argv[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					DEFUN_ATTR(cfg_dl_freq_override, cfg_dl_freq_override_cmd,
 | 
				
			||||||
 | 
						   "dl-freq-override FLOAT",
 | 
				
			||||||
 | 
						   "Overrides Tx carrier frequency\n"
 | 
				
			||||||
 | 
						   "Frequency in Hz (e.g. 145300000)\n",
 | 
				
			||||||
 | 
						   CMD_ATTR_HIDDEN)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct trx_ctx *trx = trx_from_vty(vty);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						trx->cfg.overrides.dl_freq_override = true;
 | 
				
			||||||
 | 
						trx->cfg.overrides.dl_freq = atof(argv[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN_ATTR(cfg_ul_gain_override, cfg_ul_gain_override_cmd,
 | 
				
			||||||
 | 
						   "ul-gain-override FLOAT",
 | 
				
			||||||
 | 
						   "Overrides Rx gain\n"
 | 
				
			||||||
 | 
						   "gain in dB\n",
 | 
				
			||||||
 | 
						   CMD_ATTR_HIDDEN)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct trx_ctx *trx = trx_from_vty(vty);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						trx->cfg.overrides.ul_gain_override = true;
 | 
				
			||||||
 | 
						trx->cfg.overrides.ul_gain = atof(argv[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					DEFUN_ATTR(cfg_dl_gain_override, cfg_dl_gain_override_cmd,
 | 
				
			||||||
 | 
						   "dl-gain-override FLOAT",
 | 
				
			||||||
 | 
						   "Overrides Tx gain\n"
 | 
				
			||||||
 | 
						   "gain in dB\n",
 | 
				
			||||||
 | 
						   CMD_ATTR_HIDDEN)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct trx_ctx *trx = trx_from_vty(vty);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						trx->cfg.overrides.dl_gain_override = true;
 | 
				
			||||||
 | 
						trx->cfg.overrides.dl_gain = atof(argv[0]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DEFUN_ATTR(cfg_use_viterbi, cfg_use_viterbi_cmd,
 | 
				
			||||||
 | 
						"viterbi-eq (disable|enable)",
 | 
				
			||||||
 | 
						"Use viterbi equalizer for gmsk (default=disable)\n"
 | 
				
			||||||
 | 
						"Disable VA\n"
 | 
				
			||||||
 | 
						"Enable VA\n",
 | 
				
			||||||
 | 
						CMD_ATTR_HIDDEN)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct trx_ctx *trx = trx_from_vty(vty);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (strcmp("disable", argv[0]) == 0)
 | 
				
			||||||
 | 
							trx->cfg.use_va = false;
 | 
				
			||||||
 | 
						else if (strcmp("enable", argv[0]) == 0)
 | 
				
			||||||
 | 
							trx->cfg.use_va = true;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							return CMD_WARNING;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return CMD_SUCCESS;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
 | 
					DEFUN(cfg_swap_channels, cfg_swap_channels_cmd,
 | 
				
			||||||
	"swap-channels (disable|enable)",
 | 
						"swap-channels (disable|enable)",
 | 
				
			||||||
	"Swap primary and secondary channels of the PHY (if any)\n"
 | 
						"Swap primary and secondary channels of the PHY (if any)\n"
 | 
				
			||||||
@@ -466,7 +539,6 @@ DEFUN_ATTR(cfg_ctr_error_threshold, cfg_ctr_error_threshold_cmd,
 | 
				
			|||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
	struct ctr_threshold ctr;
 | 
						struct ctr_threshold ctr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct trx_ctx *trx = trx_from_vty(vty);
 | 
					 | 
				
			||||||
	rc = vty_ctr_name_2_id(argv[0]);
 | 
						rc = vty_ctr_name_2_id(argv[0]);
 | 
				
			||||||
	if (rc < 0) {
 | 
						if (rc < 0) {
 | 
				
			||||||
		vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
 | 
							vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
 | 
				
			||||||
@@ -498,7 +570,6 @@ DEFUN_ATTR(cfg_no_ctr_error_threshold, cfg_no_ctr_error_threshold_cmd,
 | 
				
			|||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
	struct ctr_threshold ctr;
 | 
						struct ctr_threshold ctr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct trx_ctx *trx = trx_from_vty(vty);
 | 
					 | 
				
			||||||
	rc = vty_ctr_name_2_id(argv[0]);
 | 
						rc = vty_ctr_name_2_id(argv[0]);
 | 
				
			||||||
	if (rc < 0) {
 | 
						if (rc < 0) {
 | 
				
			||||||
		vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
 | 
							vty_out(vty, "No valid ctr_name found for ctr-error-threshold %s%s",
 | 
				
			||||||
@@ -609,7 +680,7 @@ static int config_write_trx(struct vty *vty)
 | 
				
			|||||||
		vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
 | 
							vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE);
 | 
				
			||||||
	if (trx->cfg.base_port != DEFAULT_TRX_PORT)
 | 
						if (trx->cfg.base_port != DEFAULT_TRX_PORT)
 | 
				
			||||||
		vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE);
 | 
							vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE);
 | 
				
			||||||
	if (trx->cfg.dev_args)
 | 
						if (strlen(trx->cfg.dev_args))
 | 
				
			||||||
		vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE);
 | 
							vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE);
 | 
				
			||||||
	if (trx->cfg.tx_sps != DEFAULT_TX_SPS)
 | 
						if (trx->cfg.tx_sps != DEFAULT_TX_SPS)
 | 
				
			||||||
		vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
 | 
							vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE);
 | 
				
			||||||
@@ -640,6 +711,16 @@ static int config_write_trx(struct vty *vty)
 | 
				
			|||||||
		vty_out(vty, " stack-size %u%s", trx->cfg.stack_size, VTY_NEWLINE);
 | 
							vty_out(vty, " stack-size %u%s", trx->cfg.stack_size, VTY_NEWLINE);
 | 
				
			||||||
	if (trx->cfg.ul_fn_offset != 0)
 | 
						if (trx->cfg.ul_fn_offset != 0)
 | 
				
			||||||
		vty_out(vty, " ul-fn-offset %d%s", trx->cfg.ul_fn_offset, VTY_NEWLINE);
 | 
							vty_out(vty, " ul-fn-offset %d%s", trx->cfg.ul_fn_offset, VTY_NEWLINE);
 | 
				
			||||||
 | 
						if (trx->cfg.overrides.dl_freq_override)
 | 
				
			||||||
 | 
							vty_out(vty, " dl-freq-override %f%s", trx->cfg.overrides.dl_freq, VTY_NEWLINE);
 | 
				
			||||||
 | 
						if (trx->cfg.overrides.ul_freq_override)
 | 
				
			||||||
 | 
							vty_out(vty, " ul-freq-override %f%s", trx->cfg.overrides.ul_freq, VTY_NEWLINE);
 | 
				
			||||||
 | 
						if (trx->cfg.overrides.dl_gain_override)
 | 
				
			||||||
 | 
							vty_out(vty, " dl-gain-override %f%s", trx->cfg.overrides.dl_gain, VTY_NEWLINE);
 | 
				
			||||||
 | 
						if (trx->cfg.overrides.ul_gain_override)
 | 
				
			||||||
 | 
							vty_out(vty, " ul-gain-override %f%s", trx->cfg.overrides.ul_gain, VTY_NEWLINE);
 | 
				
			||||||
 | 
						if (trx->cfg.use_va)
 | 
				
			||||||
 | 
							vty_out(vty, " viterbi-eq %s%s", trx->cfg.use_va ? "enable" : "disable", VTY_NEWLINE);
 | 
				
			||||||
	trx_rate_ctr_threshold_write_config(vty, " ");
 | 
						trx_rate_ctr_threshold_write_config(vty, " ");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (i = 0; i < trx->cfg.num_chans; i++) {
 | 
						for (i = 0; i < trx->cfg.num_chans; i++) {
 | 
				
			||||||
@@ -762,6 +843,7 @@ struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx)
 | 
				
			|||||||
	trx->cfg.rx_sps = DEFAULT_RX_SPS;
 | 
						trx->cfg.rx_sps = DEFAULT_RX_SPS;
 | 
				
			||||||
	trx->cfg.filler = FILLER_ZERO;
 | 
						trx->cfg.filler = FILLER_ZERO;
 | 
				
			||||||
	trx->cfg.rssi_offset = 0.0f;
 | 
						trx->cfg.rssi_offset = 0.0f;
 | 
				
			||||||
 | 
						trx->cfg.dev_args = talloc_strdup(trx, "");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return trx;
 | 
						return trx;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -804,6 +886,11 @@ int trx_vty_init(struct trx_ctx* trx)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	install_element(TRX_NODE, &cfg_chan_cmd);
 | 
						install_element(TRX_NODE, &cfg_chan_cmd);
 | 
				
			||||||
	install_element(TRX_NODE, &cfg_ul_fn_offset_cmd);
 | 
						install_element(TRX_NODE, &cfg_ul_fn_offset_cmd);
 | 
				
			||||||
 | 
						install_element(TRX_NODE, &cfg_ul_freq_override_cmd);
 | 
				
			||||||
 | 
						install_element(TRX_NODE, &cfg_dl_freq_override_cmd);
 | 
				
			||||||
 | 
						install_element(TRX_NODE, &cfg_ul_gain_override_cmd);
 | 
				
			||||||
 | 
						install_element(TRX_NODE, &cfg_dl_gain_override_cmd);
 | 
				
			||||||
 | 
						install_element(TRX_NODE, &cfg_use_viterbi_cmd);
 | 
				
			||||||
	install_node(&chan_node, dummy_config_write);
 | 
						install_node(&chan_node, dummy_config_write);
 | 
				
			||||||
	install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
 | 
						install_element(CHAN_NODE, &cfg_chan_rx_path_cmd);
 | 
				
			||||||
	install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
 | 
						install_element(CHAN_NODE, &cfg_chan_tx_path_cmd);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -26,8 +26,14 @@ AM_CXXFLAGS = -Wall -pthread
 | 
				
			|||||||
#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread
 | 
					#AM_CXXFLAGS = -Wall -O2 -NDEBUG -pthread
 | 
				
			||||||
#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread
 | 
					#AM_CFLAGS = -Wall -O2 -NDEBUG -pthread
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					SUBDIRS =
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if ENABLE_MS_TRX
 | 
				
			||||||
 | 
					SUBDIRS += $(LIBTRXCON_DIR)
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Order must be preserved
 | 
					# Order must be preserved
 | 
				
			||||||
SUBDIRS = \
 | 
					SUBDIRS += \
 | 
				
			||||||
	CommonLibs \
 | 
						CommonLibs \
 | 
				
			||||||
	GSM \
 | 
						GSM \
 | 
				
			||||||
	Transceiver52M \
 | 
						Transceiver52M \
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -27,9 +27,9 @@ GIT Repository
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
You can clone from the official osmo-trx.git repository using
 | 
					You can clone from the official osmo-trx.git repository using
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        git clone git://git.osmocom.org/osmo-trx.git
 | 
					        git clone https://gitea.osmocom.org/cellular-infrastructure/osmo-trx`
 | 
				
			||||||
 | 
					
 | 
				
			||||||
There is a cgit interface at <https://git.osmocom.org/osmo-trx/>
 | 
					There is a web interface at <https://gitea.osmocom.org/cellular-infrastructure/osmo-trx>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Documentation
 | 
					Documentation
 | 
				
			||||||
-------------
 | 
					-------------
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,7 +29,7 @@ unlike the built-in complex<> templates, these inline most operations for speed
 | 
				
			|||||||
template<class Real> class Complex {
 | 
					template<class Real> class Complex {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
 | 
					  typedef Real value_type;
 | 
				
			||||||
  Real r, i;
 | 
					  Real r, i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**@name constructors */
 | 
					  /**@name constructors */
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -40,7 +40,9 @@ COMMON_SOURCES = \
 | 
				
			|||||||
	ChannelizerBase.cpp \
 | 
						ChannelizerBase.cpp \
 | 
				
			||||||
	Channelizer.cpp \
 | 
						Channelizer.cpp \
 | 
				
			||||||
	Synthesis.cpp \
 | 
						Synthesis.cpp \
 | 
				
			||||||
	proto_trxd.c
 | 
						proto_trxd.c \
 | 
				
			||||||
 | 
						grgsm_vitac/grgsm_vitac.cpp \
 | 
				
			||||||
 | 
						grgsm_vitac/viterbi_detector.cc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
libtransceiver_common_la_SOURCES = \
 | 
					libtransceiver_common_la_SOURCES = \
 | 
				
			||||||
	$(COMMON_SOURCES) \
 | 
						$(COMMON_SOURCES) \
 | 
				
			||||||
@@ -73,6 +75,45 @@ COMMON_LDADD = \
 | 
				
			|||||||
	$(LIBOSMOCTRL_LIBS) \
 | 
						$(LIBOSMOCTRL_LIBS) \
 | 
				
			||||||
	$(LIBOSMOVTY_LIBS)
 | 
						$(LIBOSMOVTY_LIBS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if ENABLE_MS_TRX
 | 
				
			||||||
 | 
					AM_CPPFLAGS += -I$(top_srcdir)/osmocom-bb/src/host/trxcon/include/
 | 
				
			||||||
 | 
					AM_CPPFLAGS += -I${srcdir}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TRXCON_LDADD = \
 | 
				
			||||||
 | 
						$(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 =
 | 
					bin_PROGRAMS =
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if DEVICE_UHD
 | 
					if DEVICE_UHD
 | 
				
			||||||
@@ -83,6 +124,17 @@ osmo_trx_uhd_LDADD = \
 | 
				
			|||||||
	$(COMMON_LDADD) \
 | 
						$(COMMON_LDADD) \
 | 
				
			||||||
	$(UHD_LIBS)
 | 
						$(UHD_LIBS)
 | 
				
			||||||
osmo_trx_uhd_CPPFLAGS  = $(AM_CPPFLAGS) $(UHD_CFLAGS)
 | 
					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
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if DEVICE_USRP1
 | 
					if DEVICE_USRP1
 | 
				
			||||||
@@ -105,6 +157,27 @@ osmo_trx_lms_LDADD = \
 | 
				
			|||||||
osmo_trx_lms_CPPFLAGS  = $(AM_CPPFLAGS) $(LMS_CFLAGS)
 | 
					osmo_trx_lms_CPPFLAGS  = $(AM_CPPFLAGS) $(LMS_CFLAGS)
 | 
				
			||||||
endif
 | 
					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
 | 
					if DEVICE_IPC
 | 
				
			||||||
bin_PROGRAMS += osmo-trx-ipc
 | 
					bin_PROGRAMS += osmo-trx-ipc
 | 
				
			||||||
osmo_trx_ipc_SOURCES = osmo-trx.cpp
 | 
					osmo_trx_ipc_SOURCES = osmo-trx.cpp
 | 
				
			||||||
@@ -113,4 +186,3 @@ osmo_trx_ipc_LDADD = \
 | 
				
			|||||||
	$(COMMON_LDADD)
 | 
						$(COMMON_LDADD)
 | 
				
			||||||
osmo_trx_ipc_CPPFLAGS  = $(AM_CPPFLAGS)
 | 
					osmo_trx_ipc_CPPFLAGS  = $(AM_CPPFLAGS)
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -29,6 +29,7 @@
 | 
				
			|||||||
#include <fstream>
 | 
					#include <fstream>
 | 
				
			||||||
#include "Transceiver.h"
 | 
					#include "Transceiver.h"
 | 
				
			||||||
#include <Logger.h>
 | 
					#include <Logger.h>
 | 
				
			||||||
 | 
					#include <grgsm_vitac/grgsm_vitac.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extern "C" {
 | 
					extern "C" {
 | 
				
			||||||
#include "osmo_signal.h"
 | 
					#include "osmo_signal.h"
 | 
				
			||||||
@@ -135,12 +136,13 @@ bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t r
 | 
				
			|||||||
Transceiver::Transceiver(const struct trx_cfg *cfg,
 | 
					Transceiver::Transceiver(const struct trx_cfg *cfg,
 | 
				
			||||||
                         GSM::Time wTransmitLatency,
 | 
					                         GSM::Time wTransmitLatency,
 | 
				
			||||||
                         RadioInterface *wRadioInterface)
 | 
					                         RadioInterface *wRadioInterface)
 | 
				
			||||||
  : cfg(cfg), mClockSocket(-1),
 | 
					  : mChans(cfg->num_chans), cfg(cfg),
 | 
				
			||||||
    mRxLowerLoopThread(nullptr), mTxLowerLoopThread(nullptr),
 | 
					    mCtrlSockets(mChans), mClockSocket(-1),
 | 
				
			||||||
    mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
 | 
					    mTxPriorityQueues(mChans), mReceiveFIFO(mChans),
 | 
				
			||||||
    mChans(cfg->num_chans), mOn(false), mForceClockInterface(false),
 | 
					    mRxServiceLoopThreads(mChans), mRxLowerLoopThread(nullptr), mTxLowerLoopThread(nullptr),
 | 
				
			||||||
    mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0),
 | 
					    mTxPriorityQueueServiceLoopThreads(mChans), mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
 | 
				
			||||||
    mMaxExpectedDelayNB(0), mWriteBurstToDiskMask(0)
 | 
					    mOn(false),mForceClockInterface(false), mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0),
 | 
				
			||||||
 | 
					    mMaxExpectedDelayNB(0), mWriteBurstToDiskMask(0), mVersionTRXD(mChans), mStates(mChans)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  txFullScale = mRadioInterface->fullScaleInputValue();
 | 
					  txFullScale = mRadioInterface->fullScaleInputValue();
 | 
				
			||||||
  rxFullScale = mRadioInterface->fullScaleOutputValue();
 | 
					  rxFullScale = mRadioInterface->fullScaleOutputValue();
 | 
				
			||||||
@@ -207,15 +209,10 @@ bool Transceiver::init()
 | 
				
			|||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mDataSockets.resize(mChans, -1);
 | 
					  initvita();
 | 
				
			||||||
  mCtrlSockets.resize(mChans);
 | 
					
 | 
				
			||||||
  mTxPriorityQueueServiceLoopThreads.resize(mChans);
 | 
					  mDataSockets.resize(mChans, -1);
 | 
				
			||||||
  mRxServiceLoopThreads.resize(mChans);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mTxPriorityQueues.resize(mChans);
 | 
					 | 
				
			||||||
  mReceiveFIFO.resize(mChans);
 | 
					 | 
				
			||||||
  mStates.resize(mChans);
 | 
					 | 
				
			||||||
  mVersionTRXD.resize(mChans);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Filler table retransmissions - support only on channel 0 */
 | 
					  /* Filler table retransmissions - support only on channel 0 */
 | 
				
			||||||
  if (cfg->filler == FILLER_DUMMY)
 | 
					  if (cfg->filler == FILLER_DUMMY)
 | 
				
			||||||
@@ -584,7 +581,7 @@ CorrType Transceiver::expectedCorrType(GSM::Time currTime,
 | 
				
			|||||||
  case XIII: {
 | 
					  case XIII: {
 | 
				
			||||||
    int mod52 = burstFN % 52;
 | 
					    int mod52 = burstFN % 52;
 | 
				
			||||||
    if ((mod52 == 12) || (mod52 == 38))
 | 
					    if ((mod52 == 12) || (mod52 == 38))
 | 
				
			||||||
      return cfg->ext_rach ? EXT_RACH : RACH;
 | 
					      return RACH; /* RACH is always 8-bit on PTCCH/U */
 | 
				
			||||||
    else if ((mod52 == 25) || (mod52 == 51))
 | 
					    else if ((mod52 == 25) || (mod52 == 51))
 | 
				
			||||||
      return IDLE;
 | 
					      return IDLE;
 | 
				
			||||||
    else /* Enable 8-PSK burst detection if EDGE is enabled */
 | 
					    else /* Enable 8-PSK burst detection if EDGE is enabled */
 | 
				
			||||||
@@ -620,6 +617,44 @@ double Transceiver::rssiOffset(size_t chan)
 | 
				
			|||||||
  return mRadioInterface->rssiOffset(chan) + cfg->rssi_offset;
 | 
					  return mRadioInterface->rssiOffset(chan) + cfg->rssi_offset;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static SoftVector *demodAnyBurst_va(const signalVector &burst, CorrType type, int sps, int rach_max_toa, int tsc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						auto conved_beg = reinterpret_cast<const std::complex<float> *>(&burst.begin()[0]);
 | 
				
			||||||
 | 
						std::complex<float> chan_imp_resp[CHAN_IMP_RESP_LENGTH * d_OSR];
 | 
				
			||||||
 | 
						float ncmax;
 | 
				
			||||||
 | 
						const unsigned burst_len_bits = 148 + 8;
 | 
				
			||||||
 | 
						char demodded_softbits[burst_len_bits];
 | 
				
			||||||
 | 
						SoftVector *bits = new SoftVector(burst_len_bits);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (type == CorrType::TSC) {
 | 
				
			||||||
 | 
							auto rach_burst_start = get_norm_chan_imp_resp(conved_beg, chan_imp_resp, &ncmax, tsc);
 | 
				
			||||||
 | 
							rach_burst_start = std::max(rach_burst_start, 0);
 | 
				
			||||||
 | 
							detect_burst_nb(conved_beg, chan_imp_resp, rach_burst_start, demodded_softbits);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							auto normal_burst_start = get_access_imp_resp(conved_beg, chan_imp_resp, &ncmax, 0);
 | 
				
			||||||
 | 
							normal_burst_start = std::max(normal_burst_start, 0);
 | 
				
			||||||
 | 
							detect_burst_ab(conved_beg, chan_imp_resp, normal_burst_start, demodded_softbits, rach_max_toa);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float *s = &bits->begin()[0];
 | 
				
			||||||
 | 
						for (unsigned int i = 0; i < 148; i++)
 | 
				
			||||||
 | 
							s[i] = demodded_softbits[i] * -1;
 | 
				
			||||||
 | 
						for (unsigned int i = 148; i < burst_len_bits; i++)
 | 
				
			||||||
 | 
							s[i] = 0;
 | 
				
			||||||
 | 
						return bits;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define USE_VA
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USE_VA
 | 
				
			||||||
 | 
					// signalvector is owning despite claiming not to, but we can pretend, too..
 | 
				
			||||||
 | 
					static void dummy_free(void *wData){};
 | 
				
			||||||
 | 
					static void *dummy_alloc(size_t newSize)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
 * Pull bursts from the FIFO and handle according to the slot
 | 
					 * Pull bursts from the FIFO and handle according to the slot
 | 
				
			||||||
 * and burst correlation type. Equalzation is currently disabled.
 | 
					 * and burst correlation type. Equalzation is currently disabled.
 | 
				
			||||||
@@ -640,6 +675,9 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
 | 
				
			|||||||
  TransceiverState *state = &mStates[chan];
 | 
					  TransceiverState *state = &mStates[chan];
 | 
				
			||||||
  bool ctr_changed = false;
 | 
					  bool ctr_changed = false;
 | 
				
			||||||
  double rssi_offset;
 | 
					  double rssi_offset;
 | 
				
			||||||
 | 
					  static complex burst_shift_buffer[625];
 | 
				
			||||||
 | 
					  static signalVector shift_vec(burst_shift_buffer, 0, 625, dummy_alloc, dummy_free);
 | 
				
			||||||
 | 
					  signalVector *shvec_ptr = &shift_vec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Blocking FIFO read */
 | 
					  /* Blocking FIFO read */
 | 
				
			||||||
  radioVector *radio_burst = mReceiveFIFO[chan]->read();
 | 
					  radioVector *radio_burst = mReceiveFIFO[chan]->read();
 | 
				
			||||||
@@ -719,8 +757,15 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
 | 
				
			|||||||
  max_toa = (type == RACH || type == EXT_RACH) ?
 | 
					  max_toa = (type == RACH || type == EXT_RACH) ?
 | 
				
			||||||
            mMaxExpectedDelayAB : mMaxExpectedDelayNB;
 | 
					            mMaxExpectedDelayAB : mMaxExpectedDelayNB;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (cfg->use_va) {
 | 
				
			||||||
 | 
					    // shifted burst copy to make the old demod and detection happy
 | 
				
			||||||
 | 
					    std::copy(burst->begin() + 20, burst->end() - 20, shift_vec.begin());
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    shvec_ptr = burst;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Detect normal or RACH bursts */
 | 
					  /* Detect normal or RACH bursts */
 | 
				
			||||||
  rc = detectAnyBurst(*burst, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
 | 
					  rc = detectAnyBurst(*shvec_ptr, mTSC, BURST_THRESH, cfg->rx_sps, type, max_toa, &ebp);
 | 
				
			||||||
  if (rc <= 0) {
 | 
					  if (rc <= 0) {
 | 
				
			||||||
    if (rc == -SIGERR_CLIP) {
 | 
					    if (rc == -SIGERR_CLIP) {
 | 
				
			||||||
      LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst";
 | 
					      LOGCHAN(chan, DTRXDUL, INFO) << "Clipping detected on received RACH or Normal Burst";
 | 
				
			||||||
@@ -734,7 +779,13 @@ int Transceiver::pullRadioVector(size_t chan, struct trx_ul_burst_ind *bi)
 | 
				
			|||||||
    goto ret_idle;
 | 
					    goto ret_idle;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  rxBurst = demodAnyBurst(*burst, (CorrType) rc, cfg->rx_sps, &ebp);
 | 
					  if (cfg->use_va) {
 | 
				
			||||||
 | 
					    scaleVector(*burst, { (1. / (float)((1 << 14) - 1)), 0 });
 | 
				
			||||||
 | 
					    rxBurst = demodAnyBurst_va(*burst, (CorrType)rc, cfg->rx_sps, max_toa, mTSC);
 | 
				
			||||||
 | 
					  } else {
 | 
				
			||||||
 | 
					    rxBurst = demodAnyBurst(*shvec_ptr, (CorrType)rc, cfg->rx_sps, &ebp);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bi->toa = ebp.toa;
 | 
					  bi->toa = ebp.toa;
 | 
				
			||||||
  bi->tsc = ebp.tsc;
 | 
					  bi->tsc = ebp.tsc;
 | 
				
			||||||
  bi->ci = ebp.ci;
 | 
					  bi->ci = ebp.ci;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -80,7 +80,7 @@ struct TransceiverState {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  /* Received noise energy levels */
 | 
					  /* Received noise energy levels */
 | 
				
			||||||
  float mNoiseLev;
 | 
					  float mNoiseLev;
 | 
				
			||||||
  noiseVector mNoises;
 | 
					  avgVector mNoises;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /* Shadowed downlink attenuation */
 | 
					  /* Shadowed downlink attenuation */
 | 
				
			||||||
  int mPower;
 | 
					  int mPower;
 | 
				
			||||||
@@ -148,6 +148,7 @@ public:
 | 
				
			|||||||
  } ChannelCombination;
 | 
					  } ChannelCombination;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					  size_t mChans;
 | 
				
			||||||
struct ctrl_msg {
 | 
					struct ctrl_msg {
 | 
				
			||||||
  char data[101];
 | 
					  char data[101];
 | 
				
			||||||
  ctrl_msg() {};
 | 
					  ctrl_msg() {};
 | 
				
			||||||
@@ -161,9 +162,9 @@ struct ctrl_sock_state {
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  ~ctrl_sock_state() {
 | 
					  ~ctrl_sock_state() {
 | 
				
			||||||
      if(conn_bfd.fd >= 0) {
 | 
					      if(conn_bfd.fd >= 0) {
 | 
				
			||||||
 | 
					          osmo_fd_unregister(&conn_bfd);
 | 
				
			||||||
          close(conn_bfd.fd);
 | 
					          close(conn_bfd.fd);
 | 
				
			||||||
          conn_bfd.fd = -1;
 | 
					          conn_bfd.fd = -1;
 | 
				
			||||||
          osmo_fd_unregister(&conn_bfd);
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
@@ -218,7 +219,7 @@ struct ctrl_sock_state {
 | 
				
			|||||||
  /** drive handling of control messages from GSM core */
 | 
					  /** drive handling of control messages from GSM core */
 | 
				
			||||||
  int ctrl_sock_handle_rx(int chan);
 | 
					  int ctrl_sock_handle_rx(int chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  size_t mChans;
 | 
					
 | 
				
			||||||
  bool mOn;	                           ///< flag to indicate that transceiver is powered on
 | 
					  bool mOn;	                           ///< flag to indicate that transceiver is powered on
 | 
				
			||||||
  bool mForceClockInterface;           ///< flag to indicate whether IND CLOCK shall be sent unconditionally after transceiver is started
 | 
					  bool mForceClockInterface;           ///< flag to indicate whether IND CLOCK shall be sent unconditionally after transceiver is started
 | 
				
			||||||
  bool mHandover[8][8];                ///< expect handover to the timeslot/subslot
 | 
					  bool mHandover[8][8];                ///< expect handover to the timeslot/subslot
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -17,3 +17,7 @@ endif
 | 
				
			|||||||
if DEVICE_LMS
 | 
					if DEVICE_LMS
 | 
				
			||||||
SUBDIRS += lms
 | 
					SUBDIRS += lms
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					if DEVICE_BLADE
 | 
				
			||||||
 | 
					SUBDIRS += bladerf
 | 
				
			||||||
 | 
					endif
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										11
									
								
								Transceiver52M/device/bladerf/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								Transceiver52M/device/bladerf/Makefile.am
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
				
			|||||||
 | 
					include $(top_srcdir)/Makefile.common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
 | 
				
			||||||
 | 
					AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(BLADE_CFLAGS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					noinst_HEADERS = bladerf.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					noinst_LTLIBRARIES = libdevice.la
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					libdevice_la_SOURCES = bladerf.cpp
 | 
				
			||||||
 | 
					libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
 | 
				
			||||||
							
								
								
									
										611
									
								
								Transceiver52M/device/bladerf/bladerf.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										611
									
								
								Transceiver52M/device/bladerf/bladerf.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,611 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2022 sysmocom - s.f.m.c. GmbH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Eric Wild <ewild@sysmocom.de>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * SPDX-License-Identifier: AGPL-3.0+
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 * See the COPYING file in the main directory for details.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					#include <libbladeRF.h>
 | 
				
			||||||
 | 
					#include "radioDevice.h"
 | 
				
			||||||
 | 
					#include "bladerf.h"
 | 
				
			||||||
 | 
					#include "Threads.h"
 | 
				
			||||||
 | 
					#include "Logger.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef HAVE_CONFIG_H
 | 
				
			||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#include <osmocom/core/utils.h>
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsm_utils.h>
 | 
				
			||||||
 | 
					#include <osmocom/vty/cpu_sched_vty.h>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SAMPLE_BUF_SZ (1 << 20)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define B2XX_TIMING_4_4SPS 6.18462e-5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CHKRET()                                                                                                       \
 | 
				
			||||||
 | 
						{                                                                                                              \
 | 
				
			||||||
 | 
							if (status != 0)                                                                                       \
 | 
				
			||||||
 | 
								LOGC(DDEV, ERROR) << bladerf_strerror(status);                                                 \
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const dev_map_t dev_param_map{
 | 
				
			||||||
 | 
						{ std::make_tuple(blade_dev_type::BLADE2, 4, 4), { 1, 26e6, GSMRATE, B2XX_TIMING_4_4SPS, "B200 4 SPS" } },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const power_map_t dev_band_nom_power_param_map{
 | 
				
			||||||
 | 
						{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_850), { 89.75, 13.3, -7.5 } },
 | 
				
			||||||
 | 
						{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_900), { 89.75, 13.3, -7.5 } },
 | 
				
			||||||
 | 
						{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_1800), { 89.75, 7.5, -11.0 } },
 | 
				
			||||||
 | 
						{ std::make_tuple(blade_dev_type::BLADE2, GSM_BAND_1900), { 89.75, 7.7, -11.0 } },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* So far measurements done for B210 show really close to linear relationship
 | 
				
			||||||
 | 
					 * between gain and real output power, so we simply adjust the measured offset
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static double TxGain2TxPower(const dev_band_desc &desc, double tx_gain_db)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return desc.nom_out_tx_power - (desc.nom_uhd_tx_gain - tx_gain_db);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					blade_device::blade_device(InterfaceType iface, const struct trx_cfg *cfg)
 | 
				
			||||||
 | 
						: RadioDevice(iface, cfg), band_manager(dev_band_nom_power_param_map, dev_param_map), dev(nullptr),
 | 
				
			||||||
 | 
						  rx_gain_min(0.0), rx_gain_max(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), drop_cnt(0),
 | 
				
			||||||
 | 
						  prev_ts(0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					blade_device::~blade_device()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (dev) {
 | 
				
			||||||
 | 
							bladerf_enable_module(dev, BLADERF_CHANNEL_RX(0), false);
 | 
				
			||||||
 | 
							bladerf_enable_module(dev, BLADERF_CHANNEL_TX(0), false);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (size_t i = 0; i < rx_buffers.size(); i++)
 | 
				
			||||||
 | 
							delete rx_buffers[i];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void blade_device::init_gains()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						double tx_gain_min, tx_gain_max;
 | 
				
			||||||
 | 
						int status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const struct bladerf_range *r;
 | 
				
			||||||
 | 
						bladerf_get_gain_range(dev, BLADERF_RX, &r);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rx_gain_min = r->min;
 | 
				
			||||||
 | 
						rx_gain_max = r->max;
 | 
				
			||||||
 | 
						LOGC(DDEV, INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (size_t i = 0; i < rx_gains.size(); i++) {
 | 
				
			||||||
 | 
							double gain = (rx_gain_min + rx_gain_max) / 2;
 | 
				
			||||||
 | 
							status = bladerf_set_gain_mode(dev, BLADERF_CHANNEL_RX(i), BLADERF_GAIN_MGC);
 | 
				
			||||||
 | 
							CHKRET()
 | 
				
			||||||
 | 
							bladerf_gain_mode m;
 | 
				
			||||||
 | 
							bladerf_get_gain_mode(dev, BLADERF_CHANNEL_RX(i), &m);
 | 
				
			||||||
 | 
							LOGC(DDEV, INFO) << (m == BLADERF_GAIN_MANUAL ? "gain manual" : "gain AUTO");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0);
 | 
				
			||||||
 | 
							CHKRET()
 | 
				
			||||||
 | 
							int actual_gain;
 | 
				
			||||||
 | 
							status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain);
 | 
				
			||||||
 | 
							CHKRET()
 | 
				
			||||||
 | 
							LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain << " scale "
 | 
				
			||||||
 | 
									 << r->scale << " actual " << actual_gain;
 | 
				
			||||||
 | 
							rx_gains[i] = actual_gain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							status = bladerf_set_gain(dev, BLADERF_CHANNEL_RX(i), 0);
 | 
				
			||||||
 | 
							CHKRET()
 | 
				
			||||||
 | 
							status = bladerf_get_gain(dev, BLADERF_CHANNEL_RX(i), &actual_gain);
 | 
				
			||||||
 | 
							CHKRET()
 | 
				
			||||||
 | 
							LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain << " scale "
 | 
				
			||||||
 | 
									 << r->scale << " actual " << actual_gain;
 | 
				
			||||||
 | 
							rx_gains[i] = actual_gain;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						status = bladerf_get_gain_range(dev, BLADERF_TX, &r);
 | 
				
			||||||
 | 
						CHKRET()
 | 
				
			||||||
 | 
						tx_gain_min = r->min;
 | 
				
			||||||
 | 
						tx_gain_max = r->max;
 | 
				
			||||||
 | 
						LOGC(DDEV, INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (size_t i = 0; i < tx_gains.size(); i++) {
 | 
				
			||||||
 | 
							double gain = (tx_gain_min + tx_gain_max) / 2;
 | 
				
			||||||
 | 
							status = bladerf_set_gain(dev, BLADERF_CHANNEL_TX(i), 30);
 | 
				
			||||||
 | 
							CHKRET()
 | 
				
			||||||
 | 
							int actual_gain;
 | 
				
			||||||
 | 
							status = bladerf_get_gain(dev, BLADERF_CHANNEL_TX(i), &actual_gain);
 | 
				
			||||||
 | 
							CHKRET()
 | 
				
			||||||
 | 
							LOGC(DDEV, INFO) << "Default setting Tx gain for channel " << i << " to " << gain << " scale "
 | 
				
			||||||
 | 
									 << r->scale << " actual " << actual_gain;
 | 
				
			||||||
 | 
							tx_gains[i] = actual_gain;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void blade_device::set_rates()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct bladerf_rational_rate rate = { 0, static_cast<uint64_t>((1625e3 * 4)), 6 }, actual;
 | 
				
			||||||
 | 
						auto status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_RX(0), &rate, &actual);
 | 
				
			||||||
 | 
						CHKRET()
 | 
				
			||||||
 | 
						status = bladerf_set_rational_sample_rate(dev, BLADERF_CHANNEL_TX(0), &rate, &actual);
 | 
				
			||||||
 | 
						CHKRET()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tx_rate = rx_rate = (double)rate.num / (double)rate.den;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGC(DDEV, INFO) << "Rates set to" << tx_rate << " / " << rx_rate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bladerf_set_bandwidth(dev, BLADERF_CHANNEL_RX(0), (bladerf_bandwidth)2e6, (bladerf_bandwidth *)NULL);
 | 
				
			||||||
 | 
						bladerf_set_bandwidth(dev, BLADERF_CHANNEL_TX(0), (bladerf_bandwidth)2e6, (bladerf_bandwidth *)NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ts_offset = 60; // FIXME: actual blade offset, should equal b2xx
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double blade_device::setRxGain(double db, size_t chan)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (chan >= rx_gains.size()) {
 | 
				
			||||||
 | 
							LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
				
			||||||
 | 
							return 0.0f;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bladerf_set_gain(dev, BLADERF_CHANNEL_RX(chan), 30); //db);
 | 
				
			||||||
 | 
						int actual_gain;
 | 
				
			||||||
 | 
						bladerf_get_gain(dev, BLADERF_CHANNEL_RX(chan), &actual_gain);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rx_gains[chan] = actual_gain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGC(DDEV, INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rx_gains[chan];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double blade_device::getRxGain(size_t chan)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (chan >= rx_gains.size()) {
 | 
				
			||||||
 | 
							LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
				
			||||||
 | 
							return 0.0f;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rx_gains[chan];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double blade_device::rssiOffset(size_t chan)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						double rssiOffset;
 | 
				
			||||||
 | 
						dev_band_desc desc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (chan >= rx_gains.size()) {
 | 
				
			||||||
 | 
							LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
				
			||||||
 | 
							return 0.0f;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						get_dev_band_desc(desc);
 | 
				
			||||||
 | 
						rssiOffset = rx_gains[chan] + desc.rxgain2rssioffset_rel;
 | 
				
			||||||
 | 
						return rssiOffset;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double blade_device::setPowerAttenuation(int atten, size_t chan)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						double tx_power, db;
 | 
				
			||||||
 | 
						dev_band_desc desc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (chan >= tx_gains.size()) {
 | 
				
			||||||
 | 
							LOGC(DDEV, ALERT) << "Requested non-existent channel" << chan;
 | 
				
			||||||
 | 
							return 0.0f;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						get_dev_band_desc(desc);
 | 
				
			||||||
 | 
						tx_power = desc.nom_out_tx_power - atten;
 | 
				
			||||||
 | 
						db = TxPower2TxGain(desc, tx_power);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bladerf_set_gain(dev, BLADERF_CHANNEL_TX(chan), 30);
 | 
				
			||||||
 | 
						int actual_gain;
 | 
				
			||||||
 | 
						bladerf_get_gain(dev, BLADERF_CHANNEL_RX(chan), &actual_gain);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tx_gains[chan] = actual_gain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGC(DDEV, INFO)
 | 
				
			||||||
 | 
							<< "Set TX gain to " << tx_gains[chan] << "dB, ~" << TxGain2TxPower(desc, tx_gains[chan]) << " dBm "
 | 
				
			||||||
 | 
							<< "(asked for " << db << " dB, ~" << tx_power << " dBm)";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					double blade_device::getPowerAttenuation(size_t chan)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						dev_band_desc desc;
 | 
				
			||||||
 | 
						if (chan >= tx_gains.size()) {
 | 
				
			||||||
 | 
							LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
				
			||||||
 | 
							return 0.0f;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						get_dev_band_desc(desc);
 | 
				
			||||||
 | 
						return desc.nom_out_tx_power - TxGain2TxPower(desc, tx_gains[chan]);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int blade_device::getNominalTxPower(size_t chan)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						dev_band_desc desc;
 | 
				
			||||||
 | 
						get_dev_band_desc(desc);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return desc.nom_out_tx_power;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int blade_device::open()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						bladerf_log_set_verbosity(BLADERF_LOG_LEVEL_VERBOSE);
 | 
				
			||||||
 | 
						bladerf_set_usb_reset_on_open(true);
 | 
				
			||||||
 | 
						auto success = bladerf_open(&dev, cfg->dev_args);
 | 
				
			||||||
 | 
						if (success != 0) {
 | 
				
			||||||
 | 
							struct bladerf_devinfo *info;
 | 
				
			||||||
 | 
							auto num_devs = bladerf_get_device_list(&info);
 | 
				
			||||||
 | 
							LOGC(DDEV, ALERT) << "No bladerf devices found with identifier '" << cfg->dev_args << "'";
 | 
				
			||||||
 | 
							if (num_devs) {
 | 
				
			||||||
 | 
								for (int i = 0; i < num_devs; i++)
 | 
				
			||||||
 | 
									LOGC(DDEV, ALERT) << "Found device:" << info[i].product << " serial " << info[i].serial;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if (strcmp("bladerf2", bladerf_get_board_name(dev))) {
 | 
				
			||||||
 | 
							LOGC(DDEV, ALERT) << "Only BladeRF2 supported! found:" << bladerf_get_board_name(dev);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dev_type = blade_dev_type::BLADE2;
 | 
				
			||||||
 | 
						tx_window = TX_WINDOW_FIXED;
 | 
				
			||||||
 | 
						update_band_dev(dev_key(dev_type, tx_sps, rx_sps));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct bladerf_devinfo info;
 | 
				
			||||||
 | 
						bladerf_get_devinfo(dev, &info);
 | 
				
			||||||
 | 
						LOGC(DDEV, INFO) << "Using discovered bladerf device " << info.serial;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tx_freqs.resize(chans);
 | 
				
			||||||
 | 
						rx_freqs.resize(chans);
 | 
				
			||||||
 | 
						tx_gains.resize(chans);
 | 
				
			||||||
 | 
						rx_gains.resize(chans);
 | 
				
			||||||
 | 
						rx_buffers.resize(chans);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (cfg->clock_ref) {
 | 
				
			||||||
 | 
						case REF_INTERNAL:
 | 
				
			||||||
 | 
						case REF_EXTERNAL:
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							LOGC(DDEV, ALERT) << "Invalid reference type";
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (cfg->clock_ref == REF_EXTERNAL) {
 | 
				
			||||||
 | 
							bool is_locked;
 | 
				
			||||||
 | 
							int status = bladerf_set_pll_enable(dev, true);
 | 
				
			||||||
 | 
							CHKRET()
 | 
				
			||||||
 | 
							status = bladerf_set_pll_refclk(dev, 10000000);
 | 
				
			||||||
 | 
							CHKRET()
 | 
				
			||||||
 | 
							for (int i = 0; i < 20; i++) {
 | 
				
			||||||
 | 
								usleep(50 * 1000);
 | 
				
			||||||
 | 
								status = bladerf_get_pll_lock_state(dev, &is_locked);
 | 
				
			||||||
 | 
								CHKRET()
 | 
				
			||||||
 | 
								if (is_locked)
 | 
				
			||||||
 | 
									break;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (!is_locked) {
 | 
				
			||||||
 | 
								LOGC(DDEV, ALERT) << "unable to lock refclk!";
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGC(DDEV, INFO)
 | 
				
			||||||
 | 
							<< "Selected clock source is " << ((cfg->clock_ref == REF_INTERNAL) ? "internal" : "external 10Mhz");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						set_rates();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						1ts = 3/5200s
 | 
				
			||||||
 | 
						1024*2 = small gap(~180us) every 9.23ms = every 16 ts? -> every 2 frames
 | 
				
			||||||
 | 
						1024*1 = large gap(~627us) every 9.23ms = every 16 ts? -> every 2 frames
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rif convertbuffer = 625*4 = 2500 -> 4 ts
 | 
				
			||||||
 | 
						rif rxtxbuf = 4 * segment(625*4) = 10000 -> 16 ts
 | 
				
			||||||
 | 
						*/
 | 
				
			||||||
 | 
						const unsigned int num_buffers = 256;
 | 
				
			||||||
 | 
						const unsigned int buffer_size = 1024 * 4; /* Must be a multiple of 1024 */
 | 
				
			||||||
 | 
						const unsigned int num_transfers = 32;
 | 
				
			||||||
 | 
						const unsigned int timeout_ms = 3500;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bladerf_sync_config(dev, BLADERF_RX_X1, BLADERF_FORMAT_SC16_Q11_META, num_buffers, buffer_size, num_transfers,
 | 
				
			||||||
 | 
								    timeout_ms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bladerf_sync_config(dev, BLADERF_TX_X1, BLADERF_FORMAT_SC16_Q11_META, num_buffers, buffer_size, num_transfers,
 | 
				
			||||||
 | 
								    timeout_ms);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Number of samples per over-the-wire packet */
 | 
				
			||||||
 | 
						tx_spp = rx_spp = buffer_size;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
 | 
				
			||||||
 | 
						for (size_t i = 0; i < rx_buffers.size(); i++)
 | 
				
			||||||
 | 
							rx_buffers[i] = new smpl_buf(buf_len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pkt_bufs = std::vector<std::vector<short> >(chans, std::vector<short>(2 * rx_spp));
 | 
				
			||||||
 | 
						for (size_t i = 0; i < pkt_bufs.size(); i++)
 | 
				
			||||||
 | 
							pkt_ptrs.push_back(&pkt_bufs[i].front());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						init_gains();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NORMAL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool blade_device::restart()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Allow 100 ms delay to align multi-channel streams */
 | 
				
			||||||
 | 
						double delay = 0.2;
 | 
				
			||||||
 | 
						int status;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						status = bladerf_enable_module(dev, BLADERF_CHANNEL_RX(0), true);
 | 
				
			||||||
 | 
						CHKRET()
 | 
				
			||||||
 | 
						status = bladerf_enable_module(dev, BLADERF_CHANNEL_TX(0), true);
 | 
				
			||||||
 | 
						CHKRET()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bladerf_timestamp now;
 | 
				
			||||||
 | 
						status = bladerf_get_timestamp(dev, BLADERF_RX, &now);
 | 
				
			||||||
 | 
						ts_initial = now + rx_rate * delay;
 | 
				
			||||||
 | 
						LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool blade_device::start()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						LOGC(DDEV, INFO) << "Starting USRP...";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (started) {
 | 
				
			||||||
 | 
							LOGC(DDEV, ERROR) << "Device already started";
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!restart())
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						started = true;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool blade_device::stop()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (!started)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* reset internal buffer timestamps */
 | 
				
			||||||
 | 
						for (size_t i = 0; i < rx_buffers.size(); i++)
 | 
				
			||||||
 | 
							rx_buffers[i]->reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						band_reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						started = false;
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int blade_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						ssize_t rc;
 | 
				
			||||||
 | 
						uint64_t ts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (bufs.size() != chans) {
 | 
				
			||||||
 | 
							LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size();
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*overrun = false;
 | 
				
			||||||
 | 
						*underrun = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Shift read time with respect to transmit clock
 | 
				
			||||||
 | 
						timestamp += ts_offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ts = timestamp;
 | 
				
			||||||
 | 
						LOGC(DDEV, DEBUG) << "Requested timestamp = " << ts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Check that timestamp is valid
 | 
				
			||||||
 | 
						rc = rx_buffers[0]->avail_smpls(timestamp);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							LOGC(DDEV, ERROR) << rx_buffers[0]->str_code(rc);
 | 
				
			||||||
 | 
							LOGC(DDEV, ERROR) << rx_buffers[0]->str_status(timestamp);
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct bladerf_metadata meta = {};
 | 
				
			||||||
 | 
						meta.timestamp = ts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (rx_buffers[0]->avail_smpls(timestamp) < len) {
 | 
				
			||||||
 | 
							thread_enable_cancel(false);
 | 
				
			||||||
 | 
							int status = bladerf_sync_rx(dev, pkt_ptrs[0], len, &meta, 200U);
 | 
				
			||||||
 | 
							thread_enable_cancel(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (status != 0)
 | 
				
			||||||
 | 
								LOGC(DDEV, ERROR) << "RX broken: " << bladerf_strerror(status);
 | 
				
			||||||
 | 
							if (meta.flags & BLADERF_META_STATUS_OVERRUN)
 | 
				
			||||||
 | 
								LOGC(DDEV, ERROR) << "RX borken, OVERRUN: " << bladerf_strerror(status);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							size_t num_smpls = meta.actual_count;
 | 
				
			||||||
 | 
							;
 | 
				
			||||||
 | 
							ts = meta.timestamp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							for (size_t i = 0; i < rx_buffers.size(); i++) {
 | 
				
			||||||
 | 
								rc = rx_buffers[i]->write((short *)&pkt_bufs[i].front(), num_smpls, ts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								// Continue on local overrun, exit on other errors
 | 
				
			||||||
 | 
								if ((rc < 0)) {
 | 
				
			||||||
 | 
									LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
 | 
				
			||||||
 | 
									LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
 | 
				
			||||||
 | 
									if (rc != smpl_buf::ERROR_OVERFLOW)
 | 
				
			||||||
 | 
										return 0;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							meta = {};
 | 
				
			||||||
 | 
							meta.timestamp = ts + num_smpls;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (size_t i = 0; i < rx_buffers.size(); i++) {
 | 
				
			||||||
 | 
							rc = rx_buffers[i]->read(bufs[i], len, timestamp);
 | 
				
			||||||
 | 
							if ((rc < 0) || (rc != len)) {
 | 
				
			||||||
 | 
								LOGC(DDEV, ERROR) << rx_buffers[i]->str_code(rc);
 | 
				
			||||||
 | 
								LOGC(DDEV, ERROR) << rx_buffers[i]->str_status(timestamp);
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return len;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int blade_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun, unsigned long long timestamp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						*underrun = false;
 | 
				
			||||||
 | 
						static bool first_tx = true;
 | 
				
			||||||
 | 
						struct bladerf_metadata meta = {};
 | 
				
			||||||
 | 
						if (first_tx) {
 | 
				
			||||||
 | 
							meta.timestamp = timestamp;
 | 
				
			||||||
 | 
							meta.flags = BLADERF_META_FLAG_TX_BURST_START;
 | 
				
			||||||
 | 
							first_tx = false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						thread_enable_cancel(false);
 | 
				
			||||||
 | 
						int status = bladerf_sync_tx(dev, (const void *)bufs[0], len, &meta, 200U);
 | 
				
			||||||
 | 
						thread_enable_cancel(true);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (status != 0)
 | 
				
			||||||
 | 
							LOGC(DDEV, ERROR) << "TX broken: " << bladerf_strerror(status);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return len;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool blade_device::updateAlignment(TIMESTAMP timestamp)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool blade_device::set_freq(double freq, size_t chan, bool tx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (tx) {
 | 
				
			||||||
 | 
							bladerf_set_frequency(dev, BLADERF_CHANNEL_TX(chan), freq);
 | 
				
			||||||
 | 
							bladerf_frequency f;
 | 
				
			||||||
 | 
							bladerf_get_frequency(dev, BLADERF_CHANNEL_TX(chan), &f);
 | 
				
			||||||
 | 
							tx_freqs[chan] = f;
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							bladerf_set_frequency(dev, BLADERF_CHANNEL_RX(chan), freq);
 | 
				
			||||||
 | 
							bladerf_frequency f;
 | 
				
			||||||
 | 
							bladerf_get_frequency(dev, BLADERF_CHANNEL_RX(chan), &f);
 | 
				
			||||||
 | 
							rx_freqs[chan] = f;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << (tx ? "TX" : "RX") << "): " << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool blade_device::setTxFreq(double wFreq, size_t chan)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (chan >= tx_freqs.size()) {
 | 
				
			||||||
 | 
							LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ScopedLock lock(tune_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!update_band_from_freq(wFreq, chan, true))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!set_freq(wFreq, chan, true))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool blade_device::setRxFreq(double wFreq, size_t chan)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (chan >= rx_freqs.size()) {
 | 
				
			||||||
 | 
							LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ScopedLock lock(tune_lock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!update_band_from_freq(wFreq, chan, false))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return set_freq(wFreq, chan, false);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double blade_device::getTxFreq(size_t chan)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (chan >= tx_freqs.size()) {
 | 
				
			||||||
 | 
							LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
				
			||||||
 | 
							return 0.0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return tx_freqs[chan];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double blade_device::getRxFreq(size_t chan)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (chan >= rx_freqs.size()) {
 | 
				
			||||||
 | 
							LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
				
			||||||
 | 
							return 0.0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return rx_freqs[chan];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool blade_device::requiresRadioAlign()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					GSM::Time blade_device::minLatency()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return GSM::Time(6, 7);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TIMESTAMP blade_device::initialWriteTimestamp()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return ts_initial;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					TIMESTAMP blade_device::initialReadTimestamp()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return ts_initial;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double blade_device::fullScaleInputValue()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return (double)2047;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double blade_device::fullScaleOutputValue()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return (double)2047;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return new blade_device(type, cfg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										195
									
								
								Transceiver52M/device/bladerf/bladerf.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										195
									
								
								Transceiver52M/device/bladerf/bladerf.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,195 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2022 sysmocom - s.f.m.c. GmbH
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Eric Wild <ewild@sysmocom.de>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * SPDX-License-Identifier: AGPL-3.0+
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 * See the COPYING file in the main directory for details.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
 | 
					#ifdef HAVE_CONFIG_H
 | 
				
			||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "bandmanager.h"
 | 
				
			||||||
 | 
					#include "radioDevice.h"
 | 
				
			||||||
 | 
					#include "smpl_buf.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsm_utils.h>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum class blade_dev_type { BLADE1, BLADE2 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct dev_band_desc {
 | 
				
			||||||
 | 
						/* Maximum UHD Tx Gain which can be set/used without distorting the
 | 
				
			||||||
 | 
						   output signal, and the resulting real output power measured when that
 | 
				
			||||||
 | 
						   gain is used. Correct measured values only provided for B210 so far. */
 | 
				
			||||||
 | 
						double nom_uhd_tx_gain; /* dB */
 | 
				
			||||||
 | 
						double nom_out_tx_power; /* dBm */
 | 
				
			||||||
 | 
						/* Factor used to infer base real RSSI offset on the Rx path based on current
 | 
				
			||||||
 | 
						   configured RxGain. The resulting rssiOffset is added to the per burst
 | 
				
			||||||
 | 
						   calculated energy in upper layers. These values were empirically
 | 
				
			||||||
 | 
						   found and may change based on multiple factors, see OS#4468.
 | 
				
			||||||
 | 
						   rssiOffset = rxGain + rxgain2rssioffset_rel;
 | 
				
			||||||
 | 
						*/
 | 
				
			||||||
 | 
						double rxgain2rssioffset_rel; /* dB */
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Device parameter descriptor */
 | 
				
			||||||
 | 
					struct dev_desc {
 | 
				
			||||||
 | 
						unsigned channels;
 | 
				
			||||||
 | 
						double mcr;
 | 
				
			||||||
 | 
						double rate;
 | 
				
			||||||
 | 
						double offset;
 | 
				
			||||||
 | 
						std::string desc_str;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using dev_key = std::tuple<blade_dev_type, int, int>;
 | 
				
			||||||
 | 
					using dev_band_key = std::tuple<blade_dev_type, enum gsm_band>;
 | 
				
			||||||
 | 
					using power_map_t = std::map<dev_band_key, dev_band_desc>;
 | 
				
			||||||
 | 
					using dev_map_t = std::map<dev_key, dev_desc>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class blade_device : public RadioDevice, public band_manager<power_map_t, dev_map_t> {
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
						blade_device(InterfaceType iface, const struct trx_cfg *cfg);
 | 
				
			||||||
 | 
						~blade_device();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int open();
 | 
				
			||||||
 | 
						bool start();
 | 
				
			||||||
 | 
						bool stop();
 | 
				
			||||||
 | 
						bool restart();
 | 
				
			||||||
 | 
						enum TxWindowType getWindowType()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return tx_window;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int readSamples(std::vector<short *> &bufs, int len, bool *overrun, TIMESTAMP timestamp, bool *underrun);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int writeSamples(std::vector<short *> &bufs, int len, bool *underrun, TIMESTAMP timestamp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool updateAlignment(TIMESTAMP timestamp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool setTxFreq(double wFreq, size_t chan);
 | 
				
			||||||
 | 
						bool setRxFreq(double wFreq, size_t chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TIMESTAMP initialWriteTimestamp();
 | 
				
			||||||
 | 
						TIMESTAMP initialReadTimestamp();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						double fullScaleInputValue();
 | 
				
			||||||
 | 
						double fullScaleOutputValue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						double setRxGain(double db, size_t chan);
 | 
				
			||||||
 | 
						double getRxGain(size_t chan);
 | 
				
			||||||
 | 
						double maxRxGain(void)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return rx_gain_max;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						double minRxGain(void)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return rx_gain_min;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						double rssiOffset(size_t chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						double setPowerAttenuation(int atten, size_t chan);
 | 
				
			||||||
 | 
						double getPowerAttenuation(size_t chan = 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int getNominalTxPower(size_t chan = 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						double getTxFreq(size_t chan);
 | 
				
			||||||
 | 
						double getRxFreq(size_t chan);
 | 
				
			||||||
 | 
						double getRxFreq();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool setRxAntenna(const std::string &ant, size_t chan)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return {};
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						std::string getRxAntenna(size_t chan)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return {};
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						bool setTxAntenna(const std::string &ant, size_t chan)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return {};
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
						std::string getTxAntenna(size_t chan)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return {};
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool requiresRadioAlign();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						GSM::Time minLatency();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						inline double getSampleRate()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return tx_rate;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/** Receive and process asynchronous message
 | 
				
			||||||
 | 
						    @return true if message received or false on timeout or error
 | 
				
			||||||
 | 
						*/
 | 
				
			||||||
 | 
						bool recv_async_msg();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enum err_code {
 | 
				
			||||||
 | 
							ERROR_TIMING = -1,
 | 
				
			||||||
 | 
							ERROR_TIMEOUT = -2,
 | 
				
			||||||
 | 
							ERROR_UNRECOVERABLE = -3,
 | 
				
			||||||
 | 
							ERROR_UNHANDLED = -4,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    protected:
 | 
				
			||||||
 | 
						struct bladerf *dev;
 | 
				
			||||||
 | 
						void *usrp_dev;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						enum TxWindowType tx_window;
 | 
				
			||||||
 | 
						enum blade_dev_type dev_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						double tx_rate, rx_rate;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						double rx_gain_min, rx_gain_max;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						std::vector<double> tx_gains, rx_gains;
 | 
				
			||||||
 | 
						std::vector<double> tx_freqs, rx_freqs;
 | 
				
			||||||
 | 
						size_t tx_spp, rx_spp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool started;
 | 
				
			||||||
 | 
						bool aligned;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t drop_cnt;
 | 
				
			||||||
 | 
						uint64_t prev_ts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						TIMESTAMP ts_initial, ts_offset;
 | 
				
			||||||
 | 
						std::vector<smpl_buf *> rx_buffers;
 | 
				
			||||||
 | 
						/* Sample buffers used to receive samples: */
 | 
				
			||||||
 | 
						std::vector<std::vector<short> > pkt_bufs;
 | 
				
			||||||
 | 
						/* Used to call UHD API: Buffer pointer of each elem in pkt_ptrs will
 | 
				
			||||||
 | 
						   point to corresponding buffer of vector pkt_bufs. */
 | 
				
			||||||
 | 
						std::vector<short *> pkt_ptrs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void init_gains();
 | 
				
			||||||
 | 
						void set_channels(bool swap);
 | 
				
			||||||
 | 
						void set_rates();
 | 
				
			||||||
 | 
						bool flush_recv(size_t num_pkts);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool set_freq(double freq, size_t chan, bool tx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Thread *async_event_thrd;
 | 
				
			||||||
 | 
						Mutex tune_lock;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -4,7 +4,7 @@ AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES)
 | 
				
			|||||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
 | 
					AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
noinst_HEADERS = radioDevice.h smpl_buf.h
 | 
					noinst_HEADERS = radioDevice.h smpl_buf.h bandmanager.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
noinst_LTLIBRARIES = libdevice_common.la
 | 
					noinst_LTLIBRARIES = libdevice_common.la
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										137
									
								
								Transceiver52M/device/common/bandmanager.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										137
									
								
								Transceiver52M/device/common/bandmanager.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,137 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Eric Wild <ewild@sysmocom.de>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <string>
 | 
				
			||||||
 | 
					#include <tuple>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Logger.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#include <osmocom/gsm/gsm_utils.h>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <typename powermapt, typename devmapt>
 | 
				
			||||||
 | 
					class band_manager {
 | 
				
			||||||
 | 
						using powerkeyt = typename powermapt::key_type;
 | 
				
			||||||
 | 
						using powermappedt = typename powermapt::mapped_type;
 | 
				
			||||||
 | 
						using devkeyt = typename devmapt::key_type;
 | 
				
			||||||
 | 
						devkeyt m_dev_type;
 | 
				
			||||||
 | 
						const powermapt &m_power_map;
 | 
				
			||||||
 | 
						const devmapt &m_dev_map;
 | 
				
			||||||
 | 
						powerkeyt m_fallback;
 | 
				
			||||||
 | 
						enum gsm_band m_band;
 | 
				
			||||||
 | 
						powermappedt m_band_desc;
 | 
				
			||||||
 | 
						bool band_ass_curr_sess{}; /* true if  "band" was set after last POWEROFF */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// looks up either first tuple element (->enum) or straight enum
 | 
				
			||||||
 | 
						template <typename T, typename std::enable_if<std::is_enum<T>::value>::type *dummy = nullptr>
 | 
				
			||||||
 | 
						auto key_helper(T &t) -> T
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return t;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						template <typename T>
 | 
				
			||||||
 | 
						auto key_helper(T t) -> typename std::tuple_element<0, T>::type
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return std::get<0>(t);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void assign_band_desc(enum gsm_band req_band)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto key = key_helper(m_dev_type);
 | 
				
			||||||
 | 
							auto fallback_key = key_helper(m_fallback);
 | 
				
			||||||
 | 
							auto it = m_power_map.find({ key, req_band });
 | 
				
			||||||
 | 
							if (it == m_power_map.end()) {
 | 
				
			||||||
 | 
								auto desc = m_dev_map.at(m_dev_type);
 | 
				
			||||||
 | 
								LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device " << desc.desc_str
 | 
				
			||||||
 | 
										  << " on band " << gsm_band_name(req_band) << ", using fallback..";
 | 
				
			||||||
 | 
								it = m_power_map.find({ fallback_key, req_band });
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							OSMO_ASSERT(it != m_power_map.end());
 | 
				
			||||||
 | 
							m_band_desc = it->second;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool set_band(enum gsm_band req_band)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (band_ass_curr_sess && req_band != m_band) {
 | 
				
			||||||
 | 
								LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
 | 
				
			||||||
 | 
										  << " different from previous band " << gsm_band_name(m_band);
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (req_band != m_band) {
 | 
				
			||||||
 | 
								m_band = req_band;
 | 
				
			||||||
 | 
								assign_band_desc(m_band);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							band_ass_curr_sess = true;
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
						band_manager(const devkeyt &dev_type, const powermapt &power_map, const devmapt &dev_map, powerkeyt fallback)
 | 
				
			||||||
 | 
							: m_dev_type(dev_type), m_power_map(power_map), m_dev_map(dev_map), m_fallback(fallback),
 | 
				
			||||||
 | 
							  m_band((enum gsm_band)0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						band_manager(const powermapt &power_map, const devmapt &dev_map)
 | 
				
			||||||
 | 
							: m_dev_type(dev_map.begin()->first), m_power_map(power_map), m_dev_map(dev_map),
 | 
				
			||||||
 | 
							  m_fallback(m_power_map.begin()->first), m_band((enum gsm_band)0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void band_reset()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							band_ass_curr_sess = false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void update_band_dev(devkeyt dev_type) {
 | 
				
			||||||
 | 
							m_dev_type = dev_type;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						void get_dev_band_desc(powermappedt &desc)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							if (m_band == 0) {
 | 
				
			||||||
 | 
								LOGC(DDEV, ERROR)
 | 
				
			||||||
 | 
									<< "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
 | 
				
			||||||
 | 
								assign_band_desc(GSM_BAND_900);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							desc = m_band_desc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool update_band_from_freq(double wFreq, int chan, bool is_tx)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							enum gsm_band req_band;
 | 
				
			||||||
 | 
							auto dirstr = is_tx ? "Tx" : "Rx";
 | 
				
			||||||
 | 
							auto req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, !is_tx);
 | 
				
			||||||
 | 
							if (req_arfcn == 0xffff) {
 | 
				
			||||||
 | 
								LOGCHAN(chan, DDEV, ALERT)
 | 
				
			||||||
 | 
									<< "Unknown ARFCN for " << dirstr << " Frequency " << wFreq / 1000 << " kHz";
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
 | 
				
			||||||
 | 
								LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for " << dirstr << " Frequency " << wFreq
 | 
				
			||||||
 | 
											   << " Hz (ARFCN " << req_arfcn << " )";
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							return set_band(req_band);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -51,13 +51,10 @@ class RadioDevice {
 | 
				
			|||||||
    MULTI_ARFCN,
 | 
					    MULTI_ARFCN,
 | 
				
			||||||
  };
 | 
					  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type,
 | 
					  static RadioDevice *make(InterfaceType type, const struct trx_cfg *cfg);
 | 
				
			||||||
                           size_t chans = 1, double offset = 0.0,
 | 
					 | 
				
			||||||
                           const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""),
 | 
					 | 
				
			||||||
                           const std::vector<std::string>& rx_paths = std::vector<std::string>(1, ""));
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Initialize the USRP */
 | 
					  /** Initialize the USRP */
 | 
				
			||||||
  virtual int open(const std::string &args, int ref, bool swap_channels)=0;
 | 
					  virtual int open() = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  virtual ~RadioDevice() { }
 | 
					  virtual ~RadioDevice() { }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -164,24 +161,30 @@ class RadioDevice {
 | 
				
			|||||||
  double lo_offset;
 | 
					  double lo_offset;
 | 
				
			||||||
  std::vector<std::string> tx_paths, rx_paths;
 | 
					  std::vector<std::string> tx_paths, rx_paths;
 | 
				
			||||||
  std::vector<struct device_counters> m_ctr;
 | 
					  std::vector<struct device_counters> m_ctr;
 | 
				
			||||||
 | 
					  const struct trx_cfg *cfg;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chan_num, double offset,
 | 
					#define charp2str(a) ((a) ? std::string(a) : std::string(""))
 | 
				
			||||||
              const std::vector<std::string>& tx_paths,
 | 
					 | 
				
			||||||
              const std::vector<std::string>& rx_paths):
 | 
					 | 
				
			||||||
		tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chan_num), lo_offset(offset),
 | 
					 | 
				
			||||||
		tx_paths(tx_paths), rx_paths(rx_paths)
 | 
					 | 
				
			||||||
	{
 | 
					 | 
				
			||||||
		if (iface == MULTI_ARFCN) {
 | 
					 | 
				
			||||||
			LOGC(DDEV, INFO) << "Multi-ARFCN: "<< chan_num << " logical chans -> 1 physical chans";
 | 
					 | 
				
			||||||
			chans = 1;
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		m_ctr.resize(chans);
 | 
					  RadioDevice(InterfaceType type, const struct trx_cfg *cfg)
 | 
				
			||||||
		for (size_t i = 0; i < chans; i++) {
 | 
						  : tx_sps(cfg->tx_sps), rx_sps(cfg->rx_sps), iface(type), chans(cfg->num_chans), lo_offset(cfg->offset),
 | 
				
			||||||
			memset(&m_ctr[i], 0, sizeof(m_ctr[i]));
 | 
						    m_ctr(chans), cfg(cfg)
 | 
				
			||||||
			m_ctr[i].chan = i;
 | 
					  {
 | 
				
			||||||
		}
 | 
						  /* Generate vector of rx/tx_path: */
 | 
				
			||||||
	}
 | 
						  for (unsigned int i = 0; i < cfg->num_chans; i++) {
 | 
				
			||||||
 | 
							  rx_paths.push_back(charp2str(cfg->chans[i].rx_path));
 | 
				
			||||||
 | 
							  tx_paths.push_back(charp2str(cfg->chans[i].tx_path));
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  if (iface == MULTI_ARFCN) {
 | 
				
			||||||
 | 
							  LOGC(DDEV, INFO) << "Multi-ARFCN: " << chans << " logical chans -> 1 physical chans";
 | 
				
			||||||
 | 
							  chans = 1;
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						  for (size_t i = 0; i < chans; i++) {
 | 
				
			||||||
 | 
							  memset(&m_ctr[i], 0, sizeof(m_ctr[i]));
 | 
				
			||||||
 | 
							  m_ctr[i].chan = i;
 | 
				
			||||||
 | 
						  }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool set_antennas() {
 | 
					  bool set_antennas() {
 | 
				
			||||||
	unsigned int i;
 | 
						unsigned int i;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -56,20 +56,13 @@ using namespace std;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags);
 | 
					static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
IPCDevice::IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
 | 
					IPCDevice::IPCDevice(InterfaceType iface, const struct trx_cfg *cfg)
 | 
				
			||||||
		     const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
 | 
						: RadioDevice(iface, cfg), sk_chan_state(chans, ipc_per_trx_sock_state()), tx_attenuation(),
 | 
				
			||||||
	: 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), rx_buffers(chans), started(false), tx_gains(chans),
 | 
				
			||||||
	  tmp_state(IPC_IF_MSG_GREETING_REQ), shm(NULL), shm_dec(0), started(false)
 | 
						  rx_gains(chans)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	LOGC(DDEV, INFO) << "creating IPC device...";
 | 
						LOGC(DDEV, INFO) << "creating IPC device...";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rx_gains.resize(chans);
 | 
					 | 
				
			||||||
	tx_gains.resize(chans);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rx_buffers.resize(chans);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sk_chan_state.resize(chans, ipc_per_trx_sock_state());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Set up per-channel Rx timestamp based Ring buffers */
 | 
						/* Set up per-channel Rx timestamp based Ring buffers */
 | 
				
			||||||
	for (size_t i = 0; i < rx_buffers.size(); i++)
 | 
						for (size_t i = 0; i < rx_buffers.size(); i++)
 | 
				
			||||||
		rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
 | 
							rx_buffers[i] = new smpl_buf(SAMPLE_BUF_SZ / sizeof(uint32_t));
 | 
				
			||||||
@@ -543,9 +536,9 @@ void IPCDevice::ipc_sock_close(struct ipc_per_trx_sock_state *state)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	LOGP(DDEV, LOGL_NOTICE, "IPC socket has LOST connection\n");
 | 
						LOGP(DDEV, LOGL_NOTICE, "IPC socket has LOST connection\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_fd_unregister(bfd);
 | 
				
			||||||
	close(bfd->fd);
 | 
						close(bfd->fd);
 | 
				
			||||||
	bfd->fd = -1;
 | 
						bfd->fd = -1;
 | 
				
			||||||
	osmo_fd_unregister(bfd);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* flush the queue */
 | 
						/* flush the queue */
 | 
				
			||||||
	while (!llist_empty(&state->upqueue)) {
 | 
						while (!llist_empty(&state->upqueue)) {
 | 
				
			||||||
@@ -777,11 +770,12 @@ static int ipc_chan_sock_cb(struct osmo_fd *bfd, unsigned int flags)
 | 
				
			|||||||
	return rc;
 | 
						return rc;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int IPCDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
					int IPCDevice::open()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	std::string k, v;
 | 
						std::string k, v;
 | 
				
			||||||
	std::string::size_type keyend;
 | 
						std::string::size_type keyend;
 | 
				
			||||||
	int rc;
 | 
						int rc;
 | 
				
			||||||
 | 
						std::string args(cfg->dev_args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((keyend = args.find('=')) != std::string::npos) {
 | 
						if ((keyend = args.find('=')) != std::string::npos) {
 | 
				
			||||||
		k = args.substr(0, keyend++);
 | 
							k = args.substr(0, keyend++);
 | 
				
			||||||
@@ -816,7 +810,7 @@ int IPCDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
				
			|||||||
	while (tmp_state != IPC_IF_MSG_INFO_CNF)
 | 
						while (tmp_state != IPC_IF_MSG_INFO_CNF)
 | 
				
			||||||
		osmo_select_main(0);
 | 
							osmo_select_main(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ipc_tx_open_req(&master_sk_state, chans, ref);
 | 
						ipc_tx_open_req(&master_sk_state, chans, cfg->clock_ref);
 | 
				
			||||||
	/* Wait until confirmation is recieved */
 | 
						/* Wait until confirmation is recieved */
 | 
				
			||||||
	while (tmp_state != IPC_IF_MSG_OPEN_CNF)
 | 
						while (tmp_state != IPC_IF_MSG_OPEN_CNF)
 | 
				
			||||||
		osmo_select_main(0);
 | 
							osmo_select_main(0);
 | 
				
			||||||
@@ -1265,16 +1259,15 @@ bool IPCDevice::setRxFreq(double wFreq, size_t chan)
 | 
				
			|||||||
	return send_chan_wait_rsp(chan, msg, IPC_IF_MSG_SETFREQ_CNF);
 | 
						return send_chan_wait_rsp(chan, msg, IPC_IF_MSG_SETFREQ_CNF);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset,
 | 
					RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
 | 
				
			||||||
			       const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (tx_sps != rx_sps) {
 | 
						if (cfg->tx_sps != cfg->rx_sps) {
 | 
				
			||||||
		LOGC(DDEV, ERROR) << "IPC Requires tx_sps == rx_sps";
 | 
							LOGC(DDEV, ERROR) << "IPC Requires tx_sps == rx_sps";
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (lo_offset != 0.0) {
 | 
						if (cfg->offset != 0.0) {
 | 
				
			||||||
		LOGC(DDEV, ERROR) << "IPC doesn't support lo_offset";
 | 
							LOGC(DDEV, ERROR) << "IPC doesn't support lo_offset";
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return new IPCDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
 | 
						return new IPCDevice(type, cfg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -115,12 +115,11 @@ class IPCDevice : public RadioDevice {
 | 
				
			|||||||
	int ipc_chan_sock_write(osmo_fd *bfd);
 | 
						int ipc_chan_sock_write(osmo_fd *bfd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Object constructor */
 | 
						/** Object constructor */
 | 
				
			||||||
	IPCDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
 | 
						IPCDevice(InterfaceType iface, const struct trx_cfg *cfg);
 | 
				
			||||||
		  const std::vector<std::string> &tx_paths, const std::vector<std::string> &rx_paths);
 | 
					 | 
				
			||||||
	virtual ~IPCDevice() override;
 | 
						virtual ~IPCDevice() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Instantiate the IPC */
 | 
						/** Instantiate the IPC */
 | 
				
			||||||
	virtual int open(const std::string &args, int ref, bool swap_channels) override;
 | 
						virtual int open() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Start the IPC */
 | 
						/** Start the IPC */
 | 
				
			||||||
	virtual bool start() override;
 | 
						virtual bool start() override;
 | 
				
			||||||
@@ -199,7 +198,7 @@ class IPCDevice : public RadioDevice {
 | 
				
			|||||||
	virtual double minRxGain(void) override;
 | 
						virtual double minRxGain(void) override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* FIXME: return rx_gains[chan] ? receive factor from IPC Driver? */
 | 
						/* FIXME: return rx_gains[chan] ? receive factor from IPC Driver? */
 | 
				
			||||||
	double rssiOffset(size_t chan) { return 0.0f; };
 | 
						double rssiOffset(size_t chan) override { return 0.0f; };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	double setPowerAttenuation(int atten, size_t chan) override;
 | 
						double setPowerAttenuation(int atten, size_t chan) override;
 | 
				
			||||||
	double getPowerAttenuation(size_t chan = 0) override;
 | 
						double getPowerAttenuation(size_t chan = 0) override;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,9 +1,8 @@
 | 
				
			|||||||
include $(top_srcdir)/Makefile.common
 | 
					include $(top_srcdir)/Makefile.common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
 | 
					AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/../common
 | 
				
			||||||
AM_CFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
 | 
					AM_CFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(UHD_CFLAGS)
 | 
				
			||||||
AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS)
 | 
					AM_CXXFLAGS = -Wall $(LIBOSMOCORE_CFLAGS) $(UHD_CFLAGS)
 | 
				
			||||||
AM_LDFLAGS = -lpthread -lrt
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
noinst_HEADERS = IPCDevice.h shm.h ipc_shm.h ipc_chan.h ipc_sock.h
 | 
					noinst_HEADERS = IPCDevice.h shm.h ipc_shm.h ipc_chan.h ipc_sock.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -14,30 +13,22 @@ endif
 | 
				
			|||||||
noinst_LTLIBRARIES = libdevice.la
 | 
					noinst_LTLIBRARIES = libdevice.la
 | 
				
			||||||
 | 
					
 | 
				
			||||||
libdevice_la_SOURCES = IPCDevice.cpp shm.c ipc_shm.c ipc_chan.c ipc_sock.c
 | 
					libdevice_la_SOURCES = IPCDevice.cpp shm.c ipc_shm.c ipc_chan.c ipc_sock.c
 | 
				
			||||||
libdevice_la_LIBADD = $(top_builddir)/Transceiver52M/device/common/libdevice_common.la
 | 
					libdevice_la_CPPFLAGS = $(AM_CPPFLAGS) -DIPCMAGIC
 | 
				
			||||||
libdevice_la_CXXFLAGS = $(AM_CXXFLAGS) -DIPCMAGIC
 | 
					libdevice_la_LIBADD = \
 | 
				
			||||||
 | 
						$(top_builddir)/Transceiver52M/device/common/libdevice_common.la \
 | 
				
			||||||
#work around distclean issue on older autotools vers:
 | 
						-lpthread \
 | 
				
			||||||
#a direct build of ../uhd/UHDDevice.cpp tries to clean
 | 
						-lrt \
 | 
				
			||||||
#../uhd/.dep/UHDDevice.Plo twice and fails
 | 
						$(NULL)
 | 
				
			||||||
uhddev_ipc.cpp:
 | 
					 | 
				
			||||||
	echo "#include \"../uhd/UHDDevice.cpp\"" >$@
 | 
					 | 
				
			||||||
CLEANFILES= uhddev_ipc.cpp
 | 
					 | 
				
			||||||
BUILT_SOURCES = uhddev_ipc.cpp
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
if DEVICE_UHD
 | 
					if DEVICE_UHD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bin_PROGRAMS = ipc-driver-test
 | 
					bin_PROGRAMS = ipc-driver-test
 | 
				
			||||||
#ipc_driver_test_SHORTNAME = drvt
 | 
					#ipc_driver_test_SHORTNAME = drvt
 | 
				
			||||||
ipc_driver_test_SOURCES = ipc-driver-test.c uhdwrap.cpp ipc_shm.c ipc_chan.c ipc_sock.c uhddev_ipc.cpp
 | 
					ipc_driver_test_SOURCES = ipc-driver-test.c uhdwrap.cpp ../uhd/UHDDevice.cpp
 | 
				
			||||||
ipc_driver_test_LDADD = \
 | 
					ipc_driver_test_LDADD = \
 | 
				
			||||||
        shm.lo \
 | 
						libdevice.la \
 | 
				
			||||||
	$(top_builddir)/Transceiver52M/device/common/libdevice_common.la \
 | 
						$(COMMON_LA) \
 | 
				
			||||||
	$(COMMON_LA)
 | 
					 | 
				
			||||||
	$(LIBOSMOCORE_LIBS) \
 | 
						$(LIBOSMOCORE_LIBS) \
 | 
				
			||||||
 | 
						$(UHD_LIBS) \
 | 
				
			||||||
	$(NULL)
 | 
						$(NULL)
 | 
				
			||||||
ipc_driver_test_CXXFLAGS = $(AM_CXXFLAGS) $(UHD_CFLAGS)
 | 
					 | 
				
			||||||
ipc_driver_test_CPPFLAGS  = $(AM_CPPFLAGS) $(UHD_CFLAGS)
 | 
					 | 
				
			||||||
ipc_driver_test_CFLAGS  = $(AM_CFLAGS) $(UHD_CFLAGS)
 | 
					 | 
				
			||||||
ipc_driver_test_LDFLAGS  = $(AM_LDFLAGS) $(UHD_LIBS)
 | 
					 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -97,9 +97,9 @@ void ipc_sock_close(struct ipc_sock_state *state)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	ipc_exit_requested = 1;
 | 
						ipc_exit_requested = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_fd_unregister(bfd);
 | 
				
			||||||
	close(bfd->fd);
 | 
						close(bfd->fd);
 | 
				
			||||||
	bfd->fd = -1;
 | 
						bfd->fd = -1;
 | 
				
			||||||
	osmo_fd_unregister(bfd);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* re-enable the generation of ACCEPT for new connections */
 | 
						/* re-enable the generation of ACCEPT for new connections */
 | 
				
			||||||
	osmo_fd_read_enable(&state->listen_bfd);
 | 
						osmo_fd_read_enable(&state->listen_bfd);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -35,9 +35,12 @@ extern "C" {
 | 
				
			|||||||
#include "Threads.h"
 | 
					#include "Threads.h"
 | 
				
			||||||
#include "Utils.h"
 | 
					#include "Utils.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int uhd_wrap::open(const std::string &args, int ref, bool swap_channels)
 | 
					// no vty source for cfg params here, so we have to build our own
 | 
				
			||||||
 | 
					static struct trx_cfg actual_cfg = {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int uhd_wrap::open()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int rv = uhd_device::open(args, ref, swap_channels);
 | 
						int rv = uhd_device::open();
 | 
				
			||||||
	samps_per_buff_rx = rx_stream->get_max_num_samps();
 | 
						samps_per_buff_rx = rx_stream->get_max_num_samps();
 | 
				
			||||||
	samps_per_buff_tx = tx_stream->get_max_num_samps();
 | 
						samps_per_buff_tx = tx_stream->get_max_num_samps();
 | 
				
			||||||
	channel_count = usrp_dev->get_rx_num_channels();
 | 
						channel_count = usrp_dev->get_rx_num_channels();
 | 
				
			||||||
@@ -84,36 +87,33 @@ int uhd_wrap::wrap_read(TIMESTAMP *timestamp)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
extern "C" void *uhdwrap_open(struct ipc_sk_if_open_req *open_req)
 | 
					extern "C" void *uhdwrap_open(struct ipc_sk_if_open_req *open_req)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	unsigned int rx_sps, tx_sps;
 | 
						actual_cfg.num_chans = open_req->num_chans;
 | 
				
			||||||
 | 
						actual_cfg.swap_channels = false;
 | 
				
			||||||
 | 
						/* FIXME: this is actually the sps value, not the sample rate!
 | 
				
			||||||
 | 
						 * sample rate is looked up according to the sps rate by uhd backend */
 | 
				
			||||||
 | 
						actual_cfg.rx_sps = open_req->rx_sample_freq_num / open_req->rx_sample_freq_den;
 | 
				
			||||||
 | 
						actual_cfg.tx_sps = open_req->tx_sample_freq_num / open_req->tx_sample_freq_den;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* FIXME: dev arg string* */
 | 
						/* FIXME: dev arg string* */
 | 
				
			||||||
	/* FIXME: rx frontend bw? */
 | 
						/* FIXME: rx frontend bw? */
 | 
				
			||||||
	/* FIXME: tx frontend bw? */
 | 
						/* FIXME: tx frontend bw? */
 | 
				
			||||||
	ReferenceType cref;
 | 
					 | 
				
			||||||
	switch (open_req->clockref) {
 | 
						switch (open_req->clockref) {
 | 
				
			||||||
	case FEATURE_MASK_CLOCKREF_EXTERNAL:
 | 
						case FEATURE_MASK_CLOCKREF_EXTERNAL:
 | 
				
			||||||
		cref = ReferenceType::REF_EXTERNAL;
 | 
							actual_cfg.clock_ref = ReferenceType::REF_EXTERNAL;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case FEATURE_MASK_CLOCKREF_INTERNAL:
 | 
						case FEATURE_MASK_CLOCKREF_INTERNAL:
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		cref = ReferenceType::REF_INTERNAL;
 | 
							actual_cfg.clock_ref = ReferenceType::REF_INTERNAL;
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	std::vector<std::string> tx_paths;
 | 
					 | 
				
			||||||
	std::vector<std::string> rx_paths;
 | 
					 | 
				
			||||||
	for (unsigned int i = 0; i < open_req->num_chans; i++) {
 | 
						for (unsigned int i = 0; i < open_req->num_chans; i++) {
 | 
				
			||||||
		tx_paths.push_back(open_req->chan_info[i].tx_path);
 | 
							actual_cfg.chans[i].rx_path = open_req->chan_info[i].tx_path;
 | 
				
			||||||
		rx_paths.push_back(open_req->chan_info[i].rx_path);
 | 
							actual_cfg.chans[i].tx_path = open_req->chan_info[i].rx_path;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* FIXME: this is actually the sps value, not the sample rate!
 | 
						uhd_wrap *uhd_wrap_dev = new uhd_wrap(RadioDevice::NORMAL, &actual_cfg);
 | 
				
			||||||
	 * sample rate is looked up according to the sps rate by uhd backend */
 | 
						uhd_wrap_dev->open();
 | 
				
			||||||
	rx_sps = open_req->rx_sample_freq_num / open_req->rx_sample_freq_den;
 | 
					 | 
				
			||||||
	tx_sps = open_req->tx_sample_freq_num / open_req->tx_sample_freq_den;
 | 
					 | 
				
			||||||
	uhd_wrap *uhd_wrap_dev =
 | 
					 | 
				
			||||||
		new uhd_wrap(tx_sps, rx_sps, RadioDevice::NORMAL, open_req->num_chans, 0.0, tx_paths, rx_paths);
 | 
					 | 
				
			||||||
	uhd_wrap_dev->open("", cref, false);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return uhd_wrap_dev;
 | 
						return uhd_wrap_dev;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ class uhd_wrap : public uhd_device {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	//    void ipc_sock_close() override {};
 | 
						//    void ipc_sock_close() override {};
 | 
				
			||||||
	int wrap_read(TIMESTAMP *timestamp);
 | 
						int wrap_read(TIMESTAMP *timestamp);
 | 
				
			||||||
	virtual int open(const std::string &args, int ref, bool swap_channels) override;
 | 
						virtual int open() override;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	//	bool start() override;
 | 
						//	bool start() override;
 | 
				
			||||||
	//	bool stop() override;
 | 
						//	bool stop() override;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -52,39 +52,16 @@ extern "C" {
 | 
				
			|||||||
#define LMS_DEV_SDR_MINI_PREFIX_NAME "LimeSDR-Mini"
 | 
					#define LMS_DEV_SDR_MINI_PREFIX_NAME "LimeSDR-Mini"
 | 
				
			||||||
#define LMS_DEV_NET_MICRO_PREFIX_NAME "LimeNET-Micro"
 | 
					#define LMS_DEV_NET_MICRO_PREFIX_NAME "LimeNET-Micro"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Device parameter descriptor */
 | 
					 | 
				
			||||||
struct dev_desc {
 | 
					 | 
				
			||||||
	/* Does LimeSuite allow switching the clock source for this device?
 | 
					 | 
				
			||||||
	 * LimeSDR-Mini does not have switches but needs soldering to select
 | 
					 | 
				
			||||||
	 * external/internal clock. Any call to LMS_SetClockFreq() will fail.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	bool clock_src_switchable;
 | 
					 | 
				
			||||||
	/* Does LimeSuite allow using REF_INTERNAL for this device?
 | 
					 | 
				
			||||||
	 * LimeNET-Micro does not like selecting internal clock
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	bool clock_src_int_usable;
 | 
					 | 
				
			||||||
	/* Sample rate coef (without having TX/RX samples per symbol into account) */
 | 
					 | 
				
			||||||
	double rate;
 | 
					 | 
				
			||||||
	/* Sample rate coef (without having TX/RX samples per symbol into account), if multi-arfcn is enabled */
 | 
					 | 
				
			||||||
	double rate_multiarfcn;
 | 
					 | 
				
			||||||
	/* Coefficient multiplied by TX sample rate in order to shift Tx time */
 | 
					 | 
				
			||||||
	double ts_offset_coef;
 | 
					 | 
				
			||||||
	/* Coefficient multiplied by TX sample rate in order to shift Tx time, if multi-arfcn is enabled */
 | 
					 | 
				
			||||||
	double ts_offset_coef_multiarfcn;
 | 
					 | 
				
			||||||
	/* Device Name Prefix as presented by LimeSuite API LMS_GetDeviceInfo() */
 | 
					 | 
				
			||||||
	std::string name_prefix;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const std::map<enum lms_dev_type, struct dev_desc> dev_param_map {
 | 
					
 | 
				
			||||||
 | 
					static const dev_map_t dev_param_map {
 | 
				
			||||||
	{ LMS_DEV_SDR_USB,   { true,  true,  GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_SDR_USB_PREFIX_NAME } },
 | 
						{ LMS_DEV_SDR_USB,   { true,  true,  GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_SDR_USB_PREFIX_NAME } },
 | 
				
			||||||
	{ LMS_DEV_SDR_MINI,  { false, true,  GSMRATE, MCBTS_SPACING, 8.9e-5, 8.2e-5, LMS_DEV_SDR_MINI_PREFIX_NAME } },
 | 
						{ LMS_DEV_SDR_MINI,  { false, true,  GSMRATE, MCBTS_SPACING, 8.9e-5, 8.2e-5, LMS_DEV_SDR_MINI_PREFIX_NAME } },
 | 
				
			||||||
	{ LMS_DEV_NET_MICRO, { true,  false, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_NET_MICRO_PREFIX_NAME } },
 | 
						{ LMS_DEV_NET_MICRO, { true,  false, GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, LMS_DEV_NET_MICRO_PREFIX_NAME } },
 | 
				
			||||||
	{ LMS_DEV_UNKNOWN,   { true,  true,  GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, "UNKNOWN" } },
 | 
						{ LMS_DEV_UNKNOWN,   { true,  true,  GSMRATE, MCBTS_SPACING, 8.9e-5, 7.9e-5, "UNKNOWN" } },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef std::tuple<lms_dev_type, enum gsm_band> dev_band_key;
 | 
					static const power_map_t dev_band_nom_power_param_map {
 | 
				
			||||||
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
 | 
					 | 
				
			||||||
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
 | 
					 | 
				
			||||||
	{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_850),	{ 73.0, 11.2,  -6.0  } },
 | 
						{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_850),	{ 73.0, 11.2,  -6.0  } },
 | 
				
			||||||
	{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_900),	{ 73.0, 10.8,  -6.0  } },
 | 
						{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_900),	{ 73.0, 10.8,  -6.0  } },
 | 
				
			||||||
	{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_1800),	{ 65.0, -3.5,  -17.0 } }, /* FIXME: OS#4583: 1800Mhz is failing above TxGain=65, which is around -3.5dBm (already < 0 dBm) */
 | 
						{ std::make_tuple(LMS_DEV_SDR_USB, GSM_BAND_1800),	{ 65.0, -3.5,  -17.0 } }, /* FIXME: OS#4583: 1800Mhz is failing above TxGain=65, which is around -3.5dBm (already < 0 dBm) */
 | 
				
			||||||
@@ -122,8 +99,8 @@ static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
 | 
				
			|||||||
		enum lms_dev_type dev_type = it->first;
 | 
							enum lms_dev_type dev_type = it->first;
 | 
				
			||||||
		struct dev_desc desc = it->second;
 | 
							struct dev_desc desc = it->second;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		if (strncmp(device_info->deviceName, desc.name_prefix.c_str(), desc.name_prefix.length()) == 0) {
 | 
							if (strncmp(device_info->deviceName, desc.desc_str.c_str(), desc.desc_str.length()) == 0) {
 | 
				
			||||||
			LOGC(DDEV, INFO) << "Device identified as " << desc.name_prefix;
 | 
								LOGC(DDEV, INFO) << "Device identified as " << desc.desc_str;
 | 
				
			||||||
			return dev_type;
 | 
								return dev_type;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		it++;
 | 
							it++;
 | 
				
			||||||
@@ -131,12 +108,10 @@ static enum lms_dev_type parse_dev_type(lms_device_t *m_lms_dev)
 | 
				
			|||||||
	return LMS_DEV_UNKNOWN;
 | 
						return LMS_DEV_UNKNOWN;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
 | 
					LMSDevice::LMSDevice(InterfaceType iface, const struct trx_cfg *cfg)
 | 
				
			||||||
		     const std::vector<std::string>& tx_paths,
 | 
						: RadioDevice(iface, cfg),
 | 
				
			||||||
		     const std::vector<std::string>& rx_paths):
 | 
						  band_manager(m_dev_type, dev_band_nom_power_param_map, dev_param_map, {LMS_DEV_SDR_USB, GSM_BAND_850}), m_lms_dev(NULL),
 | 
				
			||||||
		     RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
 | 
						  started(false), m_dev_type(LMS_DEV_UNKNOWN)
 | 
				
			||||||
		     m_lms_dev(NULL), started(false), band_ass_curr_sess(false), band((enum gsm_band)0),
 | 
					 | 
				
			||||||
		     m_dev_type(LMS_DEV_UNKNOWN)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	LOGC(DDEV, INFO) << "creating LMS device...";
 | 
						LOGC(DDEV, INFO) << "creating LMS device...";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -223,48 +198,7 @@ int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::str
 | 
				
			|||||||
	return -1;
 | 
						return -1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void LMSDevice::assign_band_desc(enum gsm_band req_band)
 | 
					int LMSDevice::open()
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	dev_band_map_it it;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	it = dev_band_nom_power_param_map.find(dev_band_key(m_dev_type, req_band));
 | 
					 | 
				
			||||||
	if (it == dev_band_nom_power_param_map.end()) {
 | 
					 | 
				
			||||||
		dev_desc desc = dev_param_map.at(m_dev_type);
 | 
					 | 
				
			||||||
		LOGC(DDEV, ERROR) << "No Tx Power measurements exist for device "
 | 
					 | 
				
			||||||
				    << desc.name_prefix << " on band " << gsm_band_name(req_band)
 | 
					 | 
				
			||||||
				    << ", using LimeSDR-USB ones as fallback";
 | 
					 | 
				
			||||||
		it = dev_band_nom_power_param_map.find(dev_band_key(LMS_DEV_SDR_USB, req_band));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	OSMO_ASSERT(it != dev_band_nom_power_param_map.end());
 | 
					 | 
				
			||||||
	band_desc = it->second;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool LMSDevice::set_band(enum gsm_band req_band)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (band_ass_curr_sess && req_band != band) {
 | 
					 | 
				
			||||||
		LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
 | 
					 | 
				
			||||||
				  << " different from previous band " << gsm_band_name(band);
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (req_band != band) {
 | 
					 | 
				
			||||||
		band = req_band;
 | 
					 | 
				
			||||||
		assign_band_desc(band);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	band_ass_curr_sess = true;
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void LMSDevice::get_dev_band_desc(dev_band_desc& desc)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (band == 0) {
 | 
					 | 
				
			||||||
		LOGC(DDEV, ERROR) << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
 | 
					 | 
				
			||||||
		assign_band_desc(GSM_BAND_900);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	desc = band_desc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	lms_info_str_t* info_list;
 | 
						lms_info_str_t* info_list;
 | 
				
			||||||
	lms_range_t range_sr;
 | 
						lms_range_t range_sr;
 | 
				
			||||||
@@ -292,9 +226,9 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
				
			|||||||
	for (i = 0; i < n; i++)
 | 
						for (i = 0; i < n; i++)
 | 
				
			||||||
		LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i];
 | 
							LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dev_id = info_list_find(info_list, n, args);
 | 
						dev_id = info_list_find(info_list, n, cfg->dev_args);
 | 
				
			||||||
	if (dev_id == -1) {
 | 
						if (dev_id == -1) {
 | 
				
			||||||
		LOGC(DDEV, ERROR) << "No LMS device found with address '" << args << "'";
 | 
							LOGC(DDEV, ERROR) << "No LMS device found with address '" << cfg->dev_args << "'";
 | 
				
			||||||
		delete[] info_list;
 | 
							delete[] info_list;
 | 
				
			||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -311,14 +245,15 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	m_dev_type = parse_dev_type(m_lms_dev);
 | 
						m_dev_type = parse_dev_type(m_lms_dev);
 | 
				
			||||||
	dev_desc = dev_param_map.at(m_dev_type);
 | 
						dev_desc = dev_param_map.at(m_dev_type);
 | 
				
			||||||
 | 
						update_band_dev(m_dev_type);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((ref != REF_EXTERNAL) && (ref != REF_INTERNAL)){
 | 
						if ((cfg->clock_ref != REF_EXTERNAL) && (cfg->clock_ref != REF_INTERNAL)) {
 | 
				
			||||||
		LOGC(DDEV, ERROR) << "Invalid reference type";
 | 
							LOGC(DDEV, ERROR) << "Invalid reference type";
 | 
				
			||||||
		goto out_close;
 | 
							goto out_close;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* if reference clock is external, setup must happen _before_ calling LMS_Init */
 | 
						/* if reference clock is external, setup must happen _before_ calling LMS_Init */
 | 
				
			||||||
	if (ref == REF_EXTERNAL) {
 | 
						if (cfg->clock_ref == REF_EXTERNAL) {
 | 
				
			||||||
		LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz";
 | 
							LOGC(DDEV, INFO) << "Setting External clock reference to 10MHz";
 | 
				
			||||||
		/* FIXME: Assume an external 10 MHz reference clock. make
 | 
							/* FIXME: Assume an external 10 MHz reference clock. make
 | 
				
			||||||
		   external reference frequency configurable */
 | 
							   external reference frequency configurable */
 | 
				
			||||||
@@ -333,7 +268,7 @@ int LMSDevice::open(const std::string &args, int ref, bool swap_channels)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* if reference clock is internal, setup must happen _after_ calling LMS_Init */
 | 
						/* if reference clock is internal, setup must happen _after_ calling LMS_Init */
 | 
				
			||||||
	if (ref == REF_INTERNAL) {
 | 
						if (cfg->clock_ref == REF_INTERNAL) {
 | 
				
			||||||
		LOGC(DDEV, INFO) << "Setting Internal clock reference";
 | 
							LOGC(DDEV, INFO) << "Setting Internal clock reference";
 | 
				
			||||||
		/* Internal freq param is not used */
 | 
							/* Internal freq param is not used */
 | 
				
			||||||
		if (!do_clock_src_freq(REF_INTERNAL, 0))
 | 
							if (!do_clock_src_freq(REF_INTERNAL, 0))
 | 
				
			||||||
@@ -469,7 +404,7 @@ bool LMSDevice::stop()
 | 
				
			|||||||
		LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]);
 | 
							LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	band_ass_curr_sess = false;
 | 
						band_reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	started = false;
 | 
						started = false;
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
@@ -486,8 +421,8 @@ bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
 | 
				
			|||||||
		break;
 | 
							break;
 | 
				
			||||||
	case REF_INTERNAL:
 | 
						case REF_INTERNAL:
 | 
				
			||||||
		if (!dev_desc.clock_src_int_usable) {
 | 
							if (!dev_desc.clock_src_int_usable) {
 | 
				
			||||||
			LOGC(DDEV, ERROR) << "Device type " << dev_desc.name_prefix
 | 
								LOGC(DDEV, ERROR)
 | 
				
			||||||
					  << " doesn't support internal reference clock";
 | 
									<< "Device type " << dev_desc.desc_str << " doesn't support internal reference clock";
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		/* According to lms using LMS_CLOCK_EXTREF with a
 | 
							/* According to lms using LMS_CLOCK_EXTREF with a
 | 
				
			||||||
@@ -505,8 +440,8 @@ bool LMSDevice::do_clock_src_freq(enum ReferenceType ref, double freq)
 | 
				
			|||||||
		if (LMS_SetClockFreq(m_lms_dev, lms_clk_id, freq) < 0)
 | 
							if (LMS_SetClockFreq(m_lms_dev, lms_clk_id, freq) < 0)
 | 
				
			||||||
			return false;
 | 
								return false;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		LOGC(DDEV, INFO) << "Device type " << dev_desc.name_prefix
 | 
							LOGC(DDEV, INFO)
 | 
				
			||||||
				 << " doesn't support switching clock source through SW";
 | 
								<< "Device type " << dev_desc.desc_str << " doesn't support switching clock source through SW";
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
@@ -999,9 +934,6 @@ bool LMSDevice::updateAlignment(TIMESTAMP timestamp)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
bool LMSDevice::setTxFreq(double wFreq, size_t chan)
 | 
					bool LMSDevice::setTxFreq(double wFreq, size_t chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint16_t req_arfcn;
 | 
					 | 
				
			||||||
	enum gsm_band req_band;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (chan >= chans) {
 | 
						if (chan >= chans) {
 | 
				
			||||||
		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
							LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
@@ -1009,18 +941,7 @@ bool LMSDevice::setTxFreq(double wFreq, size_t chan)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz";
 | 
						LOGCHAN(chan, DDEV, NOTICE) << "Setting Tx Freq to " << wFreq << " Hz";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
 | 
						if (!update_band_from_freq(wFreq, chan, true))
 | 
				
			||||||
	if (req_arfcn == 0xffff) {
 | 
					 | 
				
			||||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
 | 
					 | 
				
			||||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
 | 
					 | 
				
			||||||
					   << " Hz (ARFCN " << req_arfcn << " )";
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!set_band(req_band))
 | 
					 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
 | 
						if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) {
 | 
				
			||||||
@@ -1033,23 +954,9 @@ bool LMSDevice::setTxFreq(double wFreq, size_t chan)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
bool LMSDevice::setRxFreq(double wFreq, size_t chan)
 | 
					bool LMSDevice::setRxFreq(double wFreq, size_t chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint16_t req_arfcn;
 | 
					 | 
				
			||||||
	enum gsm_band req_band;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOGCHAN(chan, DDEV, NOTICE) << "Setting Rx Freq to " << wFreq << " Hz";
 | 
						LOGCHAN(chan, DDEV, NOTICE) << "Setting Rx Freq to " << wFreq << " Hz";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
 | 
						if (!update_band_from_freq(wFreq, chan, false))
 | 
				
			||||||
	if (req_arfcn == 0xffff) {
 | 
					 | 
				
			||||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
 | 
					 | 
				
			||||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq
 | 
					 | 
				
			||||||
					   << " Hz (ARFCN " << req_arfcn << " )";
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!set_band(req_band))
 | 
					 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) {
 | 
						if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) {
 | 
				
			||||||
@@ -1060,18 +967,15 @@ bool LMSDevice::setRxFreq(double wFreq, size_t chan)
 | 
				
			|||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
 | 
					RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
 | 
				
			||||||
			       InterfaceType iface, size_t chans, double lo_offset,
 | 
					 | 
				
			||||||
			       const std::vector < std::string > &tx_paths,
 | 
					 | 
				
			||||||
			       const std::vector < std::string > &rx_paths)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (tx_sps != rx_sps) {
 | 
						if (cfg->tx_sps != cfg->rx_sps) {
 | 
				
			||||||
		LOGC(DDEV, ERROR) << "LMS Requires tx_sps == rx_sps";
 | 
							LOGC(DDEV, ERROR) << "LMS requires tx_sps == rx_sps";
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if (lo_offset != 0.0) {
 | 
						if (cfg->offset != 0.0) {
 | 
				
			||||||
		LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset";
 | 
							LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset";
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return new LMSDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
 | 
						return new LMSDevice(type, cfg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -18,11 +18,13 @@
 | 
				
			|||||||
#ifndef _LMS_DEVICE_H_
 | 
					#ifndef _LMS_DEVICE_H_
 | 
				
			||||||
#define _LMS_DEVICE_H_
 | 
					#define _LMS_DEVICE_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <map>
 | 
				
			||||||
#ifdef HAVE_CONFIG_H
 | 
					#ifdef HAVE_CONFIG_H
 | 
				
			||||||
#include "config.h"
 | 
					#include "config.h"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "radioDevice.h"
 | 
					#include "radioDevice.h"
 | 
				
			||||||
 | 
					#include "bandmanager.h"
 | 
				
			||||||
#include "smpl_buf.h"
 | 
					#include "smpl_buf.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include <sys/time.h>
 | 
					#include <sys/time.h>
 | 
				
			||||||
@@ -69,8 +71,35 @@ struct dev_band_desc {
 | 
				
			|||||||
	double rxgain2rssioffset_rel; /* dB */
 | 
						double rxgain2rssioffset_rel; /* dB */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Device parameter descriptor */
 | 
				
			||||||
 | 
					struct dev_desc {
 | 
				
			||||||
 | 
						/* Does LimeSuite allow switching the clock source for this device?
 | 
				
			||||||
 | 
						 * LimeSDR-Mini does not have switches but needs soldering to select
 | 
				
			||||||
 | 
						 * external/internal clock. Any call to LMS_SetClockFreq() will fail.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						bool clock_src_switchable;
 | 
				
			||||||
 | 
						/* Does LimeSuite allow using REF_INTERNAL for this device?
 | 
				
			||||||
 | 
						 * LimeNET-Micro does not like selecting internal clock
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						bool clock_src_int_usable;
 | 
				
			||||||
 | 
						/* Sample rate coef (without having TX/RX samples per symbol into account) */
 | 
				
			||||||
 | 
						double rate;
 | 
				
			||||||
 | 
						/* Sample rate coef (without having TX/RX samples per symbol into account), if multi-arfcn is enabled */
 | 
				
			||||||
 | 
						double rate_multiarfcn;
 | 
				
			||||||
 | 
						/* Coefficient multiplied by TX sample rate in order to shift Tx time */
 | 
				
			||||||
 | 
						double ts_offset_coef;
 | 
				
			||||||
 | 
						/* Coefficient multiplied by TX sample rate in order to shift Tx time, if multi-arfcn is enabled */
 | 
				
			||||||
 | 
						double ts_offset_coef_multiarfcn;
 | 
				
			||||||
 | 
						/* Device Name Prefix as presented by LimeSuite API LMS_GetDeviceInfo() */
 | 
				
			||||||
 | 
						std::string desc_str;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using dev_band_key_t = std::tuple<lms_dev_type, gsm_band>;
 | 
				
			||||||
 | 
					using power_map_t = std::map<dev_band_key_t, dev_band_desc>;
 | 
				
			||||||
 | 
					using dev_map_t = std::map<lms_dev_type, struct dev_desc>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** A class to handle a LimeSuite supported device */
 | 
					/** A class to handle a LimeSuite supported device */
 | 
				
			||||||
class LMSDevice:public RadioDevice {
 | 
					class LMSDevice:public RadioDevice, public band_manager<power_map_t, dev_map_t> {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	lms_device_t *m_lms_dev;
 | 
						lms_device_t *m_lms_dev;
 | 
				
			||||||
@@ -87,9 +116,6 @@ private:
 | 
				
			|||||||
	TIMESTAMP ts_initial, ts_offset;
 | 
						TIMESTAMP ts_initial, ts_offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	std::vector<double> tx_gains, rx_gains;
 | 
						std::vector<double> tx_gains, rx_gains;
 | 
				
			||||||
	bool band_ass_curr_sess; /* true if  "band" was set after last POWEROFF */
 | 
					 | 
				
			||||||
	enum gsm_band band;
 | 
					 | 
				
			||||||
	struct dev_band_desc band_desc;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	enum lms_dev_type m_dev_type;
 | 
						enum lms_dev_type m_dev_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -101,29 +127,25 @@ private:
 | 
				
			|||||||
	void update_stream_stats_rx(size_t chan, bool *overrun);
 | 
						void update_stream_stats_rx(size_t chan, bool *overrun);
 | 
				
			||||||
	void update_stream_stats_tx(size_t chan, bool *underrun);
 | 
						void update_stream_stats_tx(size_t chan, bool *underrun);
 | 
				
			||||||
	bool do_clock_src_freq(enum ReferenceType ref, double freq);
 | 
						bool do_clock_src_freq(enum ReferenceType ref, double freq);
 | 
				
			||||||
	void get_dev_band_desc(dev_band_desc& desc);
 | 
					 | 
				
			||||||
	bool set_band(enum gsm_band req_band);
 | 
					 | 
				
			||||||
	void assign_band_desc(enum gsm_band req_band);
 | 
					 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Object constructor */
 | 
						/** Object constructor */
 | 
				
			||||||
	LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
 | 
					    LMSDevice(InterfaceType iface, const struct trx_cfg *cfg);
 | 
				
			||||||
		  const std::vector<std::string>& tx_paths,
 | 
					    ~LMSDevice();
 | 
				
			||||||
		  const std::vector<std::string>& rx_paths);
 | 
					 | 
				
			||||||
	~LMSDevice();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Instantiate the LMS */
 | 
					    /** Instantiate the LMS */
 | 
				
			||||||
	int open(const std::string &args, int ref, bool swap_channels);
 | 
					    int open();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Start the LMS */
 | 
					    /** Start the LMS */
 | 
				
			||||||
	bool start();
 | 
					    bool start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Stop the LMS */
 | 
					    /** Stop the LMS */
 | 
				
			||||||
	bool stop();
 | 
					    bool stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	enum TxWindowType getWindowType() {
 | 
					    enum TxWindowType getWindowType()
 | 
				
			||||||
		return TX_WINDOW_LMS1;
 | 
					    {
 | 
				
			||||||
	}
 | 
						    return TX_WINDOW_LMS1;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
	Read samples from the LMS.
 | 
						Read samples from the LMS.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -91,19 +91,7 @@ extern "C" {
 | 
				
			|||||||
 *   USRP1 with timestamps is not supported by UHD.
 | 
					 *   USRP1 with timestamps is not supported by UHD.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Device Type, Tx-SPS, Rx-SPS */
 | 
					static const dev_map_t dev_param_map {
 | 
				
			||||||
typedef std::tuple<uhd_dev_type, int, int> dev_key;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Device parameter descriptor */
 | 
					 | 
				
			||||||
struct dev_desc {
 | 
					 | 
				
			||||||
	unsigned channels;
 | 
					 | 
				
			||||||
	double mcr;
 | 
					 | 
				
			||||||
	double rate;
 | 
					 | 
				
			||||||
	double offset;
 | 
					 | 
				
			||||||
	std::string str;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static const std::map<dev_key, dev_desc> dev_param_map {
 | 
					 | 
				
			||||||
	{ std::make_tuple(USRP2, 1, 1), { 1, 0.0,  390625,  1.2184e-4,  "N2XX 1 SPS"         } },
 | 
						{ std::make_tuple(USRP2, 1, 1), { 1, 0.0,  390625,  1.2184e-4,  "N2XX 1 SPS"         } },
 | 
				
			||||||
	{ std::make_tuple(USRP2, 4, 1), { 1, 0.0,  390625,  7.6547e-5,  "N2XX 4/1 Tx/Rx SPS" } },
 | 
						{ std::make_tuple(USRP2, 4, 1), { 1, 0.0,  390625,  7.6547e-5,  "N2XX 4/1 Tx/Rx SPS" } },
 | 
				
			||||||
	{ std::make_tuple(USRP2, 4, 4), { 1, 0.0,  390625,  4.6080e-5,  "N2XX 4 SPS"         } },
 | 
						{ std::make_tuple(USRP2, 4, 4), { 1, 0.0,  390625,  4.6080e-5,  "N2XX 4 SPS"         } },
 | 
				
			||||||
@@ -129,9 +117,7 @@ static const std::map<dev_key, dev_desc> dev_param_map {
 | 
				
			|||||||
	{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
 | 
						{ std::make_tuple(B2XX_MCBTS, 4, 4), { 1, 51.2e6, MCBTS_SPACING*4, B2XX_TIMING_MCBTS, "B200/B210 4 SPS Multi-ARFCN" } },
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
typedef std::tuple<uhd_dev_type, enum gsm_band> dev_band_key;
 | 
					static const power_map_t dev_band_nom_power_param_map {
 | 
				
			||||||
typedef std::map<dev_band_key, dev_band_desc>::const_iterator dev_band_map_it;
 | 
					 | 
				
			||||||
static const std::map<dev_band_key, dev_band_desc> dev_band_nom_power_param_map {
 | 
					 | 
				
			||||||
	{ std::make_tuple(B200, GSM_BAND_850),	{ 89.75, 13.3, -7.5  } },
 | 
						{ std::make_tuple(B200, GSM_BAND_850),	{ 89.75, 13.3, -7.5  } },
 | 
				
			||||||
	{ std::make_tuple(B200, GSM_BAND_900),	{ 89.75, 13.3, -7.5  } },
 | 
						{ std::make_tuple(B200, GSM_BAND_900),	{ 89.75, 13.3, -7.5  } },
 | 
				
			||||||
	{ std::make_tuple(B200, GSM_BAND_1800),	{ 89.75, 7.5,  -11.0 } },
 | 
						{ std::make_tuple(B200, GSM_BAND_1800),	{ 89.75, 7.5,  -11.0 } },
 | 
				
			||||||
@@ -220,15 +206,10 @@ static double TxPower2TxGain(const dev_band_desc &desc, double tx_power_dbm)
 | 
				
			|||||||
	return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
 | 
						return desc.nom_uhd_tx_gain - (desc.nom_out_tx_power - tx_power_dbm);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
 | 
					uhd_device::uhd_device(InterfaceType iface, const struct trx_cfg *cfg)
 | 
				
			||||||
		       InterfaceType iface, size_t chan_num, double lo_offset,
 | 
						: RadioDevice(iface, cfg), band_manager(dev_band_nom_power_param_map, dev_param_map), rx_gain_min(0.0),
 | 
				
			||||||
		       const std::vector<std::string>& tx_paths,
 | 
						  rx_gain_max(0.0), tx_spp(0), rx_spp(0), started(false), aligned(false), drop_cnt(0), prev_ts(0, 0),
 | 
				
			||||||
		       const std::vector<std::string>& rx_paths)
 | 
						  ts_initial(0), ts_offset(0), async_event_thrd(NULL)
 | 
				
			||||||
	: RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths),
 | 
					 | 
				
			||||||
	  rx_gain_min(0.0), rx_gain_max(0.0), band_ass_curr_sess(false),
 | 
					 | 
				
			||||||
	  band((enum gsm_band)0), tx_spp(0), rx_spp(0),
 | 
					 | 
				
			||||||
	  started(false), aligned(false), drop_cnt(0),
 | 
					 | 
				
			||||||
	  prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -240,47 +221,6 @@ uhd_device::~uhd_device()
 | 
				
			|||||||
		delete rx_buffers[i];
 | 
							delete rx_buffers[i];
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void uhd_device::assign_band_desc(enum gsm_band req_band)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	dev_band_map_it it;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	it = dev_band_nom_power_param_map.find(dev_band_key(dev_type, req_band));
 | 
					 | 
				
			||||||
	if (it == dev_band_nom_power_param_map.end()) {
 | 
					 | 
				
			||||||
		dev_desc desc = dev_param_map.at(dev_key(dev_type, tx_sps, rx_sps));
 | 
					 | 
				
			||||||
		LOGC(DDEV, ERROR) << "No Power parameters exist for device "
 | 
					 | 
				
			||||||
				    << desc.str << " on band " << gsm_band_name(req_band)
 | 
					 | 
				
			||||||
				    << ", using B210 ones as fallback";
 | 
					 | 
				
			||||||
		it = dev_band_nom_power_param_map.find(dev_band_key(B210, req_band));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	OSMO_ASSERT(it != dev_band_nom_power_param_map.end())
 | 
					 | 
				
			||||||
	band_desc = it->second;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
bool uhd_device::set_band(enum gsm_band req_band)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (band_ass_curr_sess && req_band != band) {
 | 
					 | 
				
			||||||
		LOGC(DDEV, ALERT) << "Requesting band " << gsm_band_name(req_band)
 | 
					 | 
				
			||||||
				  << " different from previous band " << gsm_band_name(band);
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (req_band != band) {
 | 
					 | 
				
			||||||
		band = req_band;
 | 
					 | 
				
			||||||
		assign_band_desc(band);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	band_ass_curr_sess = true;
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void uhd_device::get_dev_band_desc(dev_band_desc& desc)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (band == 0) {
 | 
					 | 
				
			||||||
		LOGC(DDEV, ERROR) << "Power parameters requested before Tx Frequency was set! Providing band 900 by default...";
 | 
					 | 
				
			||||||
		assign_band_desc(GSM_BAND_900);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	desc = band_desc;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void uhd_device::init_gains()
 | 
					void uhd_device::init_gains()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	double tx_gain_min, tx_gain_max;
 | 
						double tx_gain_min, tx_gain_max;
 | 
				
			||||||
@@ -345,7 +285,7 @@ void uhd_device::set_rates()
 | 
				
			|||||||
	rx_rate = usrp_dev->get_rx_rate();
 | 
						rx_rate = usrp_dev->get_rx_rate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
 | 
						ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate);
 | 
				
			||||||
	LOGC(DDEV, INFO) << "Rates configured for " << desc.str;
 | 
						LOGC(DDEV, INFO) << "Rates configured for " << desc.desc_str;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
double uhd_device::setRxGain(double db, size_t chan)
 | 
					double uhd_device::setRxGain(double db, size_t chan)
 | 
				
			||||||
@@ -355,6 +295,9 @@ double uhd_device::setRxGain(double db, size_t chan)
 | 
				
			|||||||
		return 0.0f;
 | 
							return 0.0f;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (cfg->overrides.ul_gain_override)
 | 
				
			||||||
 | 
							return rx_gains[chan];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	usrp_dev->set_rx_gain(db, chan);
 | 
						usrp_dev->set_rx_gain(db, chan);
 | 
				
			||||||
	rx_gains[chan] = usrp_dev->get_rx_gain(chan);
 | 
						rx_gains[chan] = usrp_dev->get_rx_gain(chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -397,6 +340,9 @@ double uhd_device::setPowerAttenuation(int atten, size_t chan) {
 | 
				
			|||||||
		return 0.0f;
 | 
							return 0.0f;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (cfg->overrides.dl_gain_override)
 | 
				
			||||||
 | 
							return atten; // ensures caller does not apply digital attenuation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	get_dev_band_desc(desc);
 | 
						get_dev_band_desc(desc);
 | 
				
			||||||
	tx_power = desc.nom_out_tx_power - atten;
 | 
						tx_power = desc.nom_out_tx_power - atten;
 | 
				
			||||||
	db = TxPower2TxGain(desc, tx_power);
 | 
						db = TxPower2TxGain(desc, tx_power);
 | 
				
			||||||
@@ -548,7 +494,7 @@ void uhd_device::set_channels(bool swap)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
					int uhd_device::open()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	const char *refstr;
 | 
						const char *refstr;
 | 
				
			||||||
	int clock_lock_attempts = 15;
 | 
						int clock_lock_attempts = 15;
 | 
				
			||||||
@@ -564,10 +510,10 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Find UHD devices
 | 
						// Find UHD devices
 | 
				
			||||||
	uhd::device_addr_t addr(args);
 | 
						uhd::device_addr_t addr(cfg->dev_args);
 | 
				
			||||||
	uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
 | 
						uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
 | 
				
			||||||
	if (dev_addrs.size() == 0) {
 | 
						if (dev_addrs.size() == 0) {
 | 
				
			||||||
		LOGC(DDEV, ALERT) << "No UHD devices found with address '" << args << "'";
 | 
							LOGC(DDEV, ALERT) << "No UHD devices found with address '" << cfg->dev_args << "'";
 | 
				
			||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -576,7 +522,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
				
			|||||||
	try {
 | 
						try {
 | 
				
			||||||
		usrp_dev = uhd::usrp::multi_usrp::make(addr);
 | 
							usrp_dev = uhd::usrp::multi_usrp::make(addr);
 | 
				
			||||||
	} catch(uhd::key_error::exception &e) {
 | 
						} catch(uhd::key_error::exception &e) {
 | 
				
			||||||
		LOGC(DDEV, ALERT) << "UHD make failed, device " << args << ", exception:\n" << e.what();
 | 
							LOGC(DDEV, ALERT) << "UHD make failed, device " << cfg->dev_args << ", exception:\n" << e.what();
 | 
				
			||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -584,14 +530,16 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
				
			|||||||
	if (!parse_dev_type())
 | 
						if (!parse_dev_type())
 | 
				
			||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						update_band_dev(dev_key(dev_type, tx_sps, rx_sps));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
 | 
						if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) {
 | 
				
			||||||
		LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater";
 | 
							LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater";
 | 
				
			||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	try {
 | 
						try {
 | 
				
			||||||
		set_channels(swap_channels);
 | 
							set_channels(cfg->swap_channels);
 | 
				
			||||||
        } catch (const std::exception &e) {
 | 
						} catch (const std::exception &e) {
 | 
				
			||||||
		LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
 | 
							LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what();
 | 
				
			||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -607,7 +555,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
				
			|||||||
	rx_gains.resize(chans);
 | 
						rx_gains.resize(chans);
 | 
				
			||||||
	rx_buffers.resize(chans);
 | 
						rx_buffers.resize(chans);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	switch (ref) {
 | 
						switch (cfg->clock_ref) {
 | 
				
			||||||
	case REF_INTERNAL:
 | 
						case REF_INTERNAL:
 | 
				
			||||||
		refstr = "internal";
 | 
							refstr = "internal";
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
@@ -686,6 +634,32 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels)
 | 
				
			|||||||
	// Print configuration
 | 
						// Print configuration
 | 
				
			||||||
	LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
 | 
						LOGC(DDEV, INFO) << "Device configuration: " << usrp_dev->get_pp_string();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (cfg->overrides.dl_freq_override) {
 | 
				
			||||||
 | 
							uhd::tune_request_t treq_tx = uhd::tune_request_t(cfg->overrides.dl_freq, 0);
 | 
				
			||||||
 | 
							auto tres = usrp_dev->set_tx_freq(treq_tx, 0);
 | 
				
			||||||
 | 
							tx_freqs[0] = usrp_dev->get_tx_freq(0);
 | 
				
			||||||
 | 
							LOGCHAN(0, DDEV, INFO) << "OVERRIDE set_freq(" << tx_freqs[0] << ", TX): " << tres.to_pp_string() << std::endl;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (cfg->overrides.ul_freq_override) {
 | 
				
			||||||
 | 
							uhd::tune_request_t treq_rx = uhd::tune_request_t(cfg->overrides.ul_freq, 0);
 | 
				
			||||||
 | 
							auto tres = usrp_dev->set_rx_freq(treq_rx, 0);
 | 
				
			||||||
 | 
							rx_freqs[0] = usrp_dev->get_rx_freq(0);
 | 
				
			||||||
 | 
							LOGCHAN(0, DDEV, INFO) << "OVERRIDE set_freq(" << rx_freqs[0] << ", RX): " << tres.to_pp_string() << std::endl;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (cfg->overrides.ul_gain_override) {
 | 
				
			||||||
 | 
							usrp_dev->set_rx_gain(cfg->overrides.ul_gain, 0);
 | 
				
			||||||
 | 
							rx_gains[0] = usrp_dev->get_rx_gain(0);
 | 
				
			||||||
 | 
							LOGCHAN(0, DDEV, INFO) << " OVERRIDE RX gain:" << rx_gains[0] << std::endl;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (cfg->overrides.dl_gain_override) {
 | 
				
			||||||
 | 
							usrp_dev->set_tx_gain(cfg->overrides.dl_gain, 0);
 | 
				
			||||||
 | 
							tx_gains[0] = usrp_dev->get_tx_gain(0);
 | 
				
			||||||
 | 
							LOGCHAN(0, DDEV, INFO) << " OVERRIDE TX gain:" << tx_gains[0] << std::endl;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (iface == MULTI_ARFCN)
 | 
						if (iface == MULTI_ARFCN)
 | 
				
			||||||
		return MULTI_ARFCN;
 | 
							return MULTI_ARFCN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -796,7 +770,7 @@ bool uhd_device::stop()
 | 
				
			|||||||
	for (size_t i = 0; i < rx_buffers.size(); i++)
 | 
						for (size_t i = 0; i < rx_buffers.size(); i++)
 | 
				
			||||||
		rx_buffers[i]->reset();
 | 
							rx_buffers[i]->reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	band_ass_curr_sess = false;
 | 
						band_reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	started = false;
 | 
						started = false;
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
@@ -1036,17 +1010,22 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	std::vector<double> freqs;
 | 
						std::vector<double> freqs;
 | 
				
			||||||
	uhd::tune_result_t tres;
 | 
						uhd::tune_result_t tres;
 | 
				
			||||||
 | 
						std::string str_dir = tx ? "Tx" : "Rx";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (cfg->overrides.dl_freq_override || cfg->overrides.ul_freq_override)
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!update_band_from_freq(freq, chan, tx))
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uhd::tune_request_t treq = select_freq(freq, chan, tx);
 | 
						uhd::tune_request_t treq = select_freq(freq, chan, tx);
 | 
				
			||||||
	std::string str_dir;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (tx) {
 | 
						if (tx) {
 | 
				
			||||||
		tres = usrp_dev->set_tx_freq(treq, chan);
 | 
							tres = usrp_dev->set_tx_freq(treq, chan);
 | 
				
			||||||
		tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
 | 
							tx_freqs[chan] = usrp_dev->get_tx_freq(chan);
 | 
				
			||||||
		str_dir = "Tx";
 | 
					 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		tres = usrp_dev->set_rx_freq(treq, chan);
 | 
							tres = usrp_dev->set_rx_freq(treq, chan);
 | 
				
			||||||
		rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
 | 
							rx_freqs[chan] = usrp_dev->get_rx_freq(chan);
 | 
				
			||||||
		str_dir = "Rx";
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
 | 
						LOGCHAN(chan, DDEV, INFO) << "set_freq(" << freq << ", " << str_dir << "): " << tres.to_pp_string() << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1076,59 +1055,20 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
bool uhd_device::setTxFreq(double wFreq, size_t chan)
 | 
					bool uhd_device::setTxFreq(double wFreq, size_t chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint16_t req_arfcn;
 | 
					 | 
				
			||||||
	enum gsm_band req_band;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (chan >= tx_freqs.size()) {
 | 
						if (chan >= tx_freqs.size()) {
 | 
				
			||||||
		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
							LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ScopedLock lock(tune_lock);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100 , 0);
 | 
						return set_freq(wFreq, chan, true);
 | 
				
			||||||
	if (req_arfcn == 0xffff) {
 | 
					 | 
				
			||||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Tx Frequency " << wFreq / 1000 << " kHz";
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
 | 
					 | 
				
			||||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Tx Frequency " << wFreq
 | 
					 | 
				
			||||||
					   << " Hz (ARFCN " << req_arfcn << " )";
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!set_band(req_band))
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!set_freq(wFreq, chan, true))
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return true;
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool uhd_device::setRxFreq(double wFreq, size_t chan)
 | 
					bool uhd_device::setRxFreq(double wFreq, size_t chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uint16_t req_arfcn;
 | 
					 | 
				
			||||||
	enum gsm_band req_band;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (chan >= rx_freqs.size()) {
 | 
						if (chan >= rx_freqs.size()) {
 | 
				
			||||||
		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
							LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan;
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	ScopedLock lock(tune_lock);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	req_arfcn = gsm_freq102arfcn(wFreq / 1000 / 100, 1);
 | 
					 | 
				
			||||||
	if (req_arfcn == 0xffff) {
 | 
					 | 
				
			||||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown ARFCN for Rx Frequency " << wFreq / 1000 << " kHz";
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	if (gsm_arfcn2band_rc(req_arfcn, &req_band) < 0) {
 | 
					 | 
				
			||||||
		LOGCHAN(chan, DDEV, ALERT) << "Unknown GSM band for Rx Frequency " << wFreq
 | 
					 | 
				
			||||||
					   << " Hz (ARFCN " << req_arfcn << " )";
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!set_band(req_band))
 | 
					 | 
				
			||||||
		return false;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return set_freq(wFreq, chan, false);
 | 
						return set_freq(wFreq, chan, false);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -1378,11 +1318,8 @@ std::string uhd_device::str_code(uhd::async_metadata_t metadata)
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef IPCMAGIC
 | 
					#ifndef IPCMAGIC
 | 
				
			||||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
 | 
					RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
 | 
				
			||||||
			       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(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
 | 
						return new uhd_device(type, cfg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -30,6 +30,7 @@
 | 
				
			|||||||
#include "config.h"
 | 
					#include "config.h"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "bandmanager.h"
 | 
				
			||||||
#include "radioDevice.h"
 | 
					#include "radioDevice.h"
 | 
				
			||||||
#include "smpl_buf.h"
 | 
					#include "smpl_buf.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -71,6 +72,19 @@ struct dev_band_desc {
 | 
				
			|||||||
	double rxgain2rssioffset_rel; /* dB */
 | 
						double rxgain2rssioffset_rel; /* dB */
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct dev_desc {
 | 
				
			||||||
 | 
						unsigned channels;
 | 
				
			||||||
 | 
						double mcr;
 | 
				
			||||||
 | 
						double rate;
 | 
				
			||||||
 | 
						double offset;
 | 
				
			||||||
 | 
						std::string desc_str;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using dev_key = std::tuple<uhd_dev_type, int, int>;
 | 
				
			||||||
 | 
					using dev_band_key = std::tuple<uhd_dev_type, enum gsm_band>;
 | 
				
			||||||
 | 
					using power_map_t = std::map<dev_band_key, dev_band_desc>;
 | 
				
			||||||
 | 
					using dev_map_t = std::map<dev_key, dev_desc>;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
    uhd_device - UHD implementation of the Device interface. Timestamped samples
 | 
					    uhd_device - UHD implementation of the Device interface. Timestamped samples
 | 
				
			||||||
                are sent to and received from the device. An intermediate buffer
 | 
					                are sent to and received from the device. An intermediate buffer
 | 
				
			||||||
@@ -78,19 +92,19 @@ struct dev_band_desc {
 | 
				
			|||||||
                Events and errors such as underruns are reported asynchronously
 | 
					                Events and errors such as underruns are reported asynchronously
 | 
				
			||||||
                by the device and received in a separate thread.
 | 
					                by the device and received in a separate thread.
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
class uhd_device : public RadioDevice {
 | 
					class uhd_device : public RadioDevice, public band_manager<power_map_t, dev_map_t> {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	uhd_device(size_t tx_sps, size_t rx_sps, InterfaceType type,
 | 
					    uhd_device(InterfaceType iface, const struct trx_cfg *cfg);
 | 
				
			||||||
		   size_t chan_num, double offset,
 | 
					    ~uhd_device();
 | 
				
			||||||
		   const std::vector<std::string>& tx_paths,
 | 
					 | 
				
			||||||
		   const std::vector<std::string>& rx_paths);
 | 
					 | 
				
			||||||
	~uhd_device();
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int open(const std::string &args, int ref, bool swap_channels);
 | 
					    int open();
 | 
				
			||||||
	bool start();
 | 
					    bool start();
 | 
				
			||||||
	bool stop();
 | 
					    bool stop();
 | 
				
			||||||
	bool restart();
 | 
					    bool restart();
 | 
				
			||||||
	enum TxWindowType getWindowType() { return tx_window; }
 | 
					    enum TxWindowType getWindowType()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
						    return tx_window;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
						int readSamples(std::vector<short *> &bufs, int len, bool *overrun,
 | 
				
			||||||
			TIMESTAMP timestamp, bool *underrun);
 | 
								TIMESTAMP timestamp, bool *underrun);
 | 
				
			||||||
@@ -160,9 +174,6 @@ protected:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	std::vector<double> tx_gains, rx_gains;
 | 
						std::vector<double> tx_gains, rx_gains;
 | 
				
			||||||
	std::vector<double> tx_freqs, rx_freqs;
 | 
						std::vector<double> tx_freqs, rx_freqs;
 | 
				
			||||||
	bool band_ass_curr_sess; /* true if  "band" was set after last POWEROFF */
 | 
					 | 
				
			||||||
	enum gsm_band band;
 | 
					 | 
				
			||||||
	struct dev_band_desc band_desc;
 | 
					 | 
				
			||||||
	size_t tx_spp, rx_spp;
 | 
						size_t tx_spp, rx_spp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool started;
 | 
						bool started;
 | 
				
			||||||
@@ -191,10 +202,6 @@ protected:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
 | 
						uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
 | 
				
			||||||
	bool set_freq(double freq, size_t chan, bool tx);
 | 
						bool set_freq(double freq, size_t chan, bool tx);
 | 
				
			||||||
	void get_dev_band_desc(dev_band_desc& desc);
 | 
					 | 
				
			||||||
	bool set_band(enum gsm_band req_band);
 | 
					 | 
				
			||||||
	void assign_band_desc(enum gsm_band req_band);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Thread *async_event_thrd;
 | 
						Thread *async_event_thrd;
 | 
				
			||||||
	Mutex tune_lock;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -60,11 +60,7 @@ const dboardConfigType dboardConfig = TXA_RXB;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const double USRPDevice::masterClockRate = 52.0e6;
 | 
					const double USRPDevice::masterClockRate = 52.0e6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
 | 
					USRPDevice::USRPDevice(InterfaceType iface, const struct trx_cfg *cfg) : RadioDevice(iface, cfg)
 | 
				
			||||||
		       size_t chan_num, double lo_offset,
 | 
					 | 
				
			||||||
		       const std::vector<std::string>& tx_paths,
 | 
					 | 
				
			||||||
		       const std::vector<std::string>& rx_paths):
 | 
					 | 
				
			||||||
		RadioDevice(tx_sps, rx_sps, iface, chan_num, lo_offset, tx_paths, rx_paths)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  LOGC(DDEV, INFO) << "creating USRP device...";
 | 
					  LOGC(DDEV, INFO) << "creating USRP device...";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -94,7 +90,7 @@ USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface,
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int USRPDevice::open(const std::string &, int, bool)
 | 
					int USRPDevice::open()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  writeLock.unlock();
 | 
					  writeLock.unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -587,8 +583,7 @@ bool USRPDevice::updateAlignment(TIMESTAMP timestamp)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
#ifndef SWLOOPBACK
 | 
					#ifndef SWLOOPBACK
 | 
				
			||||||
  short data[] = {0x00,0x02,0x00,0x00};
 | 
					  short data[] = {0x00,0x02,0x00,0x00};
 | 
				
			||||||
  uint32_t *wordPtr = (uint32_t *) data;
 | 
					  /* FIXME: big endian */
 | 
				
			||||||
  *wordPtr = host_to_usrp_u32(*wordPtr);
 | 
					 | 
				
			||||||
  bool tmpUnderrun;
 | 
					  bool tmpUnderrun;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  std::vector<short *> buf(1, data);
 | 
					  std::vector<short *> buf(1, data);
 | 
				
			||||||
@@ -659,22 +654,19 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
 | 
				
			|||||||
bool USRPDevice::setRxFreq(double wFreq) { return true;};
 | 
					bool USRPDevice::setRxFreq(double wFreq) { return true;};
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
 | 
					RadioDevice *RadioDevice::make(InterfaceType type, const struct trx_cfg *cfg)
 | 
				
			||||||
			       InterfaceType iface, size_t chans, double lo_offset,
 | 
					 | 
				
			||||||
			       const std::vector<std::string>& tx_paths,
 | 
					 | 
				
			||||||
			       const std::vector<std::string>& rx_paths)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (tx_sps != rx_sps) {
 | 
					  if (cfg->tx_sps != cfg->rx_sps) {
 | 
				
			||||||
		LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
 | 
					    LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps";
 | 
				
			||||||
		return NULL;
 | 
					    return NULL;
 | 
				
			||||||
	}
 | 
					  }
 | 
				
			||||||
	if (chans != 1) {
 | 
					  if (cfg->num_chans != 1) {
 | 
				
			||||||
		LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
 | 
					    LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel";
 | 
				
			||||||
		return NULL;
 | 
					    return NULL;
 | 
				
			||||||
	}
 | 
					  }
 | 
				
			||||||
	if (lo_offset != 0.0) {
 | 
					  if (cfg->offset != 0.0) {
 | 
				
			||||||
		LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
 | 
					    LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset";
 | 
				
			||||||
		return NULL;
 | 
					    return NULL;
 | 
				
			||||||
	}
 | 
					  }
 | 
				
			||||||
	return new USRPDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths);
 | 
					  return new USRPDevice(type, cfg);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -104,20 +104,21 @@ private:
 | 
				
			|||||||
 public:
 | 
					 public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Object constructor */
 | 
					  /** Object constructor */
 | 
				
			||||||
  USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chan_num, double lo_offset,
 | 
					     USRPDevice(InterfaceType iface, const struct trx_cfg *cfg);
 | 
				
			||||||
		const std::vector<std::string>& tx_paths,
 | 
					 | 
				
			||||||
		const std::vector<std::string>& rx_paths);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Instantiate the USRP */
 | 
					     /** Instantiate the USRP */
 | 
				
			||||||
  int open(const std::string &, int, bool);
 | 
					     int open();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Start the USRP */
 | 
					     /** Start the USRP */
 | 
				
			||||||
  bool start();
 | 
					     bool start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Stop the USRP */
 | 
					     /** Stop the USRP */
 | 
				
			||||||
  bool stop();
 | 
					     bool stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
 | 
					     enum TxWindowType getWindowType()
 | 
				
			||||||
 | 
					     {
 | 
				
			||||||
 | 
						     return TX_WINDOW_USRP1;
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
	Read samples from the USRP.
 | 
						Read samples from the USRP.
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										149
									
								
								Transceiver52M/grgsm_vitac/constants.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										149
									
								
								Transceiver52M/grgsm_vitac/constants.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,149 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					/* -*- c++ -*- */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * @file
 | 
				
			||||||
 | 
					 * @author (C) 2009-2017  by Piotr Krysik <ptrkrysik@gmail.com>
 | 
				
			||||||
 | 
					 * @section LICENSE
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Gr-gsm is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3, or (at your option)
 | 
				
			||||||
 | 
					 * any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Gr-gsm is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with gr-gsm; see the file COPYING.  If not, write to
 | 
				
			||||||
 | 
					 * the Free Software Foundation, Inc., 51 Franklin Street,
 | 
				
			||||||
 | 
					 * Boston, MA 02110-1301, USA.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <complex>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define gr_complex std::complex<float>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define GSM_SYMBOL_RATE  (1625000.0/6.0) //symbols per second
 | 
				
			||||||
 | 
					#define GSM_SYMBOL_PERIOD (1.0/GSM_SYMBOL_RATE) //seconds per symbol
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//Burst timing
 | 
				
			||||||
 | 
					#define TAIL_BITS         3
 | 
				
			||||||
 | 
					#define GUARD_BITS        8
 | 
				
			||||||
 | 
					#define GUARD_FRACTIONAL  0.25 //fractional part of guard period
 | 
				
			||||||
 | 
					#define GUARD_PERIOD      GUARD_BITS + GUARD_FRACTIONAL
 | 
				
			||||||
 | 
					#define DATA_BITS         57   //size of 1 data block in normal burst
 | 
				
			||||||
 | 
					#define STEALING_BIT      1
 | 
				
			||||||
 | 
					#define N_TRAIN_BITS      26
 | 
				
			||||||
 | 
					#define N_SYNC_BITS       64
 | 
				
			||||||
 | 
					#define N_ACCESS_BITS     41
 | 
				
			||||||
 | 
					#define USEFUL_BITS       142  //(2*(DATA_BITS+STEALING_BIT) + N_TRAIN_BITS )
 | 
				
			||||||
 | 
					#define FCCH_BITS         USEFUL_BITS
 | 
				
			||||||
 | 
					#define BURST_SIZE        (USEFUL_BITS+2*TAIL_BITS)
 | 
				
			||||||
 | 
					#define ACCESS_BURST_SIZE 88
 | 
				
			||||||
 | 
					#define PROCESSED_CHUNK   BURST_SIZE+2*GUARD_PERIOD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SCH_DATA_LEN      39
 | 
				
			||||||
 | 
					#define TS_BITS           (TAIL_BITS+USEFUL_BITS+TAIL_BITS+GUARD_BITS)  //a full TS (156 bits)
 | 
				
			||||||
 | 
					#define TS_PER_FRAME      8
 | 
				
			||||||
 | 
					#define FRAME_BITS        (TS_PER_FRAME * TS_BITS + 2) // 156.25 * 8
 | 
				
			||||||
 | 
					#define FCCH_POS          TAIL_BITS
 | 
				
			||||||
 | 
					#define SYNC_POS          (TAIL_BITS + 39)
 | 
				
			||||||
 | 
					#define TRAIN_POS         ( TAIL_BITS + (DATA_BITS+STEALING_BIT) + 5) //first 5 bits of a training sequence
 | 
				
			||||||
 | 
																		   //aren't used for channel impulse response estimation
 | 
				
			||||||
 | 
					#define TRAIN_BEGINNING   5
 | 
				
			||||||
 | 
					#define SAFETY_MARGIN     6   //
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define FCCH_HITS_NEEDED        (USEFUL_BITS - 4)
 | 
				
			||||||
 | 
					#define FCCH_MAX_MISSES         1
 | 
				
			||||||
 | 
					#define FCCH_MAX_FREQ_OFFSET    100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CHAN_IMP_RESP_LENGTH  5
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define MAX_SCH_ERRORS    10  //maximum number of subsequent sch errors after which gsm receiver goes to find_next_fcch state
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef enum { empty, fcch_burst, sch_burst, normal_burst, rach_burst, dummy, dummy_or_normal, normal_or_noise } burst_type;
 | 
				
			||||||
 | 
					typedef enum { unknown, multiframe_26, multiframe_51 } multiframe_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const unsigned char SYNC_BITS[] = {
 | 
				
			||||||
 | 
					  1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0,
 | 
				
			||||||
 | 
					  0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1,
 | 
				
			||||||
 | 
					  0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1,
 | 
				
			||||||
 | 
					  0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const unsigned char ACCESS_BITS [] = {
 | 
				
			||||||
 | 
					 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1,
 | 
				
			||||||
 | 
					 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0,
 | 
				
			||||||
 | 
					 0, 1, 1, 1, 1, 0, 0, 0
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const unsigned FCCH_FRAMES[] = { 0, 10, 20, 30, 40 };
 | 
				
			||||||
 | 
					const unsigned SCH_FRAMES[] = { 1, 11, 21, 31, 41 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					const unsigned BCCH_FRAMES[] = { 2, 3, 4, 5 };          //!!the receiver shouldn't care about logical
 | 
				
			||||||
 | 
																		  //!!channels so this will be removed from this header
 | 
				
			||||||
 | 
					const unsigned TEST_CCH_FRAMES[] = { 2, 3, 4, 5, 6, 7, 8, 9, 12, 13, 14, 15, 16, 17, 18, 19, 22, 23, 24, 25, 26, 27, 28, 29, 32, 33, 34, 35, 36, 37, 38, 39, 42, 43, 44, 45, 46, 47, 48, 49 };
 | 
				
			||||||
 | 
					const unsigned TRAFFIC_CHANNEL_F[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 };
 | 
				
			||||||
 | 
					const unsigned TEST51[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50 };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TSC0  0
 | 
				
			||||||
 | 
					#define TSC1  1
 | 
				
			||||||
 | 
					#define TSC2  2
 | 
				
			||||||
 | 
					#define TSC3  3
 | 
				
			||||||
 | 
					#define TSC4  4
 | 
				
			||||||
 | 
					#define TSC5  5
 | 
				
			||||||
 | 
					#define TSC6  6
 | 
				
			||||||
 | 
					#define TSC7  7
 | 
				
			||||||
 | 
					#define TS_DUMMY 8
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TRAIN_SEQ_NUM 9
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define TIMESLOT0  0
 | 
				
			||||||
 | 
					#define TIMESLOT1  1
 | 
				
			||||||
 | 
					#define TIMESLOT2  2
 | 
				
			||||||
 | 
					#define TIMESLOT3  3
 | 
				
			||||||
 | 
					#define TIMESLOT4  4
 | 
				
			||||||
 | 
					#define TIMESLOT5  5
 | 
				
			||||||
 | 
					#define TIMESLOT6  6
 | 
				
			||||||
 | 
					#define TIMESLOT7  7
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const unsigned char train_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS] = {
 | 
				
			||||||
 | 
					  {0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1},
 | 
				
			||||||
 | 
					  {0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1},
 | 
				
			||||||
 | 
					  {0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0},
 | 
				
			||||||
 | 
					  {0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0},
 | 
				
			||||||
 | 
					  {0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1},
 | 
				
			||||||
 | 
					  {0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 0},
 | 
				
			||||||
 | 
					  {1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1},
 | 
				
			||||||
 | 
					  {1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0},
 | 
				
			||||||
 | 
					  {0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1} // DUMMY
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//Dummy burst 0xFB 76 0A 4E 09 10 1F 1C 5C 5C 57 4A 33 39 E9 F1 2F A8
 | 
				
			||||||
 | 
					static const unsigned char dummy_burst[] = {
 | 
				
			||||||
 | 
					  0, 0, 0,
 | 
				
			||||||
 | 
					  1, 1, 1, 1, 1, 0, 1, 1, 0, 1,
 | 
				
			||||||
 | 
					  1, 1, 0, 1, 1, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
					  1, 0, 1, 0, 0, 1, 0, 0, 1, 1,
 | 
				
			||||||
 | 
					  1, 0, 0, 0, 0, 0, 1, 0, 0, 1,
 | 
				
			||||||
 | 
					  0, 0, 0, 1, 0, 0, 0, 0, 0, 0,
 | 
				
			||||||
 | 
					  0, 1, 1, 1, 1, 1, 0, 0,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  0, 1, 1, 1, 0, 0, 0, 1, 0, 1,
 | 
				
			||||||
 | 
					  1, 1, 0, 0, 0, 1, 0, 1, 1, 1,
 | 
				
			||||||
 | 
					  0, 0, 0, 1, 0, 1,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  0, 1, 1, 1, 0, 1, 0, 0, 1, 0,
 | 
				
			||||||
 | 
					  1, 0, 0, 0, 1, 1, 0, 0, 1, 1,
 | 
				
			||||||
 | 
					  0, 0, 1, 1, 1, 0, 0, 1, 1, 1,
 | 
				
			||||||
 | 
					  1, 0, 1, 0, 0, 1, 1, 1, 1, 1,
 | 
				
			||||||
 | 
					  0, 0, 0, 1, 0, 0, 1, 0, 1, 1,
 | 
				
			||||||
 | 
					  1, 1, 1, 0, 1, 0, 1, 0,
 | 
				
			||||||
 | 
					  0, 0, 0
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										305
									
								
								Transceiver52M/grgsm_vitac/grgsm_vitac.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										305
									
								
								Transceiver52M/grgsm_vitac/grgsm_vitac.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,305 @@
 | 
				
			|||||||
 | 
					/* -*- c++ -*- */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * @file
 | 
				
			||||||
 | 
					 * @author (C) 2009-2017  by Piotr Krysik <ptrkrysik@gmail.com>
 | 
				
			||||||
 | 
					 * @author Contributions by sysmocom - s.f.m.c. GmbH / Eric Wild <ewild@sysmocom.de>
 | 
				
			||||||
 | 
					 * @section LICENSE
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Gr-gsm is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3, or (at your option)
 | 
				
			||||||
 | 
					 * any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Gr-gsm is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with gr-gsm; see the file COPYING.  If not, write to
 | 
				
			||||||
 | 
					 * the Free Software Foundation, Inc., 51 Franklin Street,
 | 
				
			||||||
 | 
					 * Boston, MA 02110-1301, USA.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "constants.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef HAVE_CONFIG_H
 | 
				
			||||||
 | 
					#include "config.h"
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#include <complex>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <algorithm>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <numeric>
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "viterbi_detector.h"
 | 
				
			||||||
 | 
					#include "grgsm_vitac.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					gr_complex d_acc_training_seq[N_ACCESS_BITS]; ///<encoded training sequence of a RACH burst
 | 
				
			||||||
 | 
					gr_complex d_sch_training_seq[N_SYNC_BITS]; ///<encoded training sequence of a SCH burst
 | 
				
			||||||
 | 
					gr_complex d_norm_training_seq[TRAIN_SEQ_NUM][N_TRAIN_BITS]; ///<encoded training sequences of a normal and dummy burst
 | 
				
			||||||
 | 
					const int d_chan_imp_length = CHAN_IMP_RESP_LENGTH;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void initvita()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/**
 | 
				
			||||||
 | 
						 * Prepare SCH sequence bits
 | 
				
			||||||
 | 
						 *
 | 
				
			||||||
 | 
						 * (TS_BITS + 2 * GUARD_PERIOD)
 | 
				
			||||||
 | 
						 * Burst and two guard periods
 | 
				
			||||||
 | 
						 * (one guard period is an arbitrary overlap)
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						gmsk_mapper(SYNC_BITS, N_SYNC_BITS, d_sch_training_seq, gr_complex(0.0, -1.0));
 | 
				
			||||||
 | 
						for (auto &i : d_sch_training_seq)
 | 
				
			||||||
 | 
							i = conj(i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* ab */
 | 
				
			||||||
 | 
						gmsk_mapper(ACCESS_BITS, N_ACCESS_BITS, d_acc_training_seq, gr_complex(0.0, -1.0));
 | 
				
			||||||
 | 
						for (auto &i : d_acc_training_seq)
 | 
				
			||||||
 | 
							i = conj(i);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Prepare bits of training sequences */
 | 
				
			||||||
 | 
						for (int i = 0; i < TRAIN_SEQ_NUM; i++) {
 | 
				
			||||||
 | 
							/**
 | 
				
			||||||
 | 
							 * If first bit of the sequence is 0
 | 
				
			||||||
 | 
							 * => first symbol is 1, else -1
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							gr_complex startpoint = train_seq[i][0] == 0 ? gr_complex(1.0, 0.0) : gr_complex(-1.0, 0.0);
 | 
				
			||||||
 | 
							gmsk_mapper(train_seq[i], N_TRAIN_BITS, d_norm_training_seq[i], startpoint);
 | 
				
			||||||
 | 
							for (auto &i : d_norm_training_seq[i])
 | 
				
			||||||
 | 
								i = conj(i);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <unsigned int burst_size>
 | 
				
			||||||
 | 
					NO_UBSAN static void detect_burst_generic(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start,
 | 
				
			||||||
 | 
										  char *output_binary, int ss)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						std::vector<gr_complex> rhh_temp(CHAN_IMP_RESP_LENGTH * d_OSR);
 | 
				
			||||||
 | 
						unsigned int stop_states[2] = { 4, 12 };
 | 
				
			||||||
 | 
						gr_complex filtered_burst[burst_size];
 | 
				
			||||||
 | 
						gr_complex rhh[CHAN_IMP_RESP_LENGTH];
 | 
				
			||||||
 | 
						float output[burst_size];
 | 
				
			||||||
 | 
						int start_state = ss;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						autocorrelation(chan_imp_resp, &rhh_temp[0], d_chan_imp_length * d_OSR);
 | 
				
			||||||
 | 
						for (int ii = 0; ii < d_chan_imp_length; ii++)
 | 
				
			||||||
 | 
							rhh[ii] = conj(rhh_temp[ii * d_OSR]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mafi(&input[burst_start], burst_size, chan_imp_resp, d_chan_imp_length * d_OSR, filtered_burst);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						viterbi_detector(filtered_burst, burst_size, rhh, start_state, stop_states, 2, output);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (unsigned int i = 0; i < burst_size; i++)
 | 
				
			||||||
 | 
							output_binary[i] = (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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										89
									
								
								Transceiver52M/grgsm_vitac/grgsm_vitac.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										89
									
								
								Transceiver52M/grgsm_vitac/grgsm_vitac.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,89 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					/* -*- c++ -*- */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * @file
 | 
				
			||||||
 | 
					 * @author (C) 2009-2017  by Piotr Krysik <ptrkrysik@gmail.com>
 | 
				
			||||||
 | 
					 * @section LICENSE
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Gr-gsm is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3, or (at your option)
 | 
				
			||||||
 | 
					 * any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Gr-gsm is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with gr-gsm; see the file COPYING.  If not, write to
 | 
				
			||||||
 | 
					 * the Free Software Foundation, Inc., 51 Franklin Street,
 | 
				
			||||||
 | 
					 * Boston, MA 02110-1301, USA.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <vector>
 | 
				
			||||||
 | 
					#include "constants.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* may only be used for for the DEFINITIONS!
 | 
				
			||||||
 | 
					* see https://gcc.gnu.org/bugzilla/show_bug.cgi?id=91664
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					#if defined(__has_attribute)
 | 
				
			||||||
 | 
					#if __has_attribute(target_clones) && defined(__x86_64) && true
 | 
				
			||||||
 | 
					#define MULTI_VER_TARGET_ATTR __attribute__((target_clones("avx", "sse4.2", "sse3", "sse2", "sse", "default")))
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define MULTI_VER_TARGET_ATTR
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* ... but apparently clang disagrees... */
 | 
				
			||||||
 | 
					#if defined(__clang__)
 | 
				
			||||||
 | 
					#define MULTI_VER_TARGET_ATTR_CLANGONLY MULTI_VER_TARGET_ATTR
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define MULTI_VER_TARGET_ATTR_CLANGONLY
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* ancient gcc < 8 has no attribute, clang always pretends to be gcc 4 */
 | 
				
			||||||
 | 
					#if !defined(__clang__) && __GNUC__ < 8
 | 
				
			||||||
 | 
					#define NO_UBSAN __attribute__((no_sanitize_undefined))
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#if defined(__has_attribute)
 | 
				
			||||||
 | 
					#if __has_attribute(no_sanitize)
 | 
				
			||||||
 | 
					#define NO_UBSAN __attribute__((no_sanitize("undefined")))
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define NO_UBSAN
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SYNC_SEARCH_RANGE 30
 | 
				
			||||||
 | 
					const int d_OSR(4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void initvita();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int process_vita_burst(gr_complex *input, int tsc, unsigned char *output_binary);
 | 
				
			||||||
 | 
					int process_vita_sc_burst(gr_complex *input, int tsc, unsigned char *output_binary, int *offset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary, int ss);
 | 
				
			||||||
 | 
					void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary, int ss);
 | 
				
			||||||
 | 
					void detect_burst_nb(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary);
 | 
				
			||||||
 | 
					void detect_burst_ab(const gr_complex *input, gr_complex *chan_imp_resp, int burst_start, char *output_binary);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void gmsk_mapper(const unsigned char *input, int nitems, gr_complex *gmsk_output, gr_complex start_point);
 | 
				
			||||||
 | 
					gr_complex correlate_sequence(const gr_complex *sequence, int length, const gr_complex *input);
 | 
				
			||||||
 | 
					inline void autocorrelation(const gr_complex *input, gr_complex *out, int nitems);
 | 
				
			||||||
 | 
					inline void mafi(const gr_complex *input, int nitems, gr_complex *filter, int filter_length, gr_complex *output);
 | 
				
			||||||
 | 
					int get_sch_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp);
 | 
				
			||||||
 | 
					int get_norm_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int bcc);
 | 
				
			||||||
 | 
					int get_sch_buffer_chan_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, unsigned int len, float *corr_max);
 | 
				
			||||||
 | 
					int get_access_imp_resp(const gr_complex *input, gr_complex *chan_imp_resp, float *corr_max, int max_delay);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum class btype { NB, SCH };
 | 
				
			||||||
 | 
					struct fdata {
 | 
				
			||||||
 | 
						btype t;
 | 
				
			||||||
 | 
						unsigned int fn;
 | 
				
			||||||
 | 
						int tn;
 | 
				
			||||||
 | 
						int bcc;
 | 
				
			||||||
 | 
						std::string fpath;
 | 
				
			||||||
 | 
						std::vector<gr_complex> data;
 | 
				
			||||||
 | 
						unsigned int data_start_offset;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										392
									
								
								Transceiver52M/grgsm_vitac/viterbi_detector.cc
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										392
									
								
								Transceiver52M/grgsm_vitac/viterbi_detector.cc
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,392 @@
 | 
				
			|||||||
 | 
					/* -*- c++ -*- */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * @file
 | 
				
			||||||
 | 
					 * @author (C) 2009 by Piotr Krysik <ptrkrysik@gmail.com>
 | 
				
			||||||
 | 
					 * @section LICENSE
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Gr-gsm is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3, or (at your option)
 | 
				
			||||||
 | 
					 * any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Gr-gsm is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with gr-gsm; see the file COPYING.  If not, write to
 | 
				
			||||||
 | 
					 * the Free Software Foundation, Inc., 51 Franklin Street,
 | 
				
			||||||
 | 
					 * Boston, MA 02110-1301, USA.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * viterbi_detector:
 | 
				
			||||||
 | 
					 *           This part does the detection of received sequnece.
 | 
				
			||||||
 | 
					 *           Employed algorithm is viterbi Maximum Likehood Sequence Estimation.
 | 
				
			||||||
 | 
					 *           At this moment it gives hard decisions on the output, but
 | 
				
			||||||
 | 
					 *           it was designed with soft decisions in mind.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * SYNTAX:   void viterbi_detector(
 | 
				
			||||||
 | 
					 *                                  const gr_complex * input, 
 | 
				
			||||||
 | 
					 *                                  unsigned int samples_num, 
 | 
				
			||||||
 | 
					 *                                  gr_complex * rhh, 
 | 
				
			||||||
 | 
					 *                                  unsigned int start_state, 
 | 
				
			||||||
 | 
					 *                                  const unsigned int * stop_states, 
 | 
				
			||||||
 | 
					 *                                  unsigned int stops_num, 
 | 
				
			||||||
 | 
					 *                                  float * output)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * INPUT:    input:       Complex received signal afted matched filtering.
 | 
				
			||||||
 | 
					 *           samples_num: Number of samples in the input table.
 | 
				
			||||||
 | 
					 *           rhh:         The autocorrelation of the estimated channel 
 | 
				
			||||||
 | 
					 *                        impulse response.
 | 
				
			||||||
 | 
					 *           start_state: Number of the start point. In GSM each burst 
 | 
				
			||||||
 | 
					 *                        starts with sequence of three bits (0,0,0) which 
 | 
				
			||||||
 | 
					 *                        indicates start point of the algorithm.
 | 
				
			||||||
 | 
					 *           stop_states: Table with numbers of possible stop states.
 | 
				
			||||||
 | 
					 *           stops_num:   Number of possible stop states
 | 
				
			||||||
 | 
					 *                     
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * OUTPUT:   output:      Differentially decoded hard output of the algorithm: 
 | 
				
			||||||
 | 
					 *                        -1 for logical "0" and 1 for logical "1"
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * SUB_FUNC: none
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * TEST(S):  Tested with real world normal burst.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "constants.h"
 | 
				
			||||||
 | 
					#include <cmath>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PATHS_NUM (1 << (CHAN_IMP_RESP_LENGTH-1))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void viterbi_detector(const gr_complex * input, unsigned int samples_num, gr_complex * rhh, unsigned int start_state, const unsigned int * stop_states, unsigned int stops_num, float * output)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					   float increment[8];
 | 
				
			||||||
 | 
					   float path_metrics1[16];
 | 
				
			||||||
 | 
					   float path_metrics2[16];
 | 
				
			||||||
 | 
					   float paths_difference;
 | 
				
			||||||
 | 
					   float * new_path_metrics;
 | 
				
			||||||
 | 
					   float * old_path_metrics;
 | 
				
			||||||
 | 
					   float * tmp;
 | 
				
			||||||
 | 
					   float trans_table[BURST_SIZE][16];
 | 
				
			||||||
 | 
					   float pm_candidate1, pm_candidate2;
 | 
				
			||||||
 | 
					   bool real_imag;
 | 
				
			||||||
 | 
					   float input_symbol_real, input_symbol_imag;
 | 
				
			||||||
 | 
					   unsigned int i, sample_nr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Setup first path metrics, so only state pointed by start_state is possible.
 | 
				
			||||||
 | 
					* Start_state metric is equal to zero, the rest is written with some very low value,
 | 
				
			||||||
 | 
					* which makes them practically impossible to occur.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					   for(i=0; i<PATHS_NUM; i++){
 | 
				
			||||||
 | 
					      path_metrics1[i]=(-10e30);
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					   path_metrics1[start_state]=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Compute Increment - a table of values which does not change for subsequent input samples.
 | 
				
			||||||
 | 
					* Increment is table of reference levels for computation of branch metrics:
 | 
				
			||||||
 | 
					*    branch metric = (+/-)received_sample (+/-) reference_level
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					   increment[0] = -rhh[1].imag() -rhh[2].real() -rhh[3].imag() +rhh[4].real();
 | 
				
			||||||
 | 
					   increment[1] = rhh[1].imag() -rhh[2].real() -rhh[3].imag() +rhh[4].real();
 | 
				
			||||||
 | 
					   increment[2] = -rhh[1].imag() +rhh[2].real() -rhh[3].imag() +rhh[4].real();
 | 
				
			||||||
 | 
					   increment[3] = rhh[1].imag() +rhh[2].real() -rhh[3].imag() +rhh[4].real();
 | 
				
			||||||
 | 
					   increment[4] = -rhh[1].imag() -rhh[2].real() +rhh[3].imag() +rhh[4].real();
 | 
				
			||||||
 | 
					   increment[5] = rhh[1].imag() -rhh[2].real() +rhh[3].imag() +rhh[4].real();
 | 
				
			||||||
 | 
					   increment[6] = -rhh[1].imag() +rhh[2].real() +rhh[3].imag() +rhh[4].real();
 | 
				
			||||||
 | 
					   increment[7] = rhh[1].imag() +rhh[2].real() +rhh[3].imag() +rhh[4].real();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Computation of path metrics and decisions (Add-Compare-Select).
 | 
				
			||||||
 | 
					* It's composed of two parts: one for odd input samples (imaginary numbers)
 | 
				
			||||||
 | 
					* and one for even samples (real numbers).
 | 
				
			||||||
 | 
					* Each part is composed of independent (parallelisable) statements like  
 | 
				
			||||||
 | 
					* this one:
 | 
				
			||||||
 | 
					*      pm_candidate1 = old_path_metrics[0] -input_symbol_imag +increment[2];
 | 
				
			||||||
 | 
					*      pm_candidate2 = old_path_metrics[8] -input_symbol_imag -increment[5];
 | 
				
			||||||
 | 
					*      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					*      new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					*      trans_table[sample_nr][1] = paths_difference;
 | 
				
			||||||
 | 
					* This is very good point for optimisations (SIMD or OpenMP) as it's most time 
 | 
				
			||||||
 | 
					* consuming part of this function. 
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					   sample_nr=0;
 | 
				
			||||||
 | 
					   old_path_metrics=path_metrics1;
 | 
				
			||||||
 | 
					   new_path_metrics=path_metrics2;
 | 
				
			||||||
 | 
					   while(sample_nr<samples_num){
 | 
				
			||||||
 | 
					      //Processing imag states
 | 
				
			||||||
 | 
					      real_imag=1;
 | 
				
			||||||
 | 
					      input_symbol_imag = input[sample_nr].imag();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[0] +input_symbol_imag -increment[2];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[8] +input_symbol_imag +increment[5];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[0]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][0] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[0] -input_symbol_imag +increment[2];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[8] -input_symbol_imag -increment[5];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][1] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[1] +input_symbol_imag -increment[3];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[9] +input_symbol_imag +increment[4];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[2]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][2] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[1] -input_symbol_imag +increment[3];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[9] -input_symbol_imag -increment[4];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[3]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][3] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[2] +input_symbol_imag -increment[0];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[10] +input_symbol_imag +increment[7];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[4]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][4] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[2] -input_symbol_imag +increment[0];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[10] -input_symbol_imag -increment[7];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[5]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][5] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[3] +input_symbol_imag -increment[1];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[11] +input_symbol_imag +increment[6];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[6]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][6] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[3] -input_symbol_imag +increment[1];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[11] -input_symbol_imag -increment[6];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[7]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][7] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[4] +input_symbol_imag -increment[6];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[12] +input_symbol_imag +increment[1];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[8]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][8] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[4] -input_symbol_imag +increment[6];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[12] -input_symbol_imag -increment[1];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[9]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][9] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[5] +input_symbol_imag -increment[7];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[13] +input_symbol_imag +increment[0];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[10]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][10] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[5] -input_symbol_imag +increment[7];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[13] -input_symbol_imag -increment[0];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[11]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][11] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[6] +input_symbol_imag -increment[4];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[14] +input_symbol_imag +increment[3];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[12]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][12] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[6] -input_symbol_imag +increment[4];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[14] -input_symbol_imag -increment[3];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[13]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][13] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[7] +input_symbol_imag -increment[5];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[15] +input_symbol_imag +increment[2];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[14]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][14] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[7] -input_symbol_imag +increment[5];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[15] -input_symbol_imag -increment[2];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[15]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][15] = paths_difference;
 | 
				
			||||||
 | 
					      tmp=old_path_metrics;
 | 
				
			||||||
 | 
					      old_path_metrics=new_path_metrics;
 | 
				
			||||||
 | 
					      new_path_metrics=tmp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      sample_nr++;
 | 
				
			||||||
 | 
					      if(sample_nr==samples_num)
 | 
				
			||||||
 | 
					         break;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      //Processing real states
 | 
				
			||||||
 | 
					      real_imag=0;
 | 
				
			||||||
 | 
					      input_symbol_real = input[sample_nr].real();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[0] -input_symbol_real -increment[7];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[8] -input_symbol_real +increment[0];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[0]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][0] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[0] +input_symbol_real +increment[7];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[8] +input_symbol_real -increment[0];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[1]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][1] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[1] -input_symbol_real -increment[6];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[9] -input_symbol_real +increment[1];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[2]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][2] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[1] +input_symbol_real +increment[6];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[9] +input_symbol_real -increment[1];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[3]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][3] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[2] -input_symbol_real -increment[5];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[10] -input_symbol_real +increment[2];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[4]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][4] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[2] +input_symbol_real +increment[5];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[10] +input_symbol_real -increment[2];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[5]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][5] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[3] -input_symbol_real -increment[4];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[11] -input_symbol_real +increment[3];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[6]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][6] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[3] +input_symbol_real +increment[4];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[11] +input_symbol_real -increment[3];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[7]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][7] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[4] -input_symbol_real -increment[3];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[12] -input_symbol_real +increment[4];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[8]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][8] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[4] +input_symbol_real +increment[3];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[12] +input_symbol_real -increment[4];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[9]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][9] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[5] -input_symbol_real -increment[2];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[13] -input_symbol_real +increment[5];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[10]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][10] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[5] +input_symbol_real +increment[2];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[13] +input_symbol_real -increment[5];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[11]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][11] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[6] -input_symbol_real -increment[1];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[14] -input_symbol_real +increment[6];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[12]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][12] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[6] +input_symbol_real +increment[1];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[14] +input_symbol_real -increment[6];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[13]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][13] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[7] -input_symbol_real -increment[0];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[15] -input_symbol_real +increment[7];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[14]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][14] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      pm_candidate1 = old_path_metrics[7] +input_symbol_real +increment[0];
 | 
				
			||||||
 | 
					      pm_candidate2 = old_path_metrics[15] +input_symbol_real -increment[7];
 | 
				
			||||||
 | 
					      paths_difference=pm_candidate2-pm_candidate1;
 | 
				
			||||||
 | 
					      new_path_metrics[15]=(paths_difference<0) ? pm_candidate1 : pm_candidate2;
 | 
				
			||||||
 | 
					      trans_table[sample_nr][15] = paths_difference;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      tmp=old_path_metrics;
 | 
				
			||||||
 | 
					      old_path_metrics=new_path_metrics;
 | 
				
			||||||
 | 
					      new_path_metrics=tmp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      sample_nr++;
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Find the best from the stop states by comparing their path metrics.
 | 
				
			||||||
 | 
					* Not every stop state is always possible, so we are searching in
 | 
				
			||||||
 | 
					* a subset of them.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					   unsigned int best_stop_state;
 | 
				
			||||||
 | 
					   float stop_state_metric, max_stop_state_metric;
 | 
				
			||||||
 | 
					   best_stop_state = stop_states[0];
 | 
				
			||||||
 | 
					   max_stop_state_metric = old_path_metrics[best_stop_state];
 | 
				
			||||||
 | 
					   for(i=1; i< stops_num; i++){
 | 
				
			||||||
 | 
					      stop_state_metric = old_path_metrics[stop_states[i]];
 | 
				
			||||||
 | 
					      if(stop_state_metric > max_stop_state_metric){
 | 
				
			||||||
 | 
					         max_stop_state_metric = stop_state_metric;
 | 
				
			||||||
 | 
					         best_stop_state = stop_states[i];
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					* This table was generated with hope that it gives a litle speedup during
 | 
				
			||||||
 | 
					* traceback stage. 
 | 
				
			||||||
 | 
					* Received bit is related to the number of state in the trellis.
 | 
				
			||||||
 | 
					* I've numbered states so their parity (number of ones) is related
 | 
				
			||||||
 | 
					* to a received bit. 
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					   static const unsigned int parity_table[PATHS_NUM] = { 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0,  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Table of previous states in the trellis diagram.
 | 
				
			||||||
 | 
					* For GMSK modulation every state has two previous states.
 | 
				
			||||||
 | 
					* Example:
 | 
				
			||||||
 | 
					*   previous_state_nr1 = prev_table[current_state_nr][0]
 | 
				
			||||||
 | 
					*   previous_state_nr2 = prev_table[current_state_nr][1]
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					   static const unsigned int prev_table[PATHS_NUM][2] = { {0,8}, {0,8}, {1,9}, {1,9}, {2,10}, {2,10}, {3,11}, {3,11}, {4,12}, {4,12}, {5,13}, {5,13}, {6,14}, {6,14}, {7,15}, {7,15},  };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Traceback and differential decoding of received sequence.
 | 
				
			||||||
 | 
					* Decisions stored in trans_table are used to restore best path in the trellis.
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					   sample_nr=samples_num;
 | 
				
			||||||
 | 
					   unsigned int state_nr=best_stop_state;
 | 
				
			||||||
 | 
					   unsigned int decision;
 | 
				
			||||||
 | 
					   bool out_bit=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   while(sample_nr>0){
 | 
				
			||||||
 | 
					      sample_nr--;
 | 
				
			||||||
 | 
					      decision = (trans_table[sample_nr][state_nr]>0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if(decision != out_bit)
 | 
				
			||||||
 | 
					         output[sample_nr]=-trans_table[sample_nr][state_nr];
 | 
				
			||||||
 | 
					      else
 | 
				
			||||||
 | 
					         output[sample_nr]=trans_table[sample_nr][state_nr];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      out_bit = out_bit ^ real_imag ^ parity_table[state_nr];
 | 
				
			||||||
 | 
					      state_nr = prev_table[state_nr][decision];
 | 
				
			||||||
 | 
					      real_imag = !real_imag;
 | 
				
			||||||
 | 
					   }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										64
									
								
								Transceiver52M/grgsm_vitac/viterbi_detector.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								Transceiver52M/grgsm_vitac/viterbi_detector.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,64 @@
 | 
				
			|||||||
 | 
					/* -*- c++ -*- */
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * @file
 | 
				
			||||||
 | 
					 * @author (C) 2009 Piotr Krysik <ptrkrysik@gmail.com>
 | 
				
			||||||
 | 
					 * @section LICENSE
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Gr-gsm is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3, or (at your option)
 | 
				
			||||||
 | 
					 * any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Gr-gsm is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU General Public License
 | 
				
			||||||
 | 
					 * along with gr-gsm; see the file COPYING.  If not, write to
 | 
				
			||||||
 | 
					 * the Free Software Foundation, Inc., 51 Franklin Street,
 | 
				
			||||||
 | 
					 * Boston, MA 02110-1301, USA.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * viterbi_detector:
 | 
				
			||||||
 | 
					 *           This part does the detection of received sequnece.
 | 
				
			||||||
 | 
					 *           Employed algorithm is viterbi Maximum Likehood Sequence Estimation.
 | 
				
			||||||
 | 
					 *           At this moment it gives hard decisions on the output, but
 | 
				
			||||||
 | 
					 *           it was designed with soft decisions in mind.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * SYNTAX:   void viterbi_detector(
 | 
				
			||||||
 | 
					 *                                  const gr_complex * input, 
 | 
				
			||||||
 | 
					 *                                  unsigned int samples_num, 
 | 
				
			||||||
 | 
					 *                                  gr_complex * rhh, 
 | 
				
			||||||
 | 
					 *                                  unsigned int start_state, 
 | 
				
			||||||
 | 
					 *                                  const unsigned int * stop_states, 
 | 
				
			||||||
 | 
					 *                                  unsigned int stops_num, 
 | 
				
			||||||
 | 
					 *                                  float * output)
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * INPUT:    input:       Complex received signal afted matched filtering.
 | 
				
			||||||
 | 
					 *           samples_num: Number of samples in the input table.
 | 
				
			||||||
 | 
					 *           rhh:         The autocorrelation of the estimated channel 
 | 
				
			||||||
 | 
					 *                        impulse response.
 | 
				
			||||||
 | 
					 *           start_state: Number of the start point. In GSM each burst 
 | 
				
			||||||
 | 
					 *                        starts with sequence of three bits (0,0,0) which 
 | 
				
			||||||
 | 
					 *                        indicates start point of the algorithm.
 | 
				
			||||||
 | 
					 *           stop_states: Table with numbers of possible stop states.
 | 
				
			||||||
 | 
					 *           stops_num:   Number of possible stop states
 | 
				
			||||||
 | 
					 *                     
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * OUTPUT:   output:      Differentially decoded hard output of the algorithm: 
 | 
				
			||||||
 | 
					 *                        -1 for logical "0" and 1 for logical "1"
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * SUB_FUNC: none
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * TEST(S):  Tested with real world normal burst.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef INCLUDED_VITERBI_DETECTOR_H
 | 
				
			||||||
 | 
					#define INCLUDED_VITERBI_DETECTOR_H
 | 
				
			||||||
 | 
					#include "constants.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void viterbi_detector(const gr_complex * input, unsigned int samples_num, gr_complex * rhh, unsigned int start_state, const unsigned int * stop_states, unsigned int stops_num, float * output);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* INCLUDED_VITERBI_DETECTOR_H */
 | 
				
			||||||
							
								
								
									
										473
									
								
								Transceiver52M/ms/bladerf_specific.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										473
									
								
								Transceiver52M/ms/bladerf_specific.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,473 @@
 | 
				
			|||||||
 | 
					#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);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										249
									
								
								Transceiver52M/ms/itrq.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										249
									
								
								Transceiver52M/ms/itrq.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,249 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Eric Wild <ewild@sysmocom.de>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <atomic>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <condition_variable>
 | 
				
			||||||
 | 
					#include <mutex>
 | 
				
			||||||
 | 
					#include <sys/eventfd.h>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					namespace spsc_detail
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					template <bool block_read, bool block_write>
 | 
				
			||||||
 | 
					class spsc_cond_timeout_detail {
 | 
				
			||||||
 | 
						std::condition_variable cond_r, cond_w;
 | 
				
			||||||
 | 
						std::mutex lr, lw;
 | 
				
			||||||
 | 
						std::atomic_int r_flag, w_flag;
 | 
				
			||||||
 | 
						const int timeout_ms = 200;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
						explicit spsc_cond_timeout_detail() : r_flag(0), w_flag(0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~spsc_cond_timeout_detail()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ssize_t spsc_check_r()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							std::unique_lock<std::mutex> lk(lr);
 | 
				
			||||||
 | 
							if (cond_r.wait_for(lk, std::chrono::milliseconds(timeout_ms), [&] { return r_flag != 0; })) {
 | 
				
			||||||
 | 
								r_flag--;
 | 
				
			||||||
 | 
								return 1;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ssize_t spsc_check_w()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							std::unique_lock<std::mutex> lk(lw);
 | 
				
			||||||
 | 
							if (cond_w.wait_for(lk, std::chrono::milliseconds(timeout_ms), [&] { return w_flag != 0; })) {
 | 
				
			||||||
 | 
								w_flag--;
 | 
				
			||||||
 | 
								return 1;
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void spsc_notify_r()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							std::unique_lock<std::mutex> lk(lr);
 | 
				
			||||||
 | 
							r_flag++;
 | 
				
			||||||
 | 
							cond_r.notify_one();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void spsc_notify_w()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							std::unique_lock<std::mutex> lk(lw);
 | 
				
			||||||
 | 
							w_flag++;
 | 
				
			||||||
 | 
							cond_w.notify_one();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <bool block_read, bool block_write>
 | 
				
			||||||
 | 
					class spsc_cond_detail {
 | 
				
			||||||
 | 
						std::condition_variable cond_r, cond_w;
 | 
				
			||||||
 | 
						std::mutex lr, lw;
 | 
				
			||||||
 | 
						std::atomic_int r_flag, w_flag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
						explicit spsc_cond_detail() : r_flag(0), w_flag(0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~spsc_cond_detail()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ssize_t spsc_check_r()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							std::unique_lock<std::mutex> lk(lr);
 | 
				
			||||||
 | 
							while (r_flag == 0)
 | 
				
			||||||
 | 
								cond_r.wait(lk);
 | 
				
			||||||
 | 
							r_flag--;
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ssize_t spsc_check_w()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							std::unique_lock<std::mutex> lk(lw);
 | 
				
			||||||
 | 
							while (w_flag == 0)
 | 
				
			||||||
 | 
								cond_w.wait(lk);
 | 
				
			||||||
 | 
							w_flag--;
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void spsc_notify_r()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							std::unique_lock<std::mutex> lk(lr);
 | 
				
			||||||
 | 
							r_flag++;
 | 
				
			||||||
 | 
							cond_r.notify_one();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void spsc_notify_w()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							std::unique_lock<std::mutex> lk(lw);
 | 
				
			||||||
 | 
							w_flag++;
 | 
				
			||||||
 | 
							cond_w.notify_one();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// originally designed for select loop integration
 | 
				
			||||||
 | 
					template <bool block_read, bool block_write>
 | 
				
			||||||
 | 
					class spsc_efd_detail {
 | 
				
			||||||
 | 
						int efd_r, efd_w; /* eventfds used to block/notify readers/writers */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
						explicit spsc_efd_detail()
 | 
				
			||||||
 | 
							: efd_r(eventfd(0, block_read ? 0 : EFD_NONBLOCK)), efd_w(eventfd(1, block_write ? 0 : EFD_NONBLOCK))
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~spsc_efd_detail()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							close(efd_r);
 | 
				
			||||||
 | 
							close(efd_w);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ssize_t spsc_check_r()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							uint64_t efdr;
 | 
				
			||||||
 | 
							return read(efd_r, &efdr, sizeof(uint64_t));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						ssize_t spsc_check_w()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							uint64_t efdr;
 | 
				
			||||||
 | 
							return read(efd_w, &efdr, sizeof(uint64_t));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void spsc_notify_r()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							uint64_t efdu = 1;
 | 
				
			||||||
 | 
							write(efd_r, &efdu, sizeof(uint64_t));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						void spsc_notify_w()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							uint64_t efdu = 1;
 | 
				
			||||||
 | 
							write(efd_w, &efdu, sizeof(uint64_t));
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						int get_r_efd()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return efd_r;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						int get_w_efd()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return efd_w;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <unsigned int SZ, typename ELEM, bool block_read, bool block_write, template <bool, bool> class T>
 | 
				
			||||||
 | 
					class spsc : public T<block_read, block_write> {
 | 
				
			||||||
 | 
						static_assert(SZ > 0, "queues need a size...");
 | 
				
			||||||
 | 
						std::atomic<unsigned int> readptr;
 | 
				
			||||||
 | 
						std::atomic<unsigned int> writeptr;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ELEM buf[SZ];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
						using base_t = T<block_read, block_write>;
 | 
				
			||||||
 | 
						using elem_t = ELEM;
 | 
				
			||||||
 | 
						explicit spsc() : readptr(0), writeptr(0)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						~spsc()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*! Adds element to the queue by copying the data.
 | 
				
			||||||
 | 
					 *  \param[in] elem input buffer, must match the originally configured queue buffer size!.
 | 
				
			||||||
 | 
					 *  \returns true if queue was not full and element was successfully pushed */
 | 
				
			||||||
 | 
						bool spsc_push(const ELEM *elem)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							size_t cur_wp, cur_rp;
 | 
				
			||||||
 | 
							cur_wp = writeptr.load(std::memory_order_relaxed);
 | 
				
			||||||
 | 
							cur_rp = readptr.load(std::memory_order_acquire);
 | 
				
			||||||
 | 
							if ((cur_wp + 1) % SZ == cur_rp) {
 | 
				
			||||||
 | 
								if (block_write)
 | 
				
			||||||
 | 
									base_t::spsc_check_w(); /* blocks, ensures next (!) call succeeds */
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							buf[cur_wp] = *elem;
 | 
				
			||||||
 | 
							writeptr.store((cur_wp + 1) % SZ, std::memory_order_release);
 | 
				
			||||||
 | 
							if (block_read)
 | 
				
			||||||
 | 
								base_t::spsc_notify_r(); /* fine after release */
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*! Removes element from the queue by copying the data.
 | 
				
			||||||
 | 
					 *  \param[in] elem output buffer, must match the originally configured queue buffer size!.
 | 
				
			||||||
 | 
					 *  \returns true if queue was not empty and element was successfully removed */
 | 
				
			||||||
 | 
						bool spsc_pop(ELEM *elem)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							size_t cur_wp, cur_rp;
 | 
				
			||||||
 | 
							cur_wp = writeptr.load(std::memory_order_acquire);
 | 
				
			||||||
 | 
							cur_rp = readptr.load(std::memory_order_relaxed);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (cur_wp == cur_rp) /* blocks via prep_pop */
 | 
				
			||||||
 | 
								return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							*elem = buf[cur_rp];
 | 
				
			||||||
 | 
							readptr.store((cur_rp + 1) % SZ, std::memory_order_release);
 | 
				
			||||||
 | 
							if (block_write)
 | 
				
			||||||
 | 
								base_t::spsc_notify_w();
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*! Reads the read-fd of the queue, which, depending on settings passed on queue creation, blocks.
 | 
				
			||||||
 | 
					    * This function can be used to deliberately wait for a non-empty queue on the read side.
 | 
				
			||||||
 | 
					    *  \returns result of reading the fd. */
 | 
				
			||||||
 | 
						ssize_t spsc_prep_pop()
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							return base_t::spsc_check_r();
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					} // namespace spsc_detail
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
 | 
				
			||||||
 | 
					class spsc_evfd : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_efd_detail> {};
 | 
				
			||||||
 | 
					template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
 | 
				
			||||||
 | 
					class spsc_cond : public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_detail> {};
 | 
				
			||||||
 | 
					template <unsigned int SZ, typename ELEM, bool block_read, bool block_write>
 | 
				
			||||||
 | 
					class spsc_cond_timeout
 | 
				
			||||||
 | 
						: public spsc_detail::spsc<SZ, ELEM, block_read, block_write, spsc_detail::spsc_cond_timeout_detail> {};
 | 
				
			||||||
							
								
								
									
										275
									
								
								Transceiver52M/ms/l1ctl_server.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										275
									
								
								Transceiver52M/ms/l1ctl_server.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,275 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * OsmocomBB <-> SDR connection bridge
 | 
				
			||||||
 | 
					 * UNIX socket server for L1CTL
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * (C) 2013 by Sylvain Munaut <tnt@246tNt.com>
 | 
				
			||||||
 | 
					 * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
 | 
				
			||||||
 | 
					 * (C) 2022 by by sysmocom - s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 2 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <errno.h>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <sys/un.h>
 | 
				
			||||||
 | 
					#include <arpa/inet.h>
 | 
				
			||||||
 | 
					#include <sys/socket.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/talloc.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/select.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/socket.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/write_queue.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/bb/trxcon/logging.h>
 | 
				
			||||||
 | 
					#include <osmocom/bb/trxcon/l1ctl_server.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define LOGP_CLI(cli, cat, level, fmt, args...) LOGP(cat, level, "%s" fmt, (cli)->log_prefix, ##args)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int l1ctl_client_read_cb(struct osmo_fd *ofd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct l1ctl_client *client = (struct l1ctl_client *)ofd->data;
 | 
				
			||||||
 | 
						struct msgb *msg;
 | 
				
			||||||
 | 
						uint16_t len;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Attempt to read from socket */
 | 
				
			||||||
 | 
						rc = read(ofd->fd, &len, L1CTL_MSG_LEN_FIELD);
 | 
				
			||||||
 | 
						if (rc != L1CTL_MSG_LEN_FIELD) {
 | 
				
			||||||
 | 
							if (rc <= 0) {
 | 
				
			||||||
 | 
								LOGP_CLI(client, DL1D, LOGL_NOTICE, "L1CTL connection error: read() failed (rc=%d): %s\n", rc,
 | 
				
			||||||
 | 
									 strerror(errno));
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								LOGP_CLI(client, DL1D, LOGL_NOTICE, "L1CTL connection error: short read\n");
 | 
				
			||||||
 | 
								rc = -EIO;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							l1ctl_client_conn_close(client);
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Check message length */
 | 
				
			||||||
 | 
						len = ntohs(len);
 | 
				
			||||||
 | 
						if (len > L1CTL_LENGTH) {
 | 
				
			||||||
 | 
							LOGP_CLI(client, DL1D, LOGL_ERROR, "Length is too big: %u\n", len);
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Allocate a new msg */
 | 
				
			||||||
 | 
						msg = msgb_alloc_headroom(L1CTL_LENGTH + L1CTL_HEADROOM, L1CTL_HEADROOM, "l1ctl_rx_msg");
 | 
				
			||||||
 | 
						if (!msg) {
 | 
				
			||||||
 | 
							LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to allocate msg\n");
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msg->l1h = msgb_put(msg, len);
 | 
				
			||||||
 | 
						rc = read(ofd->fd, msg->l1h, msgb_l1len(msg));
 | 
				
			||||||
 | 
						if (rc != len) {
 | 
				
			||||||
 | 
							LOGP_CLI(client, DL1D, LOGL_ERROR, "Can not read data: len=%d < rc=%d: %s\n", len, rc, strerror(errno));
 | 
				
			||||||
 | 
							msgb_free(msg);
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Debug print */
 | 
				
			||||||
 | 
						LOGP_CLI(client, DL1D, LOGL_DEBUG, "RX: '%s'\n", osmo_hexdump(msg->data, msg->len));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Call L1CTL handler */
 | 
				
			||||||
 | 
						client->server->cfg->conn_read_cb(client, msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int l1ctl_client_write_cb(struct osmo_fd *ofd, struct msgb *msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct l1ctl_client *client = (struct l1ctl_client *)ofd->data;
 | 
				
			||||||
 | 
						int len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ofd->fd <= 0)
 | 
				
			||||||
 | 
							return -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						len = write(ofd->fd, msg->data, msg->len);
 | 
				
			||||||
 | 
						if (len != msg->len) {
 | 
				
			||||||
 | 
							LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to write data: written (%d) < msg_len (%d)\n", len,
 | 
				
			||||||
 | 
								 msg->len);
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Connection handler */
 | 
				
			||||||
 | 
					static int l1ctl_server_conn_cb(struct osmo_fd *sfd, unsigned int flags)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct l1ctl_server *server = (struct l1ctl_server *)sfd->data;
 | 
				
			||||||
 | 
						struct l1ctl_client *client;
 | 
				
			||||||
 | 
						int rc, client_fd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client_fd = accept(sfd->fd, NULL, NULL);
 | 
				
			||||||
 | 
						if (client_fd < 0) {
 | 
				
			||||||
 | 
							LOGP(DL1C, LOGL_ERROR,
 | 
				
			||||||
 | 
							     "Failed to accept() a new connection: "
 | 
				
			||||||
 | 
							     "%s\n",
 | 
				
			||||||
 | 
							     strerror(errno));
 | 
				
			||||||
 | 
							return client_fd;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (server->cfg->num_clients_max > 0 /* 0 means unlimited */ &&
 | 
				
			||||||
 | 
						    server->num_clients >= server->cfg->num_clients_max) {
 | 
				
			||||||
 | 
							LOGP(DL1C, LOGL_NOTICE,
 | 
				
			||||||
 | 
							     "L1CTL server cannot accept more "
 | 
				
			||||||
 | 
							     "than %u connection(s)\n",
 | 
				
			||||||
 | 
							     server->cfg->num_clients_max);
 | 
				
			||||||
 | 
							close(client_fd);
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client = talloc_zero(server, struct l1ctl_client);
 | 
				
			||||||
 | 
						if (client == NULL) {
 | 
				
			||||||
 | 
							LOGP(DL1C, LOGL_ERROR, "Failed to allocate an L1CTL client\n");
 | 
				
			||||||
 | 
							close(client_fd);
 | 
				
			||||||
 | 
							return -ENOMEM;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Init the client's write queue */
 | 
				
			||||||
 | 
						osmo_wqueue_init(&client->wq, 100);
 | 
				
			||||||
 | 
						INIT_LLIST_HEAD(&client->wq.bfd.list);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client->wq.write_cb = &l1ctl_client_write_cb;
 | 
				
			||||||
 | 
						client->wq.read_cb = &l1ctl_client_read_cb;
 | 
				
			||||||
 | 
						osmo_fd_setup(&client->wq.bfd, client_fd, OSMO_FD_READ, &osmo_wqueue_bfd_cb, client, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Register the client's write queue */
 | 
				
			||||||
 | 
						rc = osmo_fd_register(&client->wq.bfd);
 | 
				
			||||||
 | 
						if (rc != 0) {
 | 
				
			||||||
 | 
							LOGP(DL1C, LOGL_ERROR, "Failed to register a new connection fd\n");
 | 
				
			||||||
 | 
							close(client->wq.bfd.fd);
 | 
				
			||||||
 | 
							talloc_free(client);
 | 
				
			||||||
 | 
							return rc;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						llist_add_tail(&client->list, &server->clients);
 | 
				
			||||||
 | 
						client->id = server->next_client_id++;
 | 
				
			||||||
 | 
						client->server = server;
 | 
				
			||||||
 | 
						server->num_clients++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGP(DL1C, LOGL_NOTICE, "L1CTL server got a new connection (id=%u)\n", client->id);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (client->server->cfg->conn_accept_cb != NULL)
 | 
				
			||||||
 | 
							client->server->cfg->conn_accept_cb(client);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int l1ctl_client_send(struct l1ctl_client *client, struct msgb *msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						uint8_t *len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Debug print */
 | 
				
			||||||
 | 
						LOGP_CLI(client, DL1D, LOGL_DEBUG, "TX: '%s'\n", osmo_hexdump(msg->data, msg->len));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (msg->l1h != msg->data)
 | 
				
			||||||
 | 
							LOGP_CLI(client, DL1D, LOGL_INFO, "Message L1 header != Message Data\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Prepend 16-bit length before sending */
 | 
				
			||||||
 | 
						len = msgb_push(msg, L1CTL_MSG_LEN_FIELD);
 | 
				
			||||||
 | 
						osmo_store16be(msg->len - L1CTL_MSG_LEN_FIELD, len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (osmo_wqueue_enqueue(&client->wq, msg) != 0) {
 | 
				
			||||||
 | 
							LOGP_CLI(client, DL1D, LOGL_ERROR, "Failed to enqueue msg!\n");
 | 
				
			||||||
 | 
							msgb_free(msg);
 | 
				
			||||||
 | 
							return -EIO;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void l1ctl_client_conn_close(struct l1ctl_client *client)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct l1ctl_server *server = client->server;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGP_CLI(client, DL1C, LOGL_NOTICE, "Closing L1CTL connection\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (server->cfg->conn_close_cb != NULL)
 | 
				
			||||||
 | 
							server->cfg->conn_close_cb(client);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Close connection socket */
 | 
				
			||||||
 | 
						osmo_fd_unregister(&client->wq.bfd);
 | 
				
			||||||
 | 
						close(client->wq.bfd.fd);
 | 
				
			||||||
 | 
						client->wq.bfd.fd = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Clear pending messages */
 | 
				
			||||||
 | 
						osmo_wqueue_clear(&client->wq);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						client->server->num_clients--;
 | 
				
			||||||
 | 
						llist_del(&client->list);
 | 
				
			||||||
 | 
						talloc_free(client);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* If this was the last client, reset the client IDs generator to 0.
 | 
				
			||||||
 | 
						 * This way avoid assigning huge unreadable client IDs like 26545. */
 | 
				
			||||||
 | 
						if (llist_empty(&server->clients))
 | 
				
			||||||
 | 
							server->next_client_id = 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct l1ctl_server *l1ctl_server_alloc(void *ctx, const struct l1ctl_server_cfg *cfg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct l1ctl_server *server;
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOGP(DL1C, LOGL_NOTICE, "Init L1CTL server (sock_path=%s)\n", cfg->sock_path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server = talloc(ctx, struct l1ctl_server);
 | 
				
			||||||
 | 
						OSMO_ASSERT(server != NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						*server = (struct l1ctl_server){
 | 
				
			||||||
 | 
							.clients = LLIST_HEAD_INIT(server->clients),
 | 
				
			||||||
 | 
							.cfg = cfg,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* conn_read_cb shall not be NULL */
 | 
				
			||||||
 | 
						OSMO_ASSERT(cfg->conn_read_cb != NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Bind connection handler */
 | 
				
			||||||
 | 
						osmo_fd_setup(&server->ofd, -1, OSMO_FD_READ, &l1ctl_server_conn_cb, server, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = osmo_sock_unix_init_ofd(&server->ofd, SOCK_STREAM, 0, cfg->sock_path, OSMO_SOCK_F_BIND);
 | 
				
			||||||
 | 
						if (rc < 0) {
 | 
				
			||||||
 | 
							LOGP(DL1C, LOGL_ERROR, "Could not create UNIX socket: %s\n", strerror(errno));
 | 
				
			||||||
 | 
							talloc_free(server);
 | 
				
			||||||
 | 
							return NULL;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return server;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void l1ctl_server_free(struct l1ctl_server *server)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						LOGP(DL1C, LOGL_NOTICE, "Shutdown L1CTL server\n");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Close all client connections */
 | 
				
			||||||
 | 
						while (!llist_empty(&server->clients)) {
 | 
				
			||||||
 | 
							struct l1ctl_client *client = llist_entry(server->clients.next, struct l1ctl_client, list);
 | 
				
			||||||
 | 
							l1ctl_client_conn_close(client);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/* Unbind listening socket */
 | 
				
			||||||
 | 
						if (server->ofd.fd != -1) {
 | 
				
			||||||
 | 
							osmo_fd_unregister(&server->ofd);
 | 
				
			||||||
 | 
							close(server->ofd.fd);
 | 
				
			||||||
 | 
							server->ofd.fd = -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						talloc_free(server);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										71
									
								
								Transceiver52M/ms/l1ctl_server_cb.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								Transceiver52M/ms/l1ctl_server_cb.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Eric Wild <ewild@sysmocom.de>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#include <osmocom/bb/trxcon/trxcon.h>
 | 
				
			||||||
 | 
					#include <osmocom/bb/trxcon/trxcon_fsm.h>
 | 
				
			||||||
 | 
					#include <osmocom/bb/trxcon/l1ctl_server.h>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#include "ms_trxcon_if.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct l1ctl_server_cfg server_cfg;
 | 
				
			||||||
 | 
					static struct l1ctl_server *server = NULL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int l1ctl_rx_cb(struct l1ctl_client *l1c, struct msgb *msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct trxcon_inst *trxcon = (struct trxcon_inst *)l1c->priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return trxcon_l1ctl_receive(trxcon, msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void l1ctl_conn_accept_cb(struct l1ctl_client *l1c)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						l1c->log_prefix = talloc_strdup(l1c, g_trxcon->log_prefix);
 | 
				
			||||||
 | 
						l1c->priv = g_trxcon;
 | 
				
			||||||
 | 
						g_trxcon->l2if = l1c;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void l1ctl_conn_close_cb(struct l1ctl_client *l1c)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct trxcon_inst *trxcon = (struct trxcon_inst *)l1c->priv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (trxcon == NULL || trxcon->fi == NULL)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_fsm_inst_dispatch(trxcon->fi, TRXCON_EV_L2IF_FAILURE, NULL);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool trxc_l1ctl_init(void *tallctx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Start the L1CTL server */
 | 
				
			||||||
 | 
						server_cfg = (struct l1ctl_server_cfg){
 | 
				
			||||||
 | 
							/* TODO: make path configurable */
 | 
				
			||||||
 | 
							.sock_path = "/tmp/osmocom_l2",	       .num_clients_max = 1,
 | 
				
			||||||
 | 
							.conn_read_cb = &l1ctl_rx_cb,	       .conn_accept_cb = &l1ctl_conn_accept_cb,
 | 
				
			||||||
 | 
							.conn_close_cb = &l1ctl_conn_close_cb,
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						server = l1ctl_server_alloc(tallctx, &server_cfg);
 | 
				
			||||||
 | 
						if (server == NULL) {
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										98
									
								
								Transceiver52M/ms/logging.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										98
									
								
								Transceiver52M/ms/logging.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,98 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * OsmocomBB <-> SDR connection bridge
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * (C) 2016-2017 by Vadim Yanitskiy <axilirator@gmail.com>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 2 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/application.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/logging.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/utils.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/bb/trxcon/trxcon.h>
 | 
				
			||||||
 | 
					#include <osmocom/bb/trxcon/logging.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct log_info_cat trxcon_log_info_cat[] = {
 | 
				
			||||||
 | 
						[DAPP] = {
 | 
				
			||||||
 | 
							.name = "DAPP",
 | 
				
			||||||
 | 
							.description = "Application",
 | 
				
			||||||
 | 
							.color = "\033[1;35m",
 | 
				
			||||||
 | 
							.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						[DL1C] = {
 | 
				
			||||||
 | 
							.name = "DL1C",
 | 
				
			||||||
 | 
							.description = "Layer 1 control interface",
 | 
				
			||||||
 | 
							.color = "\033[1;31m",
 | 
				
			||||||
 | 
							.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						[DL1D] = {
 | 
				
			||||||
 | 
							.name = "DL1D",
 | 
				
			||||||
 | 
							.description = "Layer 1 data",
 | 
				
			||||||
 | 
							.color = "\033[1;31m",
 | 
				
			||||||
 | 
							.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						[DTRXC] = {
 | 
				
			||||||
 | 
							.name = "DTRXC",
 | 
				
			||||||
 | 
							.description = "Transceiver control interface",
 | 
				
			||||||
 | 
							.color = "\033[1;33m",
 | 
				
			||||||
 | 
							.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						[DTRXD] = {
 | 
				
			||||||
 | 
							.name = "DTRXD",
 | 
				
			||||||
 | 
							.description = "Transceiver data interface",
 | 
				
			||||||
 | 
							.color = "\033[1;33m",
 | 
				
			||||||
 | 
							.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						[DSCH] = {
 | 
				
			||||||
 | 
							.name = "DSCH",
 | 
				
			||||||
 | 
							.description = "Scheduler management",
 | 
				
			||||||
 | 
							.color = "\033[1;36m",
 | 
				
			||||||
 | 
							.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						[DSCHD] = {
 | 
				
			||||||
 | 
							.name = "DSCHD",
 | 
				
			||||||
 | 
							.description = "Scheduler data",
 | 
				
			||||||
 | 
							.color = "\033[1;36m",
 | 
				
			||||||
 | 
							.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
						[DGPRS] = {
 | 
				
			||||||
 | 
							.name = "DGPRS",
 | 
				
			||||||
 | 
							.description = "L1 GPRS (MAC layer)",
 | 
				
			||||||
 | 
							.color = "\033[1;36m",
 | 
				
			||||||
 | 
							.enabled = 1, .loglevel = LOGL_NOTICE,
 | 
				
			||||||
 | 
						},
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct log_info trxcon_log_info = {
 | 
				
			||||||
 | 
						.cat = trxcon_log_info_cat,
 | 
				
			||||||
 | 
						.num_cat = ARRAY_SIZE(trxcon_log_info_cat),
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const int trxcon_log_cfg[] = {
 | 
				
			||||||
 | 
						[TRXCON_LOGC_FSM] = DAPP,
 | 
				
			||||||
 | 
						[TRXCON_LOGC_L1C] = DL1C,
 | 
				
			||||||
 | 
						[TRXCON_LOGC_L1D] = DL1D,
 | 
				
			||||||
 | 
						[TRXCON_LOGC_SCHC] = DSCH,
 | 
				
			||||||
 | 
						[TRXCON_LOGC_SCHD] = DSCHD,
 | 
				
			||||||
 | 
						[TRXCON_LOGC_GPRS] = DGPRS,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void trxc_log_init(void *tallctx)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						osmo_init_logging2(tallctx, &trxcon_log_info);
 | 
				
			||||||
 | 
						log_target_file_switch_to_wqueue(osmo_stderr_target);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						trxcon_set_log_cfg(&trxcon_log_cfg[0], ARRAY_SIZE(trxcon_log_cfg));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										160
									
								
								Transceiver52M/ms/ms.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										160
									
								
								Transceiver52M/ms/ms.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,160 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Eric Wild <ewild@sysmocom.de>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include "GSMCommon.h"
 | 
				
			||||||
 | 
					#include <atomic>
 | 
				
			||||||
 | 
					#include <cassert>
 | 
				
			||||||
 | 
					#include <complex>
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <cstdlib>
 | 
				
			||||||
 | 
					#include <cstdio>
 | 
				
			||||||
 | 
					#include <thread>
 | 
				
			||||||
 | 
					#include <fstream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ms.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#include "sch.h"
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										396
									
								
								Transceiver52M/ms/ms.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										396
									
								
								Transceiver52M/ms/ms.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,396 @@
 | 
				
			|||||||
 | 
					#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;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										314
									
								
								Transceiver52M/ms/ms_rx_lower.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										314
									
								
								Transceiver52M/ms/ms_rx_lower.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,314 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										78
									
								
								Transceiver52M/ms/ms_trxcon_if.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										78
									
								
								Transceiver52M/ms/ms_trxcon_if.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,78 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Eric Wild <ewild@sysmocom.de>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <atomic>
 | 
				
			||||||
 | 
					#include "ms_trxcon_if.h"
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#include <osmocom/bb/trxcon/trxcon.h>
 | 
				
			||||||
 | 
					#include <osmocom/bb/trxcon/l1ctl_server.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/panic.h>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern tx_queue_t txq;
 | 
				
			||||||
 | 
					extern cmd_queue_t cmdq_to_phy;
 | 
				
			||||||
 | 
					extern cmdr_queue_t cmdq_from_phy;
 | 
				
			||||||
 | 
					extern std::atomic<bool> g_exit_flag;
 | 
				
			||||||
 | 
					// trxcon C call(back) if
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					int trxcon_phyif_handle_burst_req(void *phyif, const struct trxcon_phyif_burst_req *br)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (br->burst_len == 0) // dummy/nope
 | 
				
			||||||
 | 
							return 0;
 | 
				
			||||||
 | 
						OSMO_ASSERT(br->burst != 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						internal_q_tx_buf b(br);
 | 
				
			||||||
 | 
						if (!g_exit_flag)
 | 
				
			||||||
 | 
							txq.spsc_push(&b);
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int trxcon_phyif_handle_cmd(void *phyif, const struct trxcon_phyif_cmd *cmd)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					#ifdef TXDEBUG
 | 
				
			||||||
 | 
						DBGLG() << "TOP C: " << cmd2str(cmd->type) << std::endl;
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
						if (!g_exit_flag)
 | 
				
			||||||
 | 
							cmdq_to_phy.spsc_push(cmd);
 | 
				
			||||||
 | 
						// q for resp polling happens in main loop
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void trxcon_phyif_close(void *phyif)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void trxcon_l1ctl_close(struct trxcon_inst *trxcon)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						/* Avoid use-after-free: both *fi and *trxcon are children of
 | 
				
			||||||
 | 
						 * the L2IF (L1CTL connection), so we need to re-parent *fi
 | 
				
			||||||
 | 
						 * to NULL before calling l1ctl_client_conn_close(). */
 | 
				
			||||||
 | 
						talloc_steal(NULL, trxcon->fi);
 | 
				
			||||||
 | 
						l1ctl_client_conn_close((struct l1ctl_client *)trxcon->l2if);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int trxcon_l1ctl_send(struct trxcon_inst *trxcon, struct msgb *msg)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct l1ctl_client *l1c = (struct l1ctl_client *)trxcon->l2if;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return l1ctl_client_send(l1c, msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										42
									
								
								Transceiver52M/ms/ms_trxcon_if.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								Transceiver52M/ms/ms_trxcon_if.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,42 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (C) 2023 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Eric Wild <ewild@sysmocom.de>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "ms.h"
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#include <osmocom/bb/trxcon/phyif.h>
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					extern struct trxcon_inst *g_trxcon;
 | 
				
			||||||
 | 
					struct internal_q_tx_buf {
 | 
				
			||||||
 | 
						trxcon_phyif_burst_req r;
 | 
				
			||||||
 | 
						uint8_t buf[148];
 | 
				
			||||||
 | 
						internal_q_tx_buf() = default;
 | 
				
			||||||
 | 
						internal_q_tx_buf(const internal_q_tx_buf &) = delete;
 | 
				
			||||||
 | 
						internal_q_tx_buf &operator=(const internal_q_tx_buf &) = default;
 | 
				
			||||||
 | 
						internal_q_tx_buf(const struct trxcon_phyif_burst_req *br) : r(*br)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							memcpy(buf, (void *)br->burst, br->burst_len);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					using tx_queue_t = spsc_cond<8 * 1, internal_q_tx_buf, true, false>;
 | 
				
			||||||
 | 
					using cmd_queue_t = spsc_cond_timeout<8 * 1, trxcon_phyif_cmd, true, false>;
 | 
				
			||||||
 | 
					using cmdr_queue_t = spsc_cond<8 * 1, trxcon_phyif_rsp, false, false>;
 | 
				
			||||||
							
								
								
									
										473
									
								
								Transceiver52M/ms/ms_upper.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										473
									
								
								Transceiver52M/ms/ms_upper.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,473 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (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, ¶m);
 | 
				
			||||||
 | 
								printf("ID: %lu, CPU: %d policy = %d priority = %d\n", pthread_self(), sched_getcpu(), policy,
 | 
				
			||||||
 | 
								       param.sched_priority);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
								std::cerr << "started U control!" << std::endl;
 | 
				
			||||||
 | 
								while (!g_exit_flag) {
 | 
				
			||||||
 | 
									t->driveControl();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								std::cerr << "exit U control!" << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								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, ¶m);
 | 
				
			||||||
 | 
								printf("ID: %lu, CPU: %d policy = %d priority = %d\n", pthread_self(), sched_getcpu(), policy,
 | 
				
			||||||
 | 
								       param.sched_priority);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
								std::cerr << "started U tx!" << std::endl;
 | 
				
			||||||
 | 
								while (!g_exit_flag) {
 | 
				
			||||||
 | 
									t->driveTx();
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
								std::cerr << "exit U tx!" << std::endl;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								return 0;
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							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;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										48
									
								
								Transceiver52M/ms/ms_upper.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								Transceiver52M/ms/ms_upper.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Author: Eric Wild <ewild@sysmocom.de>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#include <netdb.h>
 | 
				
			||||||
 | 
					#include <sys/socket.h>
 | 
				
			||||||
 | 
					#include <arpa/inet.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "GSMCommon.h"
 | 
				
			||||||
 | 
					#include "ms.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class upper_trx : public ms_trx {
 | 
				
			||||||
 | 
						volatile bool mOn;
 | 
				
			||||||
 | 
						char demodded_softbits[444];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// void driveControl();
 | 
				
			||||||
 | 
						bool pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						pthread_t thr_control, thr_tx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
						void start_threads();
 | 
				
			||||||
 | 
						void main_loop();
 | 
				
			||||||
 | 
						void stop_upper_threads();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						bool driveControl();
 | 
				
			||||||
 | 
						void driveReceiveFIFO();
 | 
				
			||||||
 | 
						void driveTx();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						upper_trx() : mOn(false){};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										329
									
								
								Transceiver52M/ms/sch.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										329
									
								
								Transceiver52M/ms/sch.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,329 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
 | 
				
			||||||
 | 
					 * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
 | 
				
			||||||
 | 
					 * (C) 2016 by Tom Tsou <tom.tsou@ettus.com>
 | 
				
			||||||
 | 
					 * (C) 2017 by Harald Welte <laforge@gnumonks.org>
 | 
				
			||||||
 | 
					 * (C) 2022 by 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> / Eric Wild <ewild@sysmocom.de>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * SPDX-License-Identifier: GPL-2.0+
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 2 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <complex.h>
 | 
				
			||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <math.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/bits.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/conv.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/utils.h>
 | 
				
			||||||
 | 
					#include <osmocom/core/crcgen.h>
 | 
				
			||||||
 | 
					#include <osmocom/coding/gsm0503_coding.h>
 | 
				
			||||||
 | 
					#include <osmocom/coding/gsm0503_parity.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "sch.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma GCC diagnostic push
 | 
				
			||||||
 | 
					#pragma GCC diagnostic ignored "-Wunused-variable"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* GSM 04.08, 9.1.30 Synchronization channel information */
 | 
				
			||||||
 | 
					struct sch_packed_info {
 | 
				
			||||||
 | 
						ubit_t t1_hi[2];
 | 
				
			||||||
 | 
						ubit_t bsic[6];
 | 
				
			||||||
 | 
						ubit_t t1_md[8];
 | 
				
			||||||
 | 
						ubit_t t3p_hi[2];
 | 
				
			||||||
 | 
						ubit_t t2[5];
 | 
				
			||||||
 | 
						ubit_t t1_lo[1];
 | 
				
			||||||
 | 
						ubit_t t3p_lo[1];
 | 
				
			||||||
 | 
					} __attribute__((packed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct sch_burst {
 | 
				
			||||||
 | 
						sbit_t tail0[3];
 | 
				
			||||||
 | 
						sbit_t data0[39];
 | 
				
			||||||
 | 
						sbit_t etsc[64];
 | 
				
			||||||
 | 
						sbit_t data1[39];
 | 
				
			||||||
 | 
						sbit_t tail1[3];
 | 
				
			||||||
 | 
						sbit_t guard[8];
 | 
				
			||||||
 | 
					} __attribute__((packed));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const uint8_t sch_next_output[][2] = {
 | 
				
			||||||
 | 
						{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
 | 
				
			||||||
 | 
						{ 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
 | 
				
			||||||
 | 
						{ 3, 0 }, { 2, 1 }, { 3, 0 }, { 2, 1 },
 | 
				
			||||||
 | 
						{ 0, 3 }, { 1, 2 }, { 0, 3 }, { 1, 2 },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const uint8_t sch_next_state[][2] = {
 | 
				
			||||||
 | 
						{  0,  1 }, {  2,  3 }, {  4,  5 }, {  6,  7 },
 | 
				
			||||||
 | 
						{  8,  9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
 | 
				
			||||||
 | 
						{  0,  1 }, {  2,  3 }, {  4,  5 }, {  6,  7 },
 | 
				
			||||||
 | 
						{  8,  9 }, { 10, 11 }, { 12, 13 }, { 14, 15 },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const struct osmo_conv_code gsm_conv_sch = {
 | 
				
			||||||
 | 
						.N = 2,
 | 
				
			||||||
 | 
						.K = 5,
 | 
				
			||||||
 | 
						.len = GSM_SCH_UNCODED_LEN,
 | 
				
			||||||
 | 
						.next_output = sch_next_output,
 | 
				
			||||||
 | 
						.next_state  = sch_next_state,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define GSM_MAX_BURST_LEN	157 * 4
 | 
				
			||||||
 | 
					#define GSM_SYM_RATE		(1625e3 / 6) * 4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Pre-generated FCCH measurement tone */
 | 
				
			||||||
 | 
					static complex float fcch_ref[GSM_MAX_BURST_LEN];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int float_to_sbit(const float *in, sbit_t *out, float scale, int len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < len; i++) {
 | 
				
			||||||
 | 
							out[i] = (in[i] - 0.5f) * scale;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Check if FN contains a FCCH burst */
 | 
				
			||||||
 | 
					int gsm_fcch_check_fn(int fn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int fn51 = fn % 51;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (fn51) {
 | 
				
			||||||
 | 
						case 0:
 | 
				
			||||||
 | 
						case 10:
 | 
				
			||||||
 | 
						case 20:
 | 
				
			||||||
 | 
						case 30:
 | 
				
			||||||
 | 
						case 40:
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Check if FN contains a SCH burst */
 | 
				
			||||||
 | 
					int gsm_sch_check_fn(int fn)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int fn51 = fn % 51;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (fn51) {
 | 
				
			||||||
 | 
						case 1:
 | 
				
			||||||
 | 
						case 11:
 | 
				
			||||||
 | 
						case 21:
 | 
				
			||||||
 | 
						case 31:
 | 
				
			||||||
 | 
						case 41:
 | 
				
			||||||
 | 
							return 1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int gsm_fcch_check_ts(int ts, int fn) {
 | 
				
			||||||
 | 
						return ts == 0 && gsm_fcch_check_fn(fn);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int gsm_sch_check_ts(int ts, int fn) {
 | 
				
			||||||
 | 
						return ts == 0 && gsm_sch_check_fn(fn);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* SCH (T1, T2, T3p) to full FN value */
 | 
				
			||||||
 | 
					int gsm_sch_to_fn(struct sch_info *sch)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int t1 = sch->t1;
 | 
				
			||||||
 | 
						int t2 = sch->t2;
 | 
				
			||||||
 | 
						int t3p = sch->t3p;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if ((t1 < 0) || (t2 < 0) || (t3p < 0))
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						int tt;
 | 
				
			||||||
 | 
						int t3 = t3p * 10 + 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (t3 < t2)
 | 
				
			||||||
 | 
							tt = (t3 + 26) - t2;
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
							tt = (t3 - t2) % 26;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return t1 * 51 * 26 + tt * 51 + t3;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Parse encoded SCH message */
 | 
				
			||||||
 | 
					int gsm_sch_parse(const uint8_t *info, struct sch_info *desc)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sch_packed_info *p = (struct sch_packed_info *) info;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						desc->bsic = (p->bsic[0] << 0) | (p->bsic[1] << 1) |
 | 
				
			||||||
 | 
							     (p->bsic[2] << 2) | (p->bsic[3] << 3) |
 | 
				
			||||||
 | 
							     (p->bsic[4] << 4) | (p->bsic[5] << 5);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						desc->t1 = (p->t1_lo[0] << 0) | (p->t1_md[0] << 1) |
 | 
				
			||||||
 | 
							   (p->t1_md[1] << 2) | (p->t1_md[2] << 3) |
 | 
				
			||||||
 | 
							   (p->t1_md[3] << 4) | (p->t1_md[4] << 5) |
 | 
				
			||||||
 | 
							   (p->t1_md[5] << 6) | (p->t1_md[6] << 7) |
 | 
				
			||||||
 | 
							   (p->t1_md[7] << 8) | (p->t1_hi[0] << 9) |
 | 
				
			||||||
 | 
							   (p->t1_hi[1] << 10);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						desc->t2 = (p->t2[0] << 0) | (p->t2[1] << 1) |
 | 
				
			||||||
 | 
							   (p->t2[2] << 2) | (p->t2[3] << 3) |
 | 
				
			||||||
 | 
							   (p->t2[4] << 4);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						desc->t3p = (p->t3p_lo[0] << 0) | (p->t3p_hi[0] << 1) |
 | 
				
			||||||
 | 
							    (p->t3p_hi[1] << 2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* From osmo-bts */
 | 
				
			||||||
 | 
					int gsm_sch_decode(uint8_t *info, sbit_t *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int rc;
 | 
				
			||||||
 | 
						ubit_t uncoded[GSM_SCH_UNCODED_LEN];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						osmo_conv_decode(&gsm_conv_sch, data, uncoded);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						rc = osmo_crc16gen_check_bits(&gsm0503_sch_crc10,
 | 
				
			||||||
 | 
									      uncoded, GSM_SCH_INFO_LEN,
 | 
				
			||||||
 | 
									      uncoded + GSM_SCH_INFO_LEN);
 | 
				
			||||||
 | 
						if (rc)
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						memcpy(info, uncoded, GSM_SCH_INFO_LEN * sizeof(ubit_t));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define FCCH_TAIL_BITS_LEN	3*4
 | 
				
			||||||
 | 
					#define FCCH_DATA_LEN	100*4//	142
 | 
				
			||||||
 | 
					#if 1
 | 
				
			||||||
 | 
					/* Compute FCCH frequency offset */
 | 
				
			||||||
 | 
					double org_gsm_fcch_offset(float *burst, int len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i, start, end;
 | 
				
			||||||
 | 
						float a, b, c, d, ang, avg = 0.0f;
 | 
				
			||||||
 | 
						double freq;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (len > GSM_MAX_BURST_LEN)
 | 
				
			||||||
 | 
							len = GSM_MAX_BURST_LEN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < len; i++) {
 | 
				
			||||||
 | 
							a = burst[2 * i + 0];
 | 
				
			||||||
 | 
							b = burst[2 * i + 1];
 | 
				
			||||||
 | 
							c = crealf(fcch_ref[i]);
 | 
				
			||||||
 | 
							d = cimagf(fcch_ref[i]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							burst[2 * i + 0] = a * c - b * d;
 | 
				
			||||||
 | 
							burst[2 * i + 1] = a * d + b * c;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						start = FCCH_TAIL_BITS_LEN;
 | 
				
			||||||
 | 
						end = start + FCCH_DATA_LEN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = start; i < end; i++) {
 | 
				
			||||||
 | 
							a = cargf(burst[2 * (i - 1) + 0] +
 | 
				
			||||||
 | 
								  burst[2 * (i - 1) + 1] * I);
 | 
				
			||||||
 | 
							b = cargf(burst[2 * i + 0] +
 | 
				
			||||||
 | 
								  burst[2 * i + 1] * I);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							ang = b - a;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (ang > M_PI)
 | 
				
			||||||
 | 
								ang -= 2 * M_PI;
 | 
				
			||||||
 | 
							else if (ang < -M_PI)
 | 
				
			||||||
 | 
								ang += 2 * M_PI;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							avg += ang;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						avg /= (float) (end - start);
 | 
				
			||||||
 | 
						freq = avg / (2 * M_PI) * GSM_SYM_RATE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return freq;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static const int L1 = 3;
 | 
				
			||||||
 | 
					static const int L2 = 32;
 | 
				
			||||||
 | 
					static const int N1 = 92;
 | 
				
			||||||
 | 
					static const int N2 = 92;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static struct { int8_t r; int8_t s; } P_inv_table[3+32];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void pinv(int P, int8_t* r, int8_t* s, int L1, int L2) {
 | 
				
			||||||
 | 
						for (int i = 0; i < L1; i++)
 | 
				
			||||||
 | 
							for (int j = 0; j < L2; j++)
 | 
				
			||||||
 | 
								if (P == L2 * i - L1 * j) {
 | 
				
			||||||
 | 
									*r = i;
 | 
				
			||||||
 | 
									*s = j;
 | 
				
			||||||
 | 
									return;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					float ac_sum_with_lag( complex float* in, int lag, int offset, int N) {
 | 
				
			||||||
 | 
						complex float v = 0 + 0*I;
 | 
				
			||||||
 | 
						int total_offset = offset + lag;
 | 
				
			||||||
 | 
						for (int s = 0; s < N; s++)
 | 
				
			||||||
 | 
							v += in[s + total_offset] * conjf(in[s + total_offset - lag]);
 | 
				
			||||||
 | 
						return cargf(v);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double gsm_fcch_offset(float *burst, int len)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int start;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						const float fs = 13. / 48. * 1e6 * 4;
 | 
				
			||||||
 | 
						const float expected_fcch_val = ((2 * M_PI) / (fs)) * 67700;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (len > GSM_MAX_BURST_LEN)
 | 
				
			||||||
 | 
							len = GSM_MAX_BURST_LEN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						start = FCCH_TAIL_BITS_LEN+10 * 4;
 | 
				
			||||||
 | 
						float alpha_one = ac_sum_with_lag((complex float*)burst, L1, start, N1);
 | 
				
			||||||
 | 
						float alpha_two = ac_sum_with_lag((complex float*)burst, L2, start, N2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float P_unrounded = (L1 * alpha_two - L2 * alpha_one) / (2 * M_PI);
 | 
				
			||||||
 | 
						int P = roundf(P_unrounded);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int8_t r = 0, s = 0;
 | 
				
			||||||
 | 
						pinv(P, &r, &s, L1, L2);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float omegal1 = (alpha_one + 2 * M_PI * r) / L1;
 | 
				
			||||||
 | 
						float omegal2 = (alpha_two + 2 * M_PI * s) / L2;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float rv = org_gsm_fcch_offset(burst, len);
 | 
				
			||||||
 | 
						//return rv;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						float reval = GSM_SYM_RATE / (2 * M_PI) * (expected_fcch_val - (omegal1+omegal2)/2);
 | 
				
			||||||
 | 
						//fprintf(stderr, "XX rv %f %f %f %f\n", rv, reval, omegal1 / (2 * M_PI) * fs, omegal2 / (2 * M_PI) * fs);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						//fprintf(stderr, "XX rv %f %f\n", rv, reval);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return -reval;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					/* Generate FCCH measurement tone */
 | 
				
			||||||
 | 
					static __attribute__((constructor)) void init()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						double freq = 0.25;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (i = 0; i < GSM_MAX_BURST_LEN; i++) {
 | 
				
			||||||
 | 
							fcch_ref[i] = sin(2 * M_PI * freq * (double) i) +
 | 
				
			||||||
 | 
								      cos(2 * M_PI * freq * (double) i) * I;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#pragma GCC diagnostic pop
 | 
				
			||||||
							
								
								
									
										48
									
								
								Transceiver52M/ms/sch.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								Transceiver52M/ms/sch.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
				
			|||||||
 | 
					#pragma once
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * (C) 2013 by Andreas Eversberg <jolly@eversberg.eu>
 | 
				
			||||||
 | 
					 * (C) 2015 by Alexander Chemeris <Alexander.Chemeris@fairwaves.co>
 | 
				
			||||||
 | 
					 * (C) 2016 by Tom Tsou <tom.tsou@ettus.com>
 | 
				
			||||||
 | 
					 * (C) 2017 by Harald Welte <laforge@gnumonks.org>
 | 
				
			||||||
 | 
					 * (C) 2022 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> / Eric Wild <ewild@sysmocom.de>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * All Rights Reserved
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * SPDX-License-Identifier: GPL-2.0+
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software; you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation; either version 2 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU General Public License for more details.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <osmocom/core/bits.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct sch_info  {
 | 
				
			||||||
 | 
						int bsic;
 | 
				
			||||||
 | 
						int t1;
 | 
				
			||||||
 | 
						int t2;
 | 
				
			||||||
 | 
						int t3p;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define GSM_SCH_INFO_LEN		25
 | 
				
			||||||
 | 
					#define GSM_SCH_UNCODED_LEN		35
 | 
				
			||||||
 | 
					#define GSM_SCH_CODED_LEN		78
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int gsm_sch_decode(uint8_t *sb_info, sbit_t *burst);
 | 
				
			||||||
 | 
					int gsm_sch_parse(const uint8_t *sb_info, struct sch_info *desc);
 | 
				
			||||||
 | 
					int gsm_sch_to_fn(struct sch_info *sch);
 | 
				
			||||||
 | 
					int gsm_sch_check_fn(int fn);
 | 
				
			||||||
 | 
					int gsm_fcch_check_fn(int fn);
 | 
				
			||||||
 | 
					int gsm_fcch_check_ts(int ts, int fn);
 | 
				
			||||||
 | 
					int gsm_sch_check_ts(int ts, int fn);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double gsm_fcch_offset(float *burst, int len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int float_to_sbit(const float *in, sbit_t *out, float scale, int len);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										92
									
								
								Transceiver52M/ms/threadpool.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								Transceiver52M/ms/threadpool.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,92 @@
 | 
				
			|||||||
 | 
					#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();
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
							
								
								
									
										274
									
								
								Transceiver52M/ms/uhd_specific.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										274
									
								
								Transceiver52M/ms/uhd_specific.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,274 @@
 | 
				
			|||||||
 | 
					#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
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
@@ -77,6 +77,24 @@ static struct ctrl_handle *g_ctrlh;
 | 
				
			|||||||
static RadioDevice *usrp;
 | 
					static RadioDevice *usrp;
 | 
				
			||||||
static RadioInterface *radio;
 | 
					static RadioInterface *radio;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* adjusts read timestamp offset to make the viterbi equalizer happy by including the start tail bits */
 | 
				
			||||||
 | 
					template <typename B>
 | 
				
			||||||
 | 
					class rif_va_wrapper : public B {
 | 
				
			||||||
 | 
						bool use_va;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    public:
 | 
				
			||||||
 | 
						template <typename... Args>
 | 
				
			||||||
 | 
						rif_va_wrapper(bool use_va, Args &&...args) : B(std::forward<Args>(args)...), use_va(use_va)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						bool start() override
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							auto rv = B::start();
 | 
				
			||||||
 | 
							B::readTimestamp -= use_va ? 20 : 0;
 | 
				
			||||||
 | 
							return rv;
 | 
				
			||||||
 | 
						};
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Create radio interface
 | 
					/* Create radio interface
 | 
				
			||||||
 *     The interface consists of sample rate changes, frequency shifts,
 | 
					 *     The interface consists of sample rate changes, frequency shifts,
 | 
				
			||||||
 *     channel multiplexing, and other conversions. The transceiver core
 | 
					 *     channel multiplexing, and other conversions. The transceiver core
 | 
				
			||||||
@@ -91,17 +109,17 @@ RadioInterface *makeRadioInterface(struct trx_ctx *trx,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	switch (type) {
 | 
						switch (type) {
 | 
				
			||||||
	case RadioDevice::NORMAL:
 | 
						case RadioDevice::NORMAL:
 | 
				
			||||||
		radio = new RadioInterface(usrp, trx->cfg.tx_sps,
 | 
							radio = new rif_va_wrapper<RadioInterface>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps,
 | 
				
			||||||
					   trx->cfg.rx_sps, trx->cfg.num_chans);
 | 
												   trx->cfg.num_chans);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case RadioDevice::RESAMP_64M:
 | 
						case RadioDevice::RESAMP_64M:
 | 
				
			||||||
	case RadioDevice::RESAMP_100M:
 | 
						case RadioDevice::RESAMP_100M:
 | 
				
			||||||
		radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps,
 | 
							radio = new rif_va_wrapper<RadioInterfaceResamp>(trx->cfg.use_va, usrp, trx->cfg.tx_sps,
 | 
				
			||||||
						 trx->cfg.rx_sps);
 | 
													 trx->cfg.rx_sps);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	case RadioDevice::MULTI_ARFCN:
 | 
						case RadioDevice::MULTI_ARFCN:
 | 
				
			||||||
		radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps,
 | 
							radio = new rif_va_wrapper<RadioInterfaceMulti>(trx->cfg.use_va, usrp, trx->cfg.tx_sps, trx->cfg.rx_sps,
 | 
				
			||||||
						trx->cfg.rx_sps, trx->cfg.num_chans);
 | 
													trx->cfg.num_chans);
 | 
				
			||||||
		break;
 | 
							break;
 | 
				
			||||||
	default:
 | 
						default:
 | 
				
			||||||
		LOG(ALERT) << "Unsupported radio interface configuration";
 | 
							LOG(ALERT) << "Unsupported radio interface configuration";
 | 
				
			||||||
@@ -465,6 +483,12 @@ int trx_validate_config(struct trx_ctx *trx)
 | 
				
			|||||||
		return -1;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (trx->cfg.use_va &&
 | 
				
			||||||
 | 
						    (trx->cfg.egprs || trx->cfg.multi_arfcn || trx->cfg.tx_sps != 4 || trx->cfg.rx_sps != 4)) {
 | 
				
			||||||
 | 
							LOG(ERROR) << "Viterbi equalizer only works for gmsk with 4 tx/rx samples per symbol!";
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -571,24 +595,14 @@ static void trx_stop()
 | 
				
			|||||||
static int trx_start(struct trx_ctx *trx)
 | 
					static int trx_start(struct trx_ctx *trx)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int type, chans;
 | 
						int type, chans;
 | 
				
			||||||
	unsigned int i;
 | 
					 | 
				
			||||||
	std::vector<std::string> rx_paths, tx_paths;
 | 
					 | 
				
			||||||
	RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
 | 
						RadioDevice::InterfaceType iface = RadioDevice::NORMAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Create the low level device object */
 | 
						/* Create the low level device object */
 | 
				
			||||||
	if (trx->cfg.multi_arfcn)
 | 
						if (trx->cfg.multi_arfcn)
 | 
				
			||||||
		iface = RadioDevice::MULTI_ARFCN;
 | 
							iface = RadioDevice::MULTI_ARFCN;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/* Generate vector of rx/tx_path: */
 | 
						usrp = RadioDevice::make(iface, &trx->cfg);
 | 
				
			||||||
	for (i = 0; i < trx->cfg.num_chans; i++) {
 | 
						type = usrp->open();
 | 
				
			||||||
		rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path));
 | 
					 | 
				
			||||||
		tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path));
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	usrp = RadioDevice::make(trx->cfg.tx_sps, trx->cfg.rx_sps, iface,
 | 
					 | 
				
			||||||
				 trx->cfg.num_chans, trx->cfg.offset,
 | 
					 | 
				
			||||||
				 tx_paths, rx_paths);
 | 
					 | 
				
			||||||
	type = usrp->open(charp2str(trx->cfg.dev_args), trx->cfg.clock_ref, trx->cfg.swap_channels);
 | 
					 | 
				
			||||||
	if (type < 0) {
 | 
						if (type < 0) {
 | 
				
			||||||
		LOG(ALERT) << "Failed to create radio device" << std::endl;
 | 
							LOG(ALERT) << "Failed to create radio device" << std::endl;
 | 
				
			||||||
		goto shutdown;
 | 
							goto shutdown;
 | 
				
			||||||
@@ -651,11 +665,11 @@ int main(int argc, char *argv[])
 | 
				
			|||||||
		exit(2);
 | 
							exit(2);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX);
 | 
						rc = telnet_init_default(tall_trx_ctx, NULL, OSMO_VTY_PORT_TRX);
 | 
				
			||||||
	if (rc < 0)
 | 
						if (rc < 0)
 | 
				
			||||||
		exit(1);
 | 
							exit(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	g_ctrlh = ctrl_interface_setup_dynip(NULL, ctrl_vty_get_bind_addr(), OSMO_CTRL_PORT_TRX, NULL);
 | 
						g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL);
 | 
				
			||||||
	if (!g_ctrlh) {
 | 
						if (!g_ctrlh) {
 | 
				
			||||||
		LOG(ERROR) << "Failed to create CTRL interface.\n";
 | 
							LOG(ERROR) << "Failed to create CTRL interface.\n";
 | 
				
			||||||
		exit(1);
 | 
							exit(1);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,9 +48,8 @@ struct trxd_hdr_common {
 | 
				
			|||||||
		reserved:1,
 | 
							reserved:1,
 | 
				
			||||||
		version:4;
 | 
							version:4;
 | 
				
			||||||
#elif OSMO_IS_BIG_ENDIAN
 | 
					#elif OSMO_IS_BIG_ENDIAN
 | 
				
			||||||
	uint8_t version:4,
 | 
					/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
 | 
				
			||||||
		reserved:1,
 | 
						uint8_t version:4, reserved:1, tn:3;
 | 
				
			||||||
		tn:3;
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	uint32_t fn; /* big endian */
 | 
						uint32_t fn; /* big endian */
 | 
				
			||||||
} __attribute__ ((packed));
 | 
					} __attribute__ ((packed));
 | 
				
			||||||
@@ -86,9 +85,8 @@ struct trxd_hdr_v1_specific {
 | 
				
			|||||||
		modulation:4,
 | 
							modulation:4,
 | 
				
			||||||
		idle:1;
 | 
							idle:1;
 | 
				
			||||||
#elif OSMO_IS_BIG_ENDIAN
 | 
					#elif OSMO_IS_BIG_ENDIAN
 | 
				
			||||||
	uint8_t idle:1,
 | 
					/* auto-generated from the little endian part above (libosmocore/contrib/struct_endianness.py) */
 | 
				
			||||||
		modulation:4,
 | 
						uint8_t idle:1, modulation:4, tsc:3;
 | 
				
			||||||
		tsc:3;
 | 
					 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
	int16_t ci;  /* big endian, in centiBels */
 | 
						int16_t ci;  /* big endian, in centiBels */
 | 
				
			||||||
} __attribute__ ((packed));
 | 
					} __attribute__ ((packed));
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -28,7 +28,7 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen,
 | 
					RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen,
 | 
				
			||||||
			 size_t hLen, bool outDirection)
 | 
								 size_t hLen, bool outDirection)
 | 
				
			||||||
	: writeIndex(0), readIndex(0), availSamples(0)
 | 
						: writeIndex(0), readIndex(0), availSamples(0), segments(numSegments)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (!outDirection)
 | 
						if (!outDirection)
 | 
				
			||||||
		hLen = 0;
 | 
							hLen = 0;
 | 
				
			||||||
@@ -36,7 +36,6 @@ RadioBuffer::RadioBuffer(size_t numSegments, size_t segmentLen,
 | 
				
			|||||||
	buffer = new float[2 * (hLen + numSegments * segmentLen)];
 | 
						buffer = new float[2 * (hLen + numSegments * segmentLen)];
 | 
				
			||||||
	bufferLen = numSegments * segmentLen;
 | 
						bufferLen = numSegments * segmentLen;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	segments.resize(numSegments);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (size_t i = 0; i < numSegments; i++)
 | 
						for (size_t i = 0; i < numSegments; i++)
 | 
				
			||||||
		segments[i] = &buffer[2 * (hLen + i * segmentLen)];
 | 
							segments[i] = &buffer[2 * (hLen + i * segmentLen)];
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -39,9 +39,10 @@ extern "C" {
 | 
				
			|||||||
RadioInterface::RadioInterface(RadioDevice *wDevice, size_t tx_sps,
 | 
					RadioInterface::RadioInterface(RadioDevice *wDevice, size_t tx_sps,
 | 
				
			||||||
                               size_t rx_sps, size_t chans,
 | 
					                               size_t rx_sps, size_t chans,
 | 
				
			||||||
                               int wReceiveOffset, GSM::Time wStartTime)
 | 
					                               int wReceiveOffset, GSM::Time wStartTime)
 | 
				
			||||||
  : mDevice(wDevice), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
 | 
					  : mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mReceiveFIFO(mChans), mDevice(wDevice),
 | 
				
			||||||
    underrun(false), overrun(false), writeTimestamp(0), readTimestamp(0),
 | 
					    sendBuffer(mChans), recvBuffer(mChans), convertRecvBuffer(mChans),
 | 
				
			||||||
    receiveOffset(wReceiveOffset), mOn(false)
 | 
					    convertSendBuffer(mChans), powerScaling(mChans), underrun(false), overrun(false),
 | 
				
			||||||
 | 
					    writeTimestamp(0), readTimestamp(0), receiveOffset(wReceiveOffset), mOn(false)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  mClock.set(wStartTime);
 | 
					  mClock.set(wStartTime);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -58,15 +59,6 @@ bool RadioInterface::init(int type)
 | 
				
			|||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  close();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  sendBuffer.resize(mChans);
 | 
					 | 
				
			||||||
  recvBuffer.resize(mChans);
 | 
					 | 
				
			||||||
  convertSendBuffer.resize(mChans);
 | 
					 | 
				
			||||||
  convertRecvBuffer.resize(mChans);
 | 
					 | 
				
			||||||
  mReceiveFIFO.resize(mChans);
 | 
					 | 
				
			||||||
  powerScaling.resize(mChans);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  for (size_t i = 0; i < mChans; i++) {
 | 
					  for (size_t i = 0; i < mChans; i++) {
 | 
				
			||||||
    sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
 | 
					    sendBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSTx, 0, true);
 | 
				
			||||||
    recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
 | 
					    recvBuffer[i] = new RadioBuffer(NUMCHUNKS, CHUNK * mSPSRx, 0, false);
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -31,6 +31,9 @@ static const unsigned gSlotLen = 148;      ///< number of symbols per slot, not
 | 
				
			|||||||
class RadioInterface {
 | 
					class RadioInterface {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
 | 
					  size_t mSPSTx;
 | 
				
			||||||
 | 
					  size_t mSPSRx;
 | 
				
			||||||
 | 
					  size_t mChans;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Thread mAlignRadioServiceLoopThread;	      ///< thread that synchronizes transmit and receive sections
 | 
					  Thread mAlignRadioServiceLoopThread;	      ///< thread that synchronizes transmit and receive sections
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -38,10 +41,6 @@ protected:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  RadioDevice *mDevice;			      ///< the USRP object
 | 
					  RadioDevice *mDevice;			      ///< the USRP object
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  size_t mSPSTx;
 | 
					 | 
				
			||||||
  size_t mSPSRx;
 | 
					 | 
				
			||||||
  size_t mChans;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  std::vector<RadioBuffer *> sendBuffer;
 | 
					  std::vector<RadioBuffer *> sendBuffer;
 | 
				
			||||||
  std::vector<RadioBuffer *> recvBuffer;
 | 
					  std::vector<RadioBuffer *> recvBuffer;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -76,7 +75,7 @@ private:
 | 
				
			|||||||
public:
 | 
					public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** start the interface */
 | 
					  /** start the interface */
 | 
				
			||||||
  bool start();
 | 
					  virtual bool start();
 | 
				
			||||||
  bool stop();
 | 
					  bool stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** initialization */
 | 
					  /** initialization */
 | 
				
			||||||
@@ -152,7 +151,7 @@ private:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
  RadioInterfaceResamp(RadioDevice* wDevice, size_t tx_sps, size_t rx_sps);
 | 
					  RadioInterfaceResamp(RadioDevice* wDevice, size_t tx_sps, size_t rx_sps);
 | 
				
			||||||
  ~RadioInterfaceResamp();
 | 
					  virtual ~RadioInterfaceResamp();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool init(int type);
 | 
					  bool init(int type);
 | 
				
			||||||
  void close();
 | 
					  void close();
 | 
				
			||||||
@@ -185,7 +184,7 @@ private:
 | 
				
			|||||||
public:
 | 
					public:
 | 
				
			||||||
  RadioInterfaceMulti(RadioDevice* radio, size_t tx_sps,
 | 
					  RadioInterfaceMulti(RadioDevice* radio, size_t tx_sps,
 | 
				
			||||||
                      size_t rx_sps, size_t chans = 1);
 | 
					                      size_t rx_sps, size_t chans = 1);
 | 
				
			||||||
  ~RadioInterfaceMulti();
 | 
					  virtual ~RadioInterfaceMulti();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool init(int type);
 | 
					  bool init(int type);
 | 
				
			||||||
  void close();
 | 
					  void close();
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,8 +44,9 @@ extern "C" {
 | 
				
			|||||||
RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps,
 | 
					RadioInterfaceMulti::RadioInterfaceMulti(RadioDevice *radio, size_t tx_sps,
 | 
				
			||||||
					 size_t rx_sps, size_t chans)
 | 
										 size_t rx_sps, size_t chans)
 | 
				
			||||||
	: RadioInterface(radio, tx_sps, rx_sps, chans),
 | 
						: RadioInterface(radio, tx_sps, rx_sps, chans),
 | 
				
			||||||
	  outerSendBuffer(NULL), outerRecvBuffer(NULL),
 | 
						  outerSendBuffer(NULL), outerRecvBuffer(NULL), history(mChans), active(MCHANS, false),
 | 
				
			||||||
	  dnsampler(NULL), upsampler(NULL), channelizer(NULL), synthesis(NULL)
 | 
						  rx_freq_state(mChans), tx_freq_state(mChans), dnsampler(NULL), upsampler(NULL), channelizer(NULL),
 | 
				
			||||||
 | 
						  synthesis(NULL)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -74,12 +75,12 @@ void RadioInterfaceMulti::close()
 | 
				
			|||||||
	for (std::vector<signalVector*>::iterator it = history.begin(); it != history.end(); ++it)
 | 
						for (std::vector<signalVector*>::iterator it = history.begin(); it != history.end(); ++it)
 | 
				
			||||||
		delete *it;
 | 
							delete *it;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mReceiveFIFO.resize(0);
 | 
						mReceiveFIFO.clear();
 | 
				
			||||||
	powerScaling.resize(0);
 | 
						powerScaling.clear();
 | 
				
			||||||
	history.resize(0);
 | 
						history.clear();
 | 
				
			||||||
	active.resize(0);
 | 
						active.clear();
 | 
				
			||||||
	rx_freq_state.resize(0);
 | 
						rx_freq_state.clear();
 | 
				
			||||||
	tx_freq_state.resize(0);
 | 
						tx_freq_state.clear();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	RadioInterface::close();
 | 
						RadioInterface::close();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -152,20 +153,9 @@ bool RadioInterfaceMulti::init(int type)
 | 
				
			|||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	close();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sendBuffer.resize(mChans);
 | 
					 | 
				
			||||||
	recvBuffer.resize(mChans);
 | 
					 | 
				
			||||||
	convertSendBuffer.resize(1);
 | 
						convertSendBuffer.resize(1);
 | 
				
			||||||
	convertRecvBuffer.resize(1);
 | 
						convertRecvBuffer.resize(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	mReceiveFIFO.resize(mChans);
 | 
					 | 
				
			||||||
	powerScaling.resize(mChans);
 | 
					 | 
				
			||||||
	history.resize(mChans);
 | 
					 | 
				
			||||||
	rx_freq_state.resize(mChans);
 | 
					 | 
				
			||||||
	tx_freq_state.resize(mChans);
 | 
					 | 
				
			||||||
	active.resize(MCHANS, false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* 4 == sps */
 | 
						/* 4 == sps */
 | 
				
			||||||
	inchunk = RESAMP_INRATE * 4;
 | 
						inchunk = RESAMP_INRATE * 4;
 | 
				
			||||||
	outchunk = RESAMP_OUTRATE * 4;
 | 
						outchunk = RESAMP_OUTRATE * 4;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -98,15 +98,6 @@ bool RadioInterfaceResamp::init(int type)
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	float cutoff = 1.0f;
 | 
						float cutoff = 1.0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	close();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	sendBuffer.resize(1);
 | 
					 | 
				
			||||||
	recvBuffer.resize(1);
 | 
					 | 
				
			||||||
	convertSendBuffer.resize(1);
 | 
					 | 
				
			||||||
	convertRecvBuffer.resize(1);
 | 
					 | 
				
			||||||
	mReceiveFIFO.resize(1);
 | 
					 | 
				
			||||||
	powerScaling.resize(1);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	switch (type) {
 | 
						switch (type) {
 | 
				
			||||||
	case RadioDevice::RESAMP_64M:
 | 
						case RadioDevice::RESAMP_64M:
 | 
				
			||||||
		resamp_inrate = RESAMP_64M_INRATE;
 | 
							resamp_inrate = RESAMP_64M_INRATE;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -76,22 +76,25 @@ bool radioVector::setVector(signalVector *vector, size_t chan)
 | 
				
			|||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
noiseVector::noiseVector(size_t size)
 | 
					avgVector::avgVector(size_t size)
 | 
				
			||||||
	: std::vector<float>(size), itr(0)
 | 
						: std::vector<float>(size), itr(0)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
float noiseVector::avg() const
 | 
					float avgVector::avg() const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	float val = 0.0;
 | 
						float val = 0.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!size())
 | 
				
			||||||
 | 
							return 0.0f;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (size_t i = 0; i < size(); i++)
 | 
						for (size_t i = 0; i < size(); i++)
 | 
				
			||||||
		val += (*this)[i];
 | 
							val += (*this)[i];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return val / (float) size();
 | 
						return val / (float) size();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool noiseVector::insert(float val)
 | 
					bool avgVector::insert(float val)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	if (!size())
 | 
						if (!size())
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -48,9 +48,9 @@ private:
 | 
				
			|||||||
	GSM::Time mTime;
 | 
						GSM::Time mTime;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class noiseVector : std::vector<float> {
 | 
					class avgVector : std::vector<float> {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	noiseVector(size_t size = 0);
 | 
						avgVector(size_t size = 0);
 | 
				
			||||||
	bool insert(float val);
 | 
						bool insert(float val);
 | 
				
			||||||
	float avg() const;
 | 
						float avg() const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										60
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										60
									
								
								configure.ac
									
									
									
									
									
								
							@@ -82,10 +82,10 @@ AC_TYPE_SIZE_T
 | 
				
			|||||||
AC_HEADER_TIME
 | 
					AC_HEADER_TIME
 | 
				
			||||||
AC_C_BIGENDIAN
 | 
					AC_C_BIGENDIAN
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.6.0)
 | 
					PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 1.9.0)
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.6.0)
 | 
					PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 1.9.0)
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.6.0)
 | 
					PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 1.9.0)
 | 
				
			||||||
PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.6.0)
 | 
					PKG_CHECK_MODULES(LIBOSMOCODING, libosmocoding >= 1.9.0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AC_ARG_ENABLE(sanitize,
 | 
					AC_ARG_ENABLE(sanitize,
 | 
				
			||||||
	[AS_HELP_STRING(
 | 
						[AS_HELP_STRING(
 | 
				
			||||||
@@ -138,6 +138,16 @@ AC_ARG_WITH(ipc, [
 | 
				
			|||||||
        [enable IPC])
 | 
					        [enable IPC])
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AC_ARG_WITH(bladerf, [
 | 
				
			||||||
 | 
					    AS_HELP_STRING([--with-bladerf],
 | 
				
			||||||
 | 
					        [enable bladeRF])
 | 
				
			||||||
 | 
					])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AC_ARG_WITH(mstrx, [
 | 
				
			||||||
 | 
					    AS_HELP_STRING([--with-mstrx],
 | 
				
			||||||
 | 
					        [enable MS TRX])
 | 
				
			||||||
 | 
					])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AC_ARG_WITH(singledb, [
 | 
					AC_ARG_WITH(singledb, [
 | 
				
			||||||
    AS_HELP_STRING([--with-singledb],
 | 
					    AS_HELP_STRING([--with-singledb],
 | 
				
			||||||
        [enable single daughterboard use on USRP1])
 | 
					        [enable single daughterboard use on USRP1])
 | 
				
			||||||
@@ -185,6 +195,37 @@ AS_IF([test "x$with_uhd" = "xyes"],[
 | 
				
			|||||||
            [PKG_CHECK_MODULES(UHD, uhd >= 003.005)]
 | 
					            [PKG_CHECK_MODULES(UHD, uhd >= 003.005)]
 | 
				
			||||||
        )]
 | 
					        )]
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					    # OS#5608: libuhd < 4.2.0 includes boost/thread/thread.hpp in its logging
 | 
				
			||||||
 | 
					    # code and therefore requires linking against boost_thread. It's missing in
 | 
				
			||||||
 | 
					    # uhd.pc, so work around it here.
 | 
				
			||||||
 | 
					    # https://github.com/EttusResearch/uhd/commit/04a83b6e76beef970854da69ba882d717669b49c
 | 
				
			||||||
 | 
					    PKG_CHECK_MODULES(UHD, uhd < 004.002,
 | 
				
			||||||
 | 
					        [LIBS="$LIBS -lboost_thread"],
 | 
				
			||||||
 | 
					        []
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AS_IF([test "x$with_bladerf" = "xyes"], [
 | 
				
			||||||
 | 
					    PKG_CHECK_MODULES(BLADE, libbladeRF >= 2.0)
 | 
				
			||||||
 | 
					])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AC_MSG_CHECKING([whether to enable building MS TRX])
 | 
				
			||||||
 | 
					AS_IF([test "x$with_mstrx" = "xyes"], [
 | 
				
			||||||
 | 
					    AC_CONFIG_SUBDIRS([osmocom-bb/src/host/trxcon])
 | 
				
			||||||
 | 
					    LIBTRXCON_DIR="osmocom-bb/src/host/trxcon"
 | 
				
			||||||
 | 
					    if ! test -d "$srcdir/$LIBTRXCON_DIR"; then
 | 
				
			||||||
 | 
					        AC_MSG_RESULT([no])
 | 
				
			||||||
 | 
					        AC_MSG_ERROR([$LIBTRXCON_DIR does not exist])
 | 
				
			||||||
 | 
					    fi
 | 
				
			||||||
 | 
					    AC_SUBST(LIBTRXCON_DIR)
 | 
				
			||||||
 | 
					    AC_MSG_RESULT([yes])
 | 
				
			||||||
 | 
					], [
 | 
				
			||||||
 | 
					    # Despite LIBTRXCON_DIR is added to SUBDIRS conditionally,
 | 
				
			||||||
 | 
					    # autoconf/automake still requires the directory to be present
 | 
				
			||||||
 | 
					    # and thus the submodule to be fetched (even if MS TRX is not needed).
 | 
				
			||||||
 | 
					    # Work this around by pointing it to an empty dir.
 | 
				
			||||||
 | 
					    AC_SUBST(LIBTRXCON_DIR, "osmocom-bb")
 | 
				
			||||||
 | 
					    AC_MSG_RESULT([no])
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AS_IF([test "x$with_singledb" = "xyes"], [
 | 
					AS_IF([test "x$with_singledb" = "xyes"], [
 | 
				
			||||||
@@ -240,8 +281,10 @@ AM_CONDITIONAL(DEVICE_UHD, [test "x$with_uhd" = "xyes"])
 | 
				
			|||||||
AM_CONDITIONAL(DEVICE_USRP1, [test "x$with_usrp1" = "xyes"])
 | 
					AM_CONDITIONAL(DEVICE_USRP1, [test "x$with_usrp1" = "xyes"])
 | 
				
			||||||
AM_CONDITIONAL(DEVICE_LMS, [test "x$with_lms" = "xyes"])
 | 
					AM_CONDITIONAL(DEVICE_LMS, [test "x$with_lms" = "xyes"])
 | 
				
			||||||
AM_CONDITIONAL(DEVICE_IPC, [test "x$with_ipc" = "xyes"])
 | 
					AM_CONDITIONAL(DEVICE_IPC, [test "x$with_ipc" = "xyes"])
 | 
				
			||||||
 | 
					AM_CONDITIONAL(DEVICE_BLADE, [test "x$with_bladerf" = "xyes"])
 | 
				
			||||||
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
 | 
					AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
 | 
				
			||||||
AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
 | 
					AM_CONDITIONAL(ARCH_ARM_A15, [test "x$with_neon_vfpv4" = "xyes"])
 | 
				
			||||||
 | 
					AM_CONDITIONAL(ENABLE_MS_TRX, [test "x$with_mstrx" = "xyes"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
PKG_CHECK_MODULES(LIBUSB, libusb-1.0)
 | 
					PKG_CHECK_MODULES(LIBUSB, libusb-1.0)
 | 
				
			||||||
PKG_CHECK_MODULES(FFTWF, fftw3f)
 | 
					PKG_CHECK_MODULES(FFTWF, fftw3f)
 | 
				
			||||||
@@ -309,6 +352,7 @@ AC_MSG_RESULT([CFLAGS="$CFLAGS"])
 | 
				
			|||||||
AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"])
 | 
					AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"])
 | 
				
			||||||
AC_MSG_RESULT([LDFLAGS="$LDFLAGS"])
 | 
					AC_MSG_RESULT([LDFLAGS="$LDFLAGS"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
dnl Output files
 | 
					dnl Output files
 | 
				
			||||||
AC_CONFIG_FILES([\
 | 
					AC_CONFIG_FILES([\
 | 
				
			||||||
    Makefile \
 | 
					    Makefile \
 | 
				
			||||||
@@ -325,6 +369,7 @@ AC_CONFIG_FILES([\
 | 
				
			|||||||
    Transceiver52M/device/usrp1/Makefile \
 | 
					    Transceiver52M/device/usrp1/Makefile \
 | 
				
			||||||
    Transceiver52M/device/lms/Makefile \
 | 
					    Transceiver52M/device/lms/Makefile \
 | 
				
			||||||
    Transceiver52M/device/ipc/Makefile \
 | 
					    Transceiver52M/device/ipc/Makefile \
 | 
				
			||||||
 | 
					    Transceiver52M/device/bladerf/Makefile \
 | 
				
			||||||
    tests/Makefile \
 | 
					    tests/Makefile \
 | 
				
			||||||
    tests/CommonLibs/Makefile \
 | 
					    tests/CommonLibs/Makefile \
 | 
				
			||||||
    tests/Transceiver52M/Makefile \
 | 
					    tests/Transceiver52M/Makefile \
 | 
				
			||||||
@@ -333,8 +378,7 @@ AC_CONFIG_FILES([\
 | 
				
			|||||||
    doc/examples/Makefile \
 | 
					    doc/examples/Makefile \
 | 
				
			||||||
    contrib/Makefile \
 | 
					    contrib/Makefile \
 | 
				
			||||||
    contrib/systemd/Makefile \
 | 
					    contrib/systemd/Makefile \
 | 
				
			||||||
 | 
					    doc/manuals/Makefile \
 | 
				
			||||||
 | 
					    contrib/osmo-trx.spec \
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
 | 
					AC_OUTPUT
 | 
				
			||||||
AC_OUTPUT(
 | 
					 | 
				
			||||||
	doc/manuals/Makefile
 | 
					 | 
				
			||||||
	contrib/osmo-trx.spec)
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -85,7 +85,7 @@ export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH"
 | 
				
			|||||||
export LD_LIBRARY_PATH="$inst/lib"
 | 
					export LD_LIBRARY_PATH="$inst/lib"
 | 
				
			||||||
export PATH="$inst/bin:$PATH"
 | 
					export PATH="$inst/bin:$PATH"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
CONFIG="--enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms --with-ipc $INSTR"
 | 
					CONFIG="--enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms --with-ipc --with-mstrx $INSTR"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Additional configure options and depends
 | 
					# Additional configure options and depends
 | 
				
			||||||
if [ "$WITH_MANUALS" = "1" ]; then
 | 
					if [ "$WITH_MANUALS" = "1" ]; then
 | 
				
			||||||
@@ -101,13 +101,17 @@ echo
 | 
				
			|||||||
set -x
 | 
					set -x
 | 
				
			||||||
 | 
					
 | 
				
			||||||
cd "$base"
 | 
					cd "$base"
 | 
				
			||||||
 | 
					git submodule status
 | 
				
			||||||
autoreconf --install --force
 | 
					autoreconf --install --force
 | 
				
			||||||
./configure $CONFIG
 | 
					./configure $CONFIG
 | 
				
			||||||
$MAKE $PARALLEL_MAKE
 | 
					$MAKE $PARALLEL_MAKE
 | 
				
			||||||
$MAKE check \
 | 
					$MAKE check \
 | 
				
			||||||
  || cat-testlogs.sh
 | 
					  || cat-testlogs.sh
 | 
				
			||||||
DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck \
 | 
					
 | 
				
			||||||
  || cat-testlogs.sh
 | 
					if arch | grep -v -q arm; then
 | 
				
			||||||
 | 
						DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE $PARALLEL_MAKE distcheck \
 | 
				
			||||||
 | 
						  || cat-testlogs.sh
 | 
				
			||||||
 | 
					fi
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
 | 
					if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then
 | 
				
			||||||
	make -C "$base/doc/manuals" publish
 | 
						make -C "$base/doc/manuals" publish
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -34,10 +34,10 @@ BuildRequires:  pkgconfig(LimeSuite)
 | 
				
			|||||||
BuildRequires:  pkgconfig(usrp) >= 3.3
 | 
					BuildRequires:  pkgconfig(usrp) >= 3.3
 | 
				
			||||||
%endif
 | 
					%endif
 | 
				
			||||||
BuildRequires:  pkgconfig(fftw3f)
 | 
					BuildRequires:  pkgconfig(fftw3f)
 | 
				
			||||||
BuildRequires:  pkgconfig(libosmocoding) >= 1.6.0
 | 
					BuildRequires:  pkgconfig(libosmocoding) >= 1.9.0
 | 
				
			||||||
BuildRequires:  pkgconfig(libosmocore) >= 1.6.0
 | 
					BuildRequires:  pkgconfig(libosmocore) >= 1.9.0
 | 
				
			||||||
BuildRequires:  pkgconfig(libosmoctrl) >= 1.6.0
 | 
					BuildRequires:  pkgconfig(libosmoctrl) >= 1.9.0
 | 
				
			||||||
BuildRequires:  pkgconfig(libosmovty) >= 1.6.0
 | 
					BuildRequires:  pkgconfig(libosmovty) >= 1.9.0
 | 
				
			||||||
BuildRequires:  pkgconfig(libusb-1.0)
 | 
					BuildRequires:  pkgconfig(libusb-1.0)
 | 
				
			||||||
BuildRequires:  pkgconfig(uhd)
 | 
					BuildRequires:  pkgconfig(uhd)
 | 
				
			||||||
%{?systemd_requires}
 | 
					%{?systemd_requires}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,20 @@
 | 
				
			|||||||
[Unit]
 | 
					[Unit]
 | 
				
			||||||
Description=Osmocom SDR BTS L1 Transceiver (IPC Backend)
 | 
					Description=Osmocom SDR BTS L1 Transceiver (IPC Backend)
 | 
				
			||||||
 | 
					After=network-online.target
 | 
				
			||||||
 | 
					Wants=network-online.target
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Service]
 | 
					[Service]
 | 
				
			||||||
Type=simple
 | 
					Type=simple
 | 
				
			||||||
Restart=always
 | 
					Restart=always
 | 
				
			||||||
 | 
					StateDirectory=osmocom
 | 
				
			||||||
 | 
					WorkingDirectory=%S/osmocom
 | 
				
			||||||
ExecStart=/usr/bin/osmo-trx-ipc -C /etc/osmocom/osmo-trx-ipc.cfg
 | 
					ExecStart=/usr/bin/osmo-trx-ipc -C /etc/osmocom/osmo-trx-ipc.cfg
 | 
				
			||||||
RestartSec=2
 | 
					RestartSec=2
 | 
				
			||||||
 | 
					# CPU scheduling policy:
 | 
				
			||||||
 | 
					CPUSchedulingPolicy=rr
 | 
				
			||||||
 | 
					# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
 | 
				
			||||||
 | 
					CPUSchedulingPriority=21
 | 
				
			||||||
 | 
					# See sched(7) for further details on real-time policies and priorities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Install]
 | 
					[Install]
 | 
				
			||||||
WantedBy=multi-user.target
 | 
					WantedBy=multi-user.target
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,20 @@
 | 
				
			|||||||
[Unit]
 | 
					[Unit]
 | 
				
			||||||
Description=Osmocom SDR BTS L1 Transceiver (LimeSuite backend)
 | 
					Description=Osmocom SDR BTS L1 Transceiver (LimeSuite backend)
 | 
				
			||||||
 | 
					After=network-online.target
 | 
				
			||||||
 | 
					Wants=network-online.target
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Service]
 | 
					[Service]
 | 
				
			||||||
Type=simple
 | 
					Type=simple
 | 
				
			||||||
Restart=always
 | 
					Restart=always
 | 
				
			||||||
 | 
					StateDirectory=osmocom
 | 
				
			||||||
 | 
					WorkingDirectory=%S/osmocom
 | 
				
			||||||
ExecStart=/usr/bin/osmo-trx-lms -C /etc/osmocom/osmo-trx-lms.cfg
 | 
					ExecStart=/usr/bin/osmo-trx-lms -C /etc/osmocom/osmo-trx-lms.cfg
 | 
				
			||||||
RestartSec=2
 | 
					RestartSec=2
 | 
				
			||||||
 | 
					# CPU scheduling policy:
 | 
				
			||||||
 | 
					CPUSchedulingPolicy=rr
 | 
				
			||||||
 | 
					# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
 | 
				
			||||||
 | 
					CPUSchedulingPriority=21
 | 
				
			||||||
 | 
					# See sched(7) for further details on real-time policies and priorities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Install]
 | 
					[Install]
 | 
				
			||||||
WantedBy=multi-user.target
 | 
					WantedBy=multi-user.target
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,20 @@
 | 
				
			|||||||
[Unit]
 | 
					[Unit]
 | 
				
			||||||
Description=Osmocom SDR BTS L1 Transceiver (UHD Backend)
 | 
					Description=Osmocom SDR BTS L1 Transceiver (UHD Backend)
 | 
				
			||||||
 | 
					After=network-online.target
 | 
				
			||||||
 | 
					Wants=network-online.target
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Service]
 | 
					[Service]
 | 
				
			||||||
Type=simple
 | 
					Type=simple
 | 
				
			||||||
Restart=always
 | 
					Restart=always
 | 
				
			||||||
 | 
					StateDirectory=osmocom
 | 
				
			||||||
 | 
					WorkingDirectory=%S/osmocom
 | 
				
			||||||
ExecStart=/usr/bin/osmo-trx-uhd -C /etc/osmocom/osmo-trx-uhd.cfg
 | 
					ExecStart=/usr/bin/osmo-trx-uhd -C /etc/osmocom/osmo-trx-uhd.cfg
 | 
				
			||||||
RestartSec=2
 | 
					RestartSec=2
 | 
				
			||||||
 | 
					# CPU scheduling policy:
 | 
				
			||||||
 | 
					CPUSchedulingPolicy=rr
 | 
				
			||||||
 | 
					# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
 | 
				
			||||||
 | 
					CPUSchedulingPriority=21
 | 
				
			||||||
 | 
					# See sched(7) for further details on real-time policies and priorities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Install]
 | 
					[Install]
 | 
				
			||||||
WantedBy=multi-user.target
 | 
					WantedBy=multi-user.target
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,11 +1,20 @@
 | 
				
			|||||||
[Unit]
 | 
					[Unit]
 | 
				
			||||||
Description=Osmocom SDR BTS L1 Transceiver (libusrp backend)
 | 
					Description=Osmocom SDR BTS L1 Transceiver (libusrp backend)
 | 
				
			||||||
 | 
					After=network-online.target
 | 
				
			||||||
 | 
					Wants=network-online.target
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Service]
 | 
					[Service]
 | 
				
			||||||
Type=simple
 | 
					Type=simple
 | 
				
			||||||
Restart=always
 | 
					Restart=always
 | 
				
			||||||
 | 
					StateDirectory=osmocom
 | 
				
			||||||
 | 
					WorkingDirectory=%S/osmocom
 | 
				
			||||||
ExecStart=/usr/bin/osmo-trx-usrp1 -C /etc/osmocom/osmo-trx-usrp1.cfg
 | 
					ExecStart=/usr/bin/osmo-trx-usrp1 -C /etc/osmocom/osmo-trx-usrp1.cfg
 | 
				
			||||||
RestartSec=2
 | 
					RestartSec=2
 | 
				
			||||||
 | 
					# CPU scheduling policy:
 | 
				
			||||||
 | 
					CPUSchedulingPolicy=rr
 | 
				
			||||||
 | 
					# For real-time scheduling policies an integer between 1 (lowest priority) and 99 (highest priority):
 | 
				
			||||||
 | 
					CPUSchedulingPriority=21
 | 
				
			||||||
 | 
					# See sched(7) for further details on real-time policies and priorities
 | 
				
			||||||
 | 
					
 | 
				
			||||||
[Install]
 | 
					[Install]
 | 
				
			||||||
WantedBy=multi-user.target
 | 
					WantedBy=multi-user.target
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										129
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										129
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							@@ -1,3 +1,132 @@
 | 
				
			|||||||
 | 
					osmo-trx (1.6.1) unstable; urgency=medium
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Eric Wild ]
 | 
				
			||||||
 | 
					  * devices: fix wrong gain to power mapping
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 -- Eric Wild <ewild@sysmocom.de>  Thu, 09 Nov 2023 14:16:21 +0200
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					osmo-trx (1.6.0) unstable; urgency=medium
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Vadim Yanitskiy ]
 | 
				
			||||||
 | 
					  * configure.ac: check if LIBTRXCON_DIR (submodule) exists
 | 
				
			||||||
 | 
					  * tests: Makefile.am: move -I flags from AM_CFLAGS to AM_CPPFLAGS
 | 
				
			||||||
 | 
					  * tests: there shall be no libraries in LDFLAGS
 | 
				
			||||||
 | 
					  * tests: use -no-install libtool flag to avoid ./lt-* scripts
 | 
				
			||||||
 | 
					  * tests: LMSDeviceTest: fix CPPFLAGS vs CXXFLAGS
 | 
				
			||||||
 | 
					  * ipc-driver-test: clean up variables in Makefile.am
 | 
				
			||||||
 | 
					  * CommonLibs: remove unused *trx in cfg[_no]_ctr_error_threshold_cmd
 | 
				
			||||||
 | 
					  * CommonLibs: clean up and fix Makefile.am
 | 
				
			||||||
 | 
					  * ms: logging: print category, level, and extended timestamp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Oliver Smith ]
 | 
				
			||||||
 | 
					  * Run struct_endianness.py
 | 
				
			||||||
 | 
					  * debian: set compat level to 10
 | 
				
			||||||
 | 
					  * systemd: depend on networking-online.target
 | 
				
			||||||
 | 
					  * USRPDevice:updateAlignment: remove byteswap code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Eric ]
 | 
				
			||||||
 | 
					  * .clang-format: adjust template formatting
 | 
				
			||||||
 | 
					  * ms: update submodule to currently known working version
 | 
				
			||||||
 | 
					  * ms: adjust tx scaling for tx samples
 | 
				
			||||||
 | 
					  * ms : fix the template formatting
 | 
				
			||||||
 | 
					  * ms: fix the gain init for blade
 | 
				
			||||||
 | 
					  * ms: prettify scheduling + add odroid
 | 
				
			||||||
 | 
					  * ms: fix startup & shutdown of blade
 | 
				
			||||||
 | 
					  * ms: block burst q to upper layer
 | 
				
			||||||
 | 
					  * ms: use single thread pool
 | 
				
			||||||
 | 
					  * ms : rename var
 | 
				
			||||||
 | 
					  * ms : rename var
 | 
				
			||||||
 | 
					  * ms: cache frequency
 | 
				
			||||||
 | 
					  * ms: pretty tx buf class
 | 
				
			||||||
 | 
					  * ms: rearrange internal trxcon<->phy if
 | 
				
			||||||
 | 
					  * ms: remove syncthing tool
 | 
				
			||||||
 | 
					  * ms: prune common sch acq code
 | 
				
			||||||
 | 
					  * ms: rearrange code to allow clean exits
 | 
				
			||||||
 | 
					  * ms: flexible template for value_type buffer sum
 | 
				
			||||||
 | 
					  * ms: make init call less confusing
 | 
				
			||||||
 | 
					  * transceiver: pass cfg struct instead of args
 | 
				
			||||||
 | 
					  * devices: unify band handling
 | 
				
			||||||
 | 
					  * ms: fix blocking logging
 | 
				
			||||||
 | 
					  * ms: drop the tx burst padding
 | 
				
			||||||
 | 
					  * trx: fix dev-args issue
 | 
				
			||||||
 | 
					  * ms: update osmocom-bb
 | 
				
			||||||
 | 
					  * ms: restructure the va code to add rach support
 | 
				
			||||||
 | 
					  * transceiver: add experimental viterbi equalizer support
 | 
				
			||||||
 | 
					  * ms/va: make ancient gcc < 8 happy
 | 
				
			||||||
 | 
					  * ms: fix thread prio startup issue
 | 
				
			||||||
 | 
					  * ms: fix a few coverity complaints related to initialization
 | 
				
			||||||
 | 
					  * ms: bump osmocom-bb submodule to current head
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Eric Wild ]
 | 
				
			||||||
 | 
					  * ms: adjust float<->integral type conversion
 | 
				
			||||||
 | 
					  * ms: sch: drop intermediate softvector
 | 
				
			||||||
 | 
					  * devices: add freq/gain override for uhd
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ arehbein ]
 | 
				
			||||||
 | 
					  * Transition to use of 'telnet_init_default'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Pau Espin Pedrol ]
 | 
				
			||||||
 | 
					  * Call osmo_fd_unregister() before closing and changing bfd->fd
 | 
				
			||||||
 | 
					  * ms: update osmocom-bb submodule
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 12 Sep 2023 15:56:57 +0200
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					osmo-trx (1.5.0) unstable; urgency=medium
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Oliver Smith ]
 | 
				
			||||||
 | 
					  * configure.ac: add -lboost_thread for uhd < 4.2.0
 | 
				
			||||||
 | 
					  * gitignore: add uhddev_ipc.cpp
 | 
				
			||||||
 | 
					  * contrib/jenkins: don't run "make distcheck" on arm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Vadim Yanitskiy ]
 | 
				
			||||||
 | 
					  * threshold_timer_update_intv(): call osmo_timer_del() unconditionally
 | 
				
			||||||
 | 
					  * Transceiver::expectedCorrType(): RACH is always 8-bit on PTCCH/U
 | 
				
			||||||
 | 
					  * contrib/jenkins.sh: dump submodule status before building
 | 
				
			||||||
 | 
					  * configure.ac: fix: properly check whether to enable ms-trx
 | 
				
			||||||
 | 
					  * configure.ac: allow building without cloning submodules
 | 
				
			||||||
 | 
					  * configure.ac: cosmetic: rearrange MS TRX related logic
 | 
				
			||||||
 | 
					  * configure.ac: make use of AC_MSG_CHECKING and AC_MSG_RESULT
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Max ]
 | 
				
			||||||
 | 
					  * Set working directory in systemd service file
 | 
				
			||||||
 | 
					  * Add realtime scheduling and set priority in service file
 | 
				
			||||||
 | 
					  * ctrl: take both address and port from vty config
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Eric ]
 | 
				
			||||||
 | 
					  * ignore vscode dirs
 | 
				
			||||||
 | 
					  * rename noisevector class -> avgvector
 | 
				
			||||||
 | 
					  * osmocom-bb for ms-trx side trxcon integration
 | 
				
			||||||
 | 
					  * add checkpatch config
 | 
				
			||||||
 | 
					  * bladerf xa4 support
 | 
				
			||||||
 | 
					  * update osmocom-bb submodule to fix make distcheck
 | 
				
			||||||
 | 
					  * vita demod by piotr krysik, modified
 | 
				
			||||||
 | 
					  * properly update osmocom-bb submodule, for real this time..
 | 
				
			||||||
 | 
					  * ms-trx support
 | 
				
			||||||
 | 
					  * clean up mutex, scopedlock, and signal classes
 | 
				
			||||||
 | 
					  * ipc: add missing override
 | 
				
			||||||
 | 
					  * clang-format: proper c++ standard
 | 
				
			||||||
 | 
					  * ipc: remove old autotools workaround
 | 
				
			||||||
 | 
					  * ms: init trash used to escape the usb callbacks
 | 
				
			||||||
 | 
					  * radio interface: fix init
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Eric Wild ]
 | 
				
			||||||
 | 
					  * mstrx: do not wait forever if clock locking fails
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 -- Pau Espin Pedrol <pespin@sysmocom.de>  Tue, 07 Feb 2023 17:08:17 +0100
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					osmo-trx (1.4.1) unstable; urgency=medium
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Oliver Smith ]
 | 
				
			||||||
 | 
					  * treewide: remove FSF address
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Vadim Yanitskiy ]
 | 
				
			||||||
 | 
					  * tests: use 'check_PROGRAMS' instead of 'noinst_PROGRAMS'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  [ Harald Welte ]
 | 
				
			||||||
 | 
					  * update git URLs (git -> https; gitea)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 -- Pau Espin Pedrol <pespin@sysmocom.de>  Wed, 29 Jun 2022 09:32:56 +0200
 | 
				
			||||||
 | 
					
 | 
				
			||||||
osmo-trx (1.4.0) unstable; urgency=medium
 | 
					osmo-trx (1.4.0) unstable; urgency=medium
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  [ Pau Espin Pedrol ]
 | 
					  [ Pau Espin Pedrol ]
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/compat
									
									
									
									
										vendored
									
									
								
							@@ -1 +1 @@
 | 
				
			|||||||
9
 | 
					10
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										10
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										10
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							@@ -2,7 +2,7 @@ Source: osmo-trx
 | 
				
			|||||||
Section: net
 | 
					Section: net
 | 
				
			||||||
Priority: optional
 | 
					Priority: optional
 | 
				
			||||||
Maintainer: Osmocom team <openbsc@lists.osmocom.org>
 | 
					Maintainer: Osmocom team <openbsc@lists.osmocom.org>
 | 
				
			||||||
Build-Depends: debhelper (>= 9),
 | 
					Build-Depends: debhelper (>= 10),
 | 
				
			||||||
               autotools-dev,
 | 
					               autotools-dev,
 | 
				
			||||||
               autoconf-archive,
 | 
					               autoconf-archive,
 | 
				
			||||||
               pkg-config,
 | 
					               pkg-config,
 | 
				
			||||||
@@ -14,11 +14,11 @@ Build-Depends: debhelper (>= 9),
 | 
				
			|||||||
               libtalloc-dev,
 | 
					               libtalloc-dev,
 | 
				
			||||||
               libusrp-dev,
 | 
					               libusrp-dev,
 | 
				
			||||||
               liblimesuite-dev,
 | 
					               liblimesuite-dev,
 | 
				
			||||||
               libosmocore-dev (>= 1.6.0),
 | 
					               libosmocore-dev (>= 1.9.0),
 | 
				
			||||||
               osmo-gsm-manuals-dev
 | 
					               osmo-gsm-manuals-dev (>= 1.5.0)
 | 
				
			||||||
Standards-Version: 3.9.6
 | 
					Standards-Version: 3.9.6
 | 
				
			||||||
Vcs-Browser: http://cgit.osmocom.org/osmo-trx
 | 
					Vcs-Browser: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
 | 
				
			||||||
Vcs-Git: git://git.osmocom.org/osmo-trx
 | 
					Vcs-Git: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
 | 
				
			||||||
Homepage: https://projects.osmocom.org/projects/osmotrx
 | 
					Homepage: https://projects.osmocom.org/projects/osmotrx
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Package: osmo-trx
 | 
					Package: osmo-trx
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										2
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										2
									
								
								debian/copyright
									
									
									
									
										vendored
									
									
								
							@@ -1,6 +1,6 @@
 | 
				
			|||||||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 | 
					Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
 | 
				
			||||||
Upstream-Name: OsmoTRX
 | 
					Upstream-Name: OsmoTRX
 | 
				
			||||||
Source: http://cgit.osmocom.org/osmo-trx/
 | 
					Source: https://gitea.osmocom.org/cellular-infrastructure/osmo-trx
 | 
				
			||||||
Files-Excluded: Transceiver52M/device/usrp1/std_inband.rbf
 | 
					Files-Excluded: Transceiver52M/device/usrp1/std_inband.rbf
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Files: *
 | 
					Files: *
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										1
									
								
								osmocom-bb
									
									
									
									
									
										Submodule
									
								
							
							
								
								
								
								
								
							
						
						
									
										1
									
								
								osmocom-bb
									
									
									
									
									
										Submodule
									
								
							 Submodule osmocom-bb added at 05ddc05233
									
								
							@@ -29,9 +29,9 @@
 | 
				
			|||||||
#include "Threads.h"
 | 
					#include "Threads.h"
 | 
				
			||||||
#include "Interthread.h"
 | 
					#include "Interthread.h"
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					#include <mutex>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace std;
 | 
					std::mutex dbg_cout;
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
InterthreadQueue<int> gQ;
 | 
					InterthreadQueue<int> gQ;
 | 
				
			||||||
InterthreadMap<int,int> gMap;
 | 
					InterthreadMap<int,int> gMap;
 | 
				
			||||||
@@ -41,6 +41,8 @@ int q_last_write_val;
 | 
				
			|||||||
int m_last_read_val;
 | 
					int m_last_read_val;
 | 
				
			||||||
int m_last_write_val;
 | 
					int m_last_write_val;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define CERR(text) { dbg_cout.lock() ; std::cerr << text; dbg_cout.unlock(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void* qWriter(void*)
 | 
					void* qWriter(void*)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int *p;
 | 
						int *p;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,7 +1,25 @@
 | 
				
			|||||||
include $(top_srcdir)/Makefile.common
 | 
					include $(top_srcdir)/Makefile.common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AM_CPPFLAGS = -Wall -I$(top_srcdir)/CommonLibs $(STD_DEFINES_AND_INCLUDES) $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) -g
 | 
					AM_CPPFLAGS = \
 | 
				
			||||||
AM_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS)
 | 
						-I$(top_srcdir)/CommonLibs \
 | 
				
			||||||
 | 
						$(STD_DEFINES_AND_INCLUDES) \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AM_CXXFLAGS = \
 | 
				
			||||||
 | 
						-Wall -g \
 | 
				
			||||||
 | 
						$(LIBOSMOCORE_CFLAGS) \
 | 
				
			||||||
 | 
						$(LIBOSMOCTRL_CFLAGS) \
 | 
				
			||||||
 | 
						$(LIBOSMOVTY_CFLAGS) \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AM_LDFLAGS = -no-install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					LDADD = \
 | 
				
			||||||
 | 
						$(COMMON_LA) \
 | 
				
			||||||
 | 
						$(LIBOSMOCORE_LIBS) \
 | 
				
			||||||
 | 
						$(LIBOSMOCTRL_LIBS) \
 | 
				
			||||||
 | 
						$(LIBOSMOVTY_LIBS) \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXTRA_DIST = BitVectorTest.ok \
 | 
					EXTRA_DIST = BitVectorTest.ok \
 | 
				
			||||||
             PRBSTest.ok \
 | 
					             PRBSTest.ok \
 | 
				
			||||||
@@ -11,7 +29,7 @@ EXTRA_DIST = BitVectorTest.ok \
 | 
				
			|||||||
             LogTest.ok \
 | 
					             LogTest.ok \
 | 
				
			||||||
             LogTest.err
 | 
					             LogTest.err
 | 
				
			||||||
 | 
					
 | 
				
			||||||
noinst_PROGRAMS = \
 | 
					check_PROGRAMS = \
 | 
				
			||||||
	BitVectorTest \
 | 
						BitVectorTest \
 | 
				
			||||||
	PRBSTest \
 | 
						PRBSTest \
 | 
				
			||||||
	InterthreadTest \
 | 
						InterthreadTest \
 | 
				
			||||||
@@ -20,21 +38,16 @@ noinst_PROGRAMS = \
 | 
				
			|||||||
	LogTest
 | 
						LogTest
 | 
				
			||||||
 | 
					
 | 
				
			||||||
BitVectorTest_SOURCES = BitVectorTest.cpp
 | 
					BitVectorTest_SOURCES = BitVectorTest.cpp
 | 
				
			||||||
BitVectorTest_LDADD = $(COMMON_LA)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
PRBSTest_SOURCES = PRBSTest.cpp
 | 
					PRBSTest_SOURCES = PRBSTest.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
InterthreadTest_SOURCES = InterthreadTest.cpp
 | 
					InterthreadTest_SOURCES = InterthreadTest.cpp
 | 
				
			||||||
InterthreadTest_LDADD = $(COMMON_LA)
 | 
					InterthreadTest_LDADD = $(LDADD) -lpthread
 | 
				
			||||||
InterthreadTest_LDFLAGS = -lpthread $(AM_LDFLAGS)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
TimevalTest_SOURCES = TimevalTest.cpp
 | 
					TimevalTest_SOURCES = TimevalTest.cpp
 | 
				
			||||||
TimevalTest_LDADD = $(COMMON_LA)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
VectorTest_SOURCES = VectorTest.cpp
 | 
					VectorTest_SOURCES = VectorTest.cpp
 | 
				
			||||||
VectorTest_LDADD = $(COMMON_LA)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
LogTest_SOURCES = LogTest.cpp
 | 
					LogTest_SOURCES = LogTest.cpp
 | 
				
			||||||
LogTest_LDADD = $(COMMON_LA)
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
MOSTLYCLEANFILES += testSource testDestination
 | 
					MOSTLYCLEANFILES += testSource testDestination
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,10 +1,17 @@
 | 
				
			|||||||
include $(top_srcdir)/Makefile.common
 | 
					include $(top_srcdir)/Makefile.common
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AM_CFLAGS = -Wall -I$(top_srcdir)/Transceiver52M -I$(top_srcdir)/Transceiver52M/arch/common $(STD_DEFINES_AND_INCLUDES) -g
 | 
					AM_CPPFLAGS = \
 | 
				
			||||||
 | 
						-I$(top_srcdir)/Transceiver52M \
 | 
				
			||||||
 | 
						-I$(top_srcdir)/Transceiver52M/arch/common \
 | 
				
			||||||
 | 
						$(STD_DEFINES_AND_INCLUDES) \
 | 
				
			||||||
 | 
						$(NULL)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					AM_CFLAGS = -Wall -g
 | 
				
			||||||
 | 
					AM_LDFLAGS = -no-install
 | 
				
			||||||
 | 
					
 | 
				
			||||||
EXTRA_DIST = convolve_test.ok convolve_test_golden.h
 | 
					EXTRA_DIST = convolve_test.ok convolve_test_golden.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
noinst_PROGRAMS = \
 | 
					check_PROGRAMS = \
 | 
				
			||||||
	convolve_test
 | 
						convolve_test
 | 
				
			||||||
 | 
					
 | 
				
			||||||
convolve_test_SOURCES = convolve_test.c
 | 
					convolve_test_SOURCES = convolve_test.c
 | 
				
			||||||
@@ -18,12 +25,13 @@ convolve_test_CFLAGS += $(SIMD_FLAGS)
 | 
				
			|||||||
endif
 | 
					endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if DEVICE_LMS
 | 
					if DEVICE_LMS
 | 
				
			||||||
noinst_PROGRAMS += LMSDeviceTest
 | 
					check_PROGRAMS += LMSDeviceTest
 | 
				
			||||||
LMSDeviceTest_SOURCES = LMSDeviceTest.cpp
 | 
					LMSDeviceTest_SOURCES = LMSDeviceTest.cpp
 | 
				
			||||||
LMSDeviceTest_LDFLAGS = $(LIBOSMOCORE_LIBS) $(LMS_LIBS)
 | 
					 | 
				
			||||||
LMSDeviceTest_LDADD = \
 | 
					LMSDeviceTest_LDADD = \
 | 
				
			||||||
	$(top_builddir)/Transceiver52M/device/lms/libdevice.la \
 | 
						$(top_builddir)/Transceiver52M/device/lms/libdevice.la \
 | 
				
			||||||
 | 
						$(LIBOSMOCORE_LIBS) \
 | 
				
			||||||
	$(COMMON_LA) \
 | 
						$(COMMON_LA) \
 | 
				
			||||||
	$(LMS_LIBS)
 | 
						$(LMS_LIBS) \
 | 
				
			||||||
LMSDeviceTest_CPPFLAGS = $(AM_CPPFLAGS) $(LMS_CFLAGS)
 | 
						$(NULL)
 | 
				
			||||||
 | 
					LMSDeviceTest_CXXFLAGS = $(AM_CFLAGS) $(LMS_CFLAGS)
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user