mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
				synced 2025-10-31 04:03:35 +00:00 
			
		
		
		
	Compare commits
	
		
			179 Commits
		
	
	
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
|  | d20b7fa579 | ||
|  | 77e18352fb | ||
|  | f97d75b355 | ||
|  | a944001873 | ||
|  | 42c165605a | ||
|  | affd351787 | ||
|  | 03b11620d9 | ||
|  | 25185886f0 | ||
|  | 47031405f5 | ||
|  | 0646b3ce75 | ||
|  | 46cf9efc8e | ||
|  | 871713bf6a | ||
|  | 8d9a05ce5b | ||
|  | fe865f45d7 | ||
|  | e5b6664419 | ||
|  | 1595ddaa5f | ||
|  | 441d82add9 | ||
|  | 867cea575b | ||
|  | 2f53ea4cf2 | ||
|  | 138caaf09d | ||
|  | 3fadcad33e | ||
|  | eaa0144dcb | ||
|  | f7331764ac | ||
|  | 800c029c70 | ||
|  | 522cbe91d3 | ||
|  | ed361f9912 | ||
|  | 2d085e290b | ||
|  | 5cea18e5cb | ||
|  | 4adc4eb8d0 | ||
|  | 41c68aa41c | ||
|  | 39e0bbacca | ||
|  | b4ea7b5211 | ||
|  | 69869bd58f | ||
|  | ebb37693a5 | ||
|  | 55928f23cb | ||
|  | 0277b58f6d | ||
|  | be8a83f681 | ||
|  | d9a460dce9 | ||
|  | f570b4af62 | ||
|  | 46560ea254 | ||
|  | 038ab45137 | ||
|  | 6b50b62b12 | ||
|  | 3984256273 | ||
|  | d06172ce32 | ||
|  | 762c951faa | ||
|  | e47d7e964a | ||
|  | 3cee25ac68 | ||
|  | 0bd0782a39 | ||
|  | 8072650406 | ||
|  | c6541e6c26 | ||
|  | 1e2c0105e2 | ||
|  | 32b3c2e4b2 | ||
|  | 1c4bbadda6 | ||
|  | 08dfe237c8 | ||
|  | 444ff34396 | ||
|  | a79bc70737 | ||
|  | a439fed166 | ||
|  | 8fb0c3dce4 | ||
|  | f5e1cf8790 | ||
|  | e7d267f178 | ||
|  | 06f3b622b8 | ||
|  | 74bcc562a9 | ||
|  | 55dd2aa5ab | ||
|  | 5b60c98769 | ||
|  | 207d8a2624 | ||
|  | 92fc186450 | ||
|  | 4d179abfd0 | ||
|  | 6fdfb68b9e | ||
|  | 5ac2cb3662 | ||
|  | f4ee021b8c | ||
|  | e62555370e | ||
|  | 1f15152968 | ||
|  | 21ce05c54f | ||
|  | 5e68cde779 | ||
|  | 1f4a009c67 | ||
|  | 544ce0d6b7 | ||
|  | b148d05542 | ||
|  | 970096932e | ||
|  | db936b9b55 | ||
|  | 49ad759072 | ||
|  | 8e498bfd35 | ||
|  | 46444637c6 | ||
|  | 86be40b4eb | ||
|  | 288d8af070 | ||
|  | aae403f0c9 | ||
|  | 5cc8858d8f | ||
|  | 70d0344b31 | ||
|  | 62002fb8ac | ||
|  | 03b3c30533 | ||
|  | a4b569d936 | ||
|  | eb0945d85d | ||
|  | f968507912 | ||
|  | 58c89fb8d6 | ||
|  | 16e7e20f85 | ||
|  | c7756e73b7 | ||
|  | 7ed686b223 | ||
|  | 2ab6ddb0de | ||
|  | b229439b31 | ||
|  | ffb3301bd8 | ||
|  | 62b7900fd7 | ||
|  | 61707e8b23 | ||
|  | ce70ba529c | ||
|  | cfb9dac70d | ||
|  | 105a61e689 | ||
|  | 2407314f2e | ||
|  | ff4418539c | ||
|  | 0494d05916 | ||
|  | fd268b6f5a | ||
|  | 43f3678b40 | ||
|  | 6493dc838b | ||
|  | f7905ac548 | ||
|  | 0c27938d44 | ||
|  | 587916e810 | ||
|  | 79024867de | ||
|  | 4aec1f1cf5 | ||
|  | 68f054169b | ||
|  | ea8be22e50 | ||
|  | a5054b398b | ||
|  | a438114173 | ||
|  | 9cb4f27112 | ||
|  | c38e45e9dc | ||
|  | 1f50fedb5f | ||
|  | c7a0bf1ffc | ||
|  | 940738e86a | ||
|  | 8c1e2bddff | ||
|  | 01eea0aa42 | ||
|  | 55df1e43e3 | ||
|  | e9424e241f | ||
|  | d0ac926b56 | ||
|  | 00d5114717 | ||
|  | 3a496f3b8a | ||
|  | fad2e09840 | ||
|  | e09e80f5ee | ||
|  | 2e276e7edd | ||
|  | dffc21725c | ||
|  | 96f0f2cf7e | ||
|  | 225b16d48e | ||
|  | 0ebbb2ed2e | ||
|  | 2fea950644 | ||
|  | d0a97a5f73 | ||
|  | 295b938d51 | ||
|  | e8605202ab | ||
|  | 2a8183bdf0 | ||
|  | 478f82f47e | ||
|  | f37b0ad652 | ||
|  | 3b78cbfdc1 | ||
|  | f3d7f443a0 | ||
|  | e564f0fd84 | ||
|  | 0fc20d14b3 | ||
|  | a4316ee4c5 | ||
|  | 2128a308eb | ||
|  | 43fedb656b | ||
|  | 53bdb7f82a | ||
|  | 6462dd3963 | ||
|  | e1977fcd22 | ||
|  | f97296e0ce | ||
|  | 20259cb307 | ||
|  | ffa4e5938c | ||
|  | c0c6d70fe9 | ||
|  | 8c6c5d2bcd | ||
|  | a62fcf786a | ||
|  | 4d9b59c3ef | ||
|  | bd0efb0bea | ||
|  | 8fbbd656c7 | ||
|  | b35cba613a | ||
|  | 8dffadb8da | ||
|  | 408f25081e | ||
|  | 2001550f7d | ||
|  | a3ab8c263d | ||
|  | efac20b6bb | ||
|  | 0bbd8922ea | ||
|  | 28b8cc6283 | ||
|  | 3f52f0e6c5 | ||
|  | 3da1f8352e | ||
|  | 5ea1817dc2 | ||
|  | 49d42e979e | ||
|  | 3a3b220751 | ||
|  | ab22f4c421 | ||
|  | 8b843e5bed | 
							
								
								
									
										16
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										16
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -2,7 +2,9 @@ | ||||
| *.o | ||||
| *.lo | ||||
| *.la | ||||
| Transceiver52M/osmo-trx | ||||
| Transceiver52M/osmo-trx-uhd | ||||
| Transceiver52M/osmo-trx-usrp1 | ||||
| Transceiver52M/osmo-trx-lms | ||||
|  | ||||
| # tests | ||||
| tests/CommonLibs/BitVectorTest | ||||
| @@ -16,6 +18,7 @@ tests/CommonLibs/URLEncodeTest | ||||
| tests/CommonLibs/VectorTest | ||||
| tests/CommonLibs/PRBSTest | ||||
| tests/Transceiver52M/convolve_test | ||||
| tests/Transceiver52M/LMSDeviceTest | ||||
|  | ||||
| # automake/autoconf | ||||
| *.in | ||||
| @@ -49,3 +52,14 @@ tests/testsuite.log | ||||
|  | ||||
| # vim | ||||
| *.sw? | ||||
|  | ||||
| # manuals | ||||
| doc/manuals/*.html | ||||
| doc/manuals/*.svg | ||||
| doc/manuals/*.pdf | ||||
| doc/manuals/*__*.png | ||||
| doc/manuals/*.check | ||||
| doc/manuals/generated/ | ||||
| doc/manuals/osmomsc-usermanual.xml | ||||
| doc/manuals/common | ||||
| doc/manuals/build | ||||
|   | ||||
| @@ -29,6 +29,25 @@ | ||||
| #include "LinkedLists.h" | ||||
|  | ||||
|  | ||||
| PointerFIFO::~PointerFIFO() | ||||
| { | ||||
| 	ListNode *node, *next; | ||||
|  | ||||
| 	node = mHead; | ||||
| 	while (node != NULL) { | ||||
| 		next = node->next(); | ||||
| 		delete node; | ||||
| 		node = next; | ||||
| 	} | ||||
|  | ||||
| 	node = mFreeList; | ||||
| 	while (node != NULL) { | ||||
| 		next = node->next(); | ||||
| 		delete node; | ||||
| 		node = next; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void PointerFIFO::push_front(void* val)	// by pat | ||||
| { | ||||
| 	// Pat added this routine for completeness, but never used or tested. | ||||
|   | ||||
| @@ -70,6 +70,7 @@ class PointerFIFO { | ||||
| 		:mHead(NULL),mTail(NULL),mFreeList(NULL), | ||||
| 		mSize(0) | ||||
| 	{} | ||||
| 	~PointerFIFO(); | ||||
|  | ||||
| 	unsigned size() const { return mSize; } | ||||
| 	unsigned totalSize() const { return 0; }	// Not used in this version. | ||||
|   | ||||
| @@ -1,7 +1,5 @@ | ||||
| /* | ||||
| * Copyright 2009, 2010 Free Software Foundation, Inc. | ||||
| * Copyright 2010 Kestrel Signal Processing, Inc. | ||||
| * Copyright 2011, 2012 Range Networks, Inc. | ||||
| * Copyright (C) 2018 sysmocom - s.f.m.c. GmbH | ||||
| * | ||||
| * | ||||
| * This software is distributed under the terms of the GNU Affero Public License. | ||||
| @@ -39,55 +37,6 @@ using namespace std; | ||||
|  | ||||
| Mutex gLogToLock; | ||||
|  | ||||
| // Global log level threshold: | ||||
| int config_log_level; | ||||
|  | ||||
| /** Names of the logging levels. */ | ||||
| const char *levelNames[] = { | ||||
| 	"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG" | ||||
| }; | ||||
| int numLevels = 8; | ||||
|  | ||||
|  | ||||
| int levelStringToInt(const string& name) | ||||
| { | ||||
| 	// Reverse search, since the numerically larger levels are more common. | ||||
| 	for (int i=numLevels-1; i>=0; i--) { | ||||
| 		if (name == levelNames[i]) return i; | ||||
| 	} | ||||
|  | ||||
| 	// Common substitutions. | ||||
| 	if (name=="INFORMATION") return 6; | ||||
| 	if (name=="WARN") return 4; | ||||
| 	if (name=="ERROR") return 3; | ||||
| 	if (name=="CRITICAL") return 2; | ||||
| 	if (name=="EMERGENCY") return 0; | ||||
|  | ||||
| 	// Unknown level. | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| static std::string format(const char *fmt, ...) | ||||
| { | ||||
| 	va_list ap; | ||||
| 	char buf[300]; | ||||
| 	va_start(ap,fmt); | ||||
| 	int n = vsnprintf(buf,300,fmt,ap); | ||||
| 	va_end(ap); | ||||
| 	if (n >= (300-4)) { strcpy(&buf[(300-4)],"..."); } | ||||
| 	return std::string(buf); | ||||
| } | ||||
|  | ||||
| const std::string timestr() | ||||
| { | ||||
| 	struct timeval tv; | ||||
| 	struct tm tm; | ||||
| 	gettimeofday(&tv,NULL); | ||||
| 	localtime_r(&tv.tv_sec,&tm); | ||||
| 	unsigned tenths = tv.tv_usec / 100000;	// Rounding down is ok. | ||||
| 	return format(" %02d:%02d:%02d.%1d",tm.tm_hour,tm.tm_min,tm.tm_sec,tenths); | ||||
| } | ||||
|  | ||||
| std::ostream& operator<<(std::ostream& os, std::ostringstream& ss) | ||||
| { | ||||
| 	return os << ss.str(); | ||||
| @@ -95,34 +44,21 @@ std::ostream& operator<<(std::ostream& os, std::ostringstream& ss) | ||||
|  | ||||
| Log::~Log() | ||||
| { | ||||
| 	// Anything at or above LOG_CRIT is an "alarm". | ||||
| 	if (mPriority <= LOG_ERR) { | ||||
| 		cerr << mStream.str() << endl; | ||||
| 	} | ||||
|  | ||||
| 	int old_state; | ||||
| 	pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &old_state); | ||||
| 	int mlen = mStream.str().size(); | ||||
| 	int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n'); | ||||
| 	const char *fmt = neednl ? "%s\n" : "%s"; | ||||
| 	ScopedLock lock(gLogToLock); | ||||
| 	// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers, | ||||
| 	// so just use std::cout. | ||||
| 	std::cout << mStream.str(); | ||||
| 	if (neednl) std::cout<<"\n"; | ||||
| 	LOGPSRC(mCategory, mPriority, filename, line, fmt, mStream.str().c_str()); | ||||
| 	pthread_setcancelstate(old_state, NULL); | ||||
| } | ||||
|  | ||||
| ostringstream& Log::get() | ||||
| { | ||||
| 	assert(mPriority<numLevels); | ||||
| 	mStream << levelNames[mPriority] <<  ' '; | ||||
| 	return mStream; | ||||
| } | ||||
|  | ||||
|  | ||||
|  | ||||
| void gLogInit(const char* level, char *fn) | ||||
| { | ||||
| 	// Set the level if one has been specified. | ||||
| 	if (level) | ||||
| 		config_log_level = levelStringToInt(level); | ||||
| } | ||||
|  | ||||
| // vim: ts=4 sw=4 | ||||
|   | ||||
| @@ -23,12 +23,6 @@ | ||||
|  | ||||
| */ | ||||
|  | ||||
| // (pat) WARNING is stupidly defined in /usr/local/include/osipparser2/osip_const.h. | ||||
| // This must be outside the #ifndef LOGGER_H to fix it as long as Logger.h included after the above file. | ||||
| #ifdef WARNING | ||||
| #undef WARNING | ||||
| #endif | ||||
|  | ||||
| #ifndef LOGGER_H | ||||
| #define LOGGER_H | ||||
|  | ||||
| @@ -37,30 +31,30 @@ | ||||
| #include <sstream> | ||||
| #include <string> | ||||
|  | ||||
| extern int config_log_level; | ||||
| extern "C" { | ||||
| #include <osmocom/core/logging.h> | ||||
| #include "debug.h" | ||||
| } | ||||
|  | ||||
| #define LOG_EMERG       0       /* system is unusable */ | ||||
| #define LOG_ALERT       1       /* action must be taken immediately */ | ||||
| #define LOG_CRIT        2       /* critical conditions */ | ||||
| #define LOG_ERR         3       /* error conditions */ | ||||
| #define LOG_WARNING     4       /* warning conditions */ | ||||
| #define LOG_NOTICE      5       /* normal but significant condition */ | ||||
| #define LOG_INFO        6       /* informational */ | ||||
| #define LOG_DEBUG       7       /* debug-level messages */ | ||||
|  | ||||
| #define _LOG(level) \ | ||||
| 	Log(LOG_##level).get() << pthread_self() \ | ||||
| 	<< timestr() << " " __FILE__  ":"  << __LINE__ << ":" << __FUNCTION__ << ": " | ||||
|  | ||||
| #define IS_LOG_LEVEL(wLevel) (config_log_level>=LOG_##wLevel) | ||||
|  | ||||
| #ifdef NDEBUG | ||||
| #define LOG(wLevel) \ | ||||
| 	if (LOG_##wLevel!=LOG_DEBUG && IS_LOG_LEVEL(wLevel)) _LOG(wLevel) | ||||
| #else | ||||
| #define LOG(wLevel) \ | ||||
| 	if (IS_LOG_LEVEL(wLevel)) _LOG(wLevel) | ||||
| /* Translation for old log statements */ | ||||
| #ifndef LOGL_ALERT | ||||
| #define LOGL_ALERT LOGL_FATAL | ||||
| #endif | ||||
| #ifndef LOGL_ERR | ||||
| #define LOGL_ERR LOGL_ERROR | ||||
| #endif | ||||
| #ifndef LOGL_WARNING | ||||
| #define LOGL_WARNING LOGL_NOTICE | ||||
| #endif | ||||
|  | ||||
| #define LOG(level) \ | ||||
| 	Log(DMAIN, LOGL_##level, __BASE_FILE__, __LINE__).get() <<  "[tid=" << pthread_self() << "] " | ||||
|  | ||||
| #define LOGC(category, level) \ | ||||
| 	Log(category, LOGL_##level, __BASE_FILE__, __LINE__).get() <<  "[tid=" << pthread_self() << "] " | ||||
|  | ||||
| #define LOGLV(category, level) \ | ||||
| 	Log(category, level, __BASE_FILE__, __LINE__).get() <<  "[tid=" << pthread_self() << "] " | ||||
|  | ||||
| /** | ||||
| 	A C++ stream-based thread-safe logger. | ||||
| @@ -73,13 +67,17 @@ class Log { | ||||
|  | ||||
| 	protected: | ||||
|  | ||||
| 	std::ostringstream mStream;		///< This is where we buffer up the log entry. | ||||
| 	int mPriority;					///< Priority of current report. | ||||
| 	std::ostringstream mStream;	///< This is where we buffer up the log entry. | ||||
| 	int mCategory;			///< Priority of current report. | ||||
| 	int mPriority;			///< Category of current report. | ||||
| 	const char *filename;		///< Source File Name of current report. | ||||
| 	int line;			///< Line number in source file of current report. | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	Log(int wPriority) | ||||
| 		:mPriority(wPriority) | ||||
| 	Log(int wCategory, int wPriority, const char* filename, int line) | ||||
| 		: mCategory(wCategory), mPriority(wPriority), | ||||
| 		  filename(filename), line(line) | ||||
| 	{ } | ||||
|  | ||||
| 	// Most of the work is in the destructor. | ||||
| @@ -89,16 +87,8 @@ class Log { | ||||
| 	std::ostringstream& get(); | ||||
| }; | ||||
|  | ||||
| const std::string timestr();		// A timestamp to print in messages. | ||||
| std::ostream& operator<<(std::ostream& os, std::ostringstream& ss); | ||||
|  | ||||
| /**@ Global control and initialization of the logging system. */ | ||||
| //@{ | ||||
| /** Initialize the global logging system. */ | ||||
| void gLogInit(const char* level=NULL, char* fn=NULL); | ||||
| //@} | ||||
|  | ||||
|  | ||||
| #endif | ||||
|  | ||||
| // vim: ts=4 sw=4 | ||||
|   | ||||
| @@ -22,11 +22,8 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES) | ||||
| AM_CXXFLAGS = -Wall -O3 -g -lpthread | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	example.config \ | ||||
| 	README.common | ||||
| AM_CXXFLAGS = -Wall -O3 -g -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) | ||||
| AM_CFLAGS = $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) | ||||
|  | ||||
| noinst_LTLIBRARIES = libcommon.la | ||||
|  | ||||
| @@ -36,7 +33,11 @@ libcommon_la_SOURCES = \ | ||||
| 	Sockets.cpp \ | ||||
| 	Threads.cpp \ | ||||
| 	Timeval.cpp \ | ||||
| 	Logger.cpp | ||||
| 	Logger.cpp \ | ||||
| 	Utils.cpp \ | ||||
| 	trx_vty.c \ | ||||
| 	debug.c | ||||
| libcommon_la_LIBADD = $(LIBOSMOCORE_LIBS) $(LIBOSMOCTRL_LIBS) $(LIBOSMOVTY_LIBS) | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
| 	BitVector.h \ | ||||
| @@ -47,4 +48,9 @@ noinst_HEADERS = \ | ||||
| 	Threads.h \ | ||||
| 	Timeval.h \ | ||||
| 	Vector.h \ | ||||
| 	Logger.h | ||||
| 	Logger.h \ | ||||
| 	Utils.h \ | ||||
| 	trx_vty.h \ | ||||
| 	debug.h \ | ||||
| 	osmo_signal.h \ | ||||
| 	config_defs.h | ||||
|   | ||||
| @@ -24,11 +24,17 @@ | ||||
| */ | ||||
|  | ||||
|  | ||||
|  | ||||
|  | ||||
| #include <string.h> | ||||
| #include <sys/types.h> | ||||
|  | ||||
| #include "Threads.h" | ||||
| #include "Timeval.h" | ||||
| #include "Logger.h" | ||||
|  | ||||
| #ifndef gettid | ||||
| #include <sys/syscall.h> | ||||
| #define gettid() syscall(SYS_gettid) | ||||
| #endif | ||||
|  | ||||
|  | ||||
| using namespace std; | ||||
| @@ -102,6 +108,19 @@ void Signal::wait(Mutex& wMutex, unsigned timeout) const | ||||
| 	pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime); | ||||
| } | ||||
|  | ||||
| void set_selfthread_name(const char *name) | ||||
| { | ||||
| 	pthread_t selfid = pthread_self(); | ||||
| 	pid_t tid = gettid(); | ||||
| 	if (pthread_setname_np(selfid, name) == 0) { | ||||
| 		LOG(INFO) << "Thread "<< selfid << " (task " << tid << ") set name: " << name; | ||||
| 	} else { | ||||
| 		char buf[256]; | ||||
| 		int err = errno; | ||||
| 		char* err_str = strerror_r(err, buf, sizeof(buf)); | ||||
| 		LOG(NOTICE) << "Thread "<< selfid << " (task " << tid << ") set name \"" << name << "\" failed: (" << err << ") " << err_str; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| void Thread::start(void *(*task)(void*), void *arg) | ||||
| { | ||||
|   | ||||
| @@ -141,6 +141,8 @@ class Signal { | ||||
| #define START_THREAD(thread,function,argument) \ | ||||
| 	thread.start((void *(*)(void*))function, (void*)argument); | ||||
|  | ||||
| void set_selfthread_name(const char *name); | ||||
|  | ||||
| /** A C++ wrapper for pthread threads.  */ | ||||
| class Thread { | ||||
|  | ||||
|   | ||||
| @@ -27,43 +27,49 @@ | ||||
|  | ||||
| #include "Timeval.h" | ||||
|  | ||||
| extern "C" { | ||||
| #include <osmocom/core/timer.h> | ||||
| } | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| void Timeval::now() | ||||
| { | ||||
| 	osmo_clock_gettime(CLOCK_REALTIME, &mTimespec); | ||||
| } | ||||
|  | ||||
| void Timeval::future(unsigned offset) | ||||
| { | ||||
| 	now(); | ||||
| 	unsigned sec = offset/1000; | ||||
| 	unsigned msec = offset%1000; | ||||
| 	mTimeval.tv_usec += msec*1000; | ||||
| 	mTimeval.tv_sec += sec; | ||||
| 	if (mTimeval.tv_usec>1000000) { | ||||
| 		mTimeval.tv_usec -= 1000000; | ||||
| 		mTimeval.tv_sec += 1; | ||||
| 	mTimespec.tv_nsec += msec*1000*1000; | ||||
| 	mTimespec.tv_sec += sec; | ||||
| 	if (mTimespec.tv_nsec > 1000*1000*1000) { | ||||
| 		mTimespec.tv_nsec -= 1000*1000*1000; | ||||
| 		mTimespec.tv_sec += 1; | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| struct timespec Timeval::timespec() const | ||||
| { | ||||
| 	struct timespec retVal; | ||||
| 	retVal.tv_sec = mTimeval.tv_sec; | ||||
| 	retVal.tv_nsec = 1000 * (long)mTimeval.tv_usec; | ||||
| 	return retVal; | ||||
| 	return mTimespec; | ||||
| } | ||||
|  | ||||
|  | ||||
| bool Timeval::passed() const | ||||
| { | ||||
| 	Timeval nowTime; | ||||
| 	if (nowTime.mTimeval.tv_sec < mTimeval.tv_sec) return false; | ||||
| 	if (nowTime.mTimeval.tv_sec > mTimeval.tv_sec) return true; | ||||
| 	if (nowTime.mTimeval.tv_usec > mTimeval.tv_usec) return true; | ||||
| 	if (nowTime.mTimespec.tv_sec < mTimespec.tv_sec) return false; | ||||
| 	if (nowTime.mTimespec.tv_sec > mTimespec.tv_sec) return true; | ||||
| 	if (nowTime.mTimespec.tv_nsec >= mTimespec.tv_nsec) return true; | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| double Timeval::seconds() const | ||||
| { | ||||
| 	return ((double)mTimeval.tv_sec) + 1e-6*((double)mTimeval.tv_usec); | ||||
| 	return ((double)mTimespec.tv_sec) + 1e-9*((double)mTimespec.tv_nsec); | ||||
| } | ||||
|  | ||||
|  | ||||
| @@ -72,8 +78,8 @@ long Timeval::delta(const Timeval& other) const | ||||
| { | ||||
| 	// 2^31 milliseconds is just over 4 years. | ||||
| 	int32_t deltaS = other.sec() - sec(); | ||||
| 	int32_t deltaUs = other.usec() - usec(); | ||||
| 	return 1000*deltaS + deltaUs/1000; | ||||
| 	int32_t deltaNs = other.nsec() - nsec(); | ||||
| 	return 1000*deltaS + deltaNs/1000000; | ||||
| } | ||||
| 	 | ||||
|  | ||||
| @@ -89,7 +95,7 @@ ostream& operator<<(ostream& os, const Timeval& tv) | ||||
|  | ||||
| ostream& operator<<(ostream& os, const struct timespec& ts) | ||||
| { | ||||
| 	os << ts.tv_sec << "," << ts.tv_nsec; | ||||
| 	os << ts.tv_sec << "," << ts.tv_nsec/1000; | ||||
| 	return os; | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -42,12 +42,12 @@ class Timeval { | ||||
|  | ||||
| 	private: | ||||
|  | ||||
| 	struct timeval mTimeval; | ||||
| 	struct timespec mTimespec; | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| 	/** Set the value to gettimeofday. */ | ||||
| 	void now() { gettimeofday(&mTimeval,NULL); } | ||||
| 	/** Set the value to current time. */ | ||||
| 	void now(); | ||||
|  | ||||
| 	/** Set the value to gettimeofday plus an offset. */ | ||||
| 	void future(unsigned ms); | ||||
| @@ -55,16 +55,18 @@ class Timeval { | ||||
| 	//@{ | ||||
| 	Timeval(unsigned sec, unsigned usec) | ||||
| 	{ | ||||
| 		mTimeval.tv_sec = sec; | ||||
| 		mTimeval.tv_usec = usec; | ||||
| 		mTimespec.tv_sec = sec; | ||||
| 		mTimespec.tv_nsec = usec*1000; | ||||
| 	} | ||||
|  | ||||
| 	Timeval(const struct timeval& wTimeval) | ||||
| 		:mTimeval(wTimeval) | ||||
| 	{} | ||||
| 	{ | ||||
| 		mTimespec.tv_sec = wTimeval.tv_sec; | ||||
| 		mTimespec.tv_nsec = wTimeval.tv_sec*1000; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 		Create a Timeval offset into the future. | ||||
| 		Create a Timespec offset into the future. | ||||
| 		@param offset milliseconds | ||||
| 	*/ | ||||
| 	Timeval(unsigned offset=0) { future(offset); } | ||||
| @@ -76,8 +78,9 @@ class Timeval { | ||||
| 	/** Return total seconds. */ | ||||
| 	double seconds() const; | ||||
|  | ||||
| 	uint32_t sec() const { return mTimeval.tv_sec; } | ||||
| 	uint32_t usec() const { return mTimeval.tv_usec; } | ||||
| 	uint32_t sec() const { return mTimespec.tv_sec; } | ||||
| 	uint32_t usec() const { return mTimespec.tv_nsec / 1000; } | ||||
| 	uint32_t nsec() const { return mTimespec.tv_nsec; } | ||||
|  | ||||
| 	/** Return differnce from other (other-self), in ms. */ | ||||
| 	long delta(const Timeval& other) const; | ||||
| @@ -88,11 +91,11 @@ class Timeval { | ||||
| 	/** Remaining time in ms. */ | ||||
| 	long remaining() const { return -elapsed(); } | ||||
|  | ||||
| 	/** Return true if the time has passed, as per gettimeofday. */ | ||||
| 	/** Return true if the time has passed, as per clock_gettime(CLOCK_REALTIME). */ | ||||
| 	bool passed() const; | ||||
|  | ||||
| 	/** Add a given number of minutes to the time. */ | ||||
| 	void addMinutes(unsigned minutes) { mTimeval.tv_sec += minutes*60; } | ||||
| 	void addMinutes(unsigned minutes) { mTimespec.tv_sec += minutes*60; } | ||||
|  | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										36
									
								
								CommonLibs/Utils.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								CommonLibs/Utils.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| /* | ||||
|  * Copyright 2018 sysmocom - s.f.m.c. GmbH | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library 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 | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #include <vector> | ||||
| #include <string> | ||||
| #include <sstream> | ||||
|  | ||||
| std::vector<std::string> comma_delimited_to_vector(const char* opt) | ||||
| { | ||||
| 	std::string str = std::string(opt); | ||||
| 	std::vector<std::string> result; | ||||
| 	std::stringstream ss(str); | ||||
|  | ||||
| 	while( ss.good() ) | ||||
| 	{ | ||||
| 	    std::string substr; | ||||
| 	    getline(ss, substr, ','); | ||||
| 	    result.push_back(substr); | ||||
| 	} | ||||
| 	return result; | ||||
| } | ||||
							
								
								
									
										24
									
								
								CommonLibs/Utils.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								CommonLibs/Utils.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,24 @@ | ||||
| /* | ||||
|  * Copyright 2018 sysmocom - s.f.m.c. GmbH | ||||
|  * | ||||
|  * This library is free software; you can redistribute it and/or | ||||
|  * modify it under the terms of the GNU Lesser General Public | ||||
|  * License as published by the Free Software Foundation; either | ||||
|  * version 2.1 of the License, or (at your option) any later version. | ||||
|  * | ||||
|  * This library 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 | ||||
|  * Lesser General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Lesser General Public | ||||
|  * License along with this library; if not, write to the Free Software | ||||
|  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <vector> | ||||
| #include <string> | ||||
|  | ||||
| std::vector<std::string> comma_delimited_to_vector(const char* opt); | ||||
| @@ -32,11 +32,14 @@ | ||||
| #include <string.h> | ||||
| #include <iostream> | ||||
| #include <assert.h> | ||||
| #include <stdlib.h> | ||||
|  | ||||
| // We cant use Logger.h in this file... | ||||
| extern int gVectorDebug; | ||||
| #define BVDEBUG(msg) if (gVectorDebug) {std::cout << msg;} | ||||
|  | ||||
|  | ||||
| typedef void (*vector_free_func)(void* wData); | ||||
| typedef void *(*vector_alloc_func)(size_t newSize); | ||||
|  | ||||
| /** | ||||
| 	A simplified Vector template with aliases. | ||||
| @@ -60,6 +63,8 @@ template <class T> class Vector { | ||||
| 	T* mData;		///< allocated data block, if any | ||||
| 	T* mStart;		///< start of useful data | ||||
| 	T* mEnd;		///< end of useful data + 1 | ||||
| 	vector_alloc_func mAllocFunc; ///< function used to alloc new mData during resize. | ||||
| 	vector_free_func mFreeFunc; ///< function used to free mData. | ||||
|  | ||||
| 	public: | ||||
|  | ||||
| @@ -85,9 +90,19 @@ template <class T> class Vector { | ||||
| 	/** Change the size of the Vector, discarding content. */ | ||||
| 	void resize(size_t newSize) | ||||
| 	{ | ||||
| 		if (mData!=NULL) delete[] mData; | ||||
| 		if (mData!=NULL) { | ||||
| 			if (mFreeFunc) | ||||
| 				mFreeFunc(mData); | ||||
| 			else | ||||
| 				delete[] mData; | ||||
| 		} | ||||
| 		if (newSize==0) mData=NULL; | ||||
| 		else mData = new T[newSize]; | ||||
| 		else { | ||||
| 			if (mAllocFunc) | ||||
| 				mData = (T*) mAllocFunc(newSize); | ||||
| 			else | ||||
| 				mData = new T[newSize]; | ||||
| 		} | ||||
| 		mStart = mData; | ||||
| 		mEnd = mStart + newSize; | ||||
| 	} | ||||
| @@ -107,7 +122,7 @@ template <class T> class Vector { | ||||
| 	void clone(const Vector<T>& other) | ||||
| 	{ | ||||
| 		resize(other.size()); | ||||
| 		memcpy(mData,other.mStart,other.bytes()); | ||||
| 		other.copyTo(*this); | ||||
| 	} | ||||
|  | ||||
|  | ||||
| @@ -116,29 +131,31 @@ template <class T> class Vector { | ||||
| 	//@{ | ||||
|  | ||||
| 	/** Build an empty Vector of a given size. */ | ||||
| 	Vector(size_t wSize=0):mData(NULL) { resize(wSize); } | ||||
| 	Vector(size_t wSize=0, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL) | ||||
| 		:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc) | ||||
| 	{ resize(wSize); } | ||||
|  | ||||
| 	/** Build a Vector by moving another. */ | ||||
| 	Vector(Vector<T>&& other) | ||||
| 		:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd) | ||||
| 		:mData(other.mData),mStart(other.mStart),mEnd(other.mEnd), mAllocFunc(other.mAllocFunc),  mFreeFunc(other.mFreeFunc) | ||||
| 	{ other.mData=NULL; } | ||||
|  | ||||
| 	/** Build a Vector by copying another. */ | ||||
| 	Vector(const Vector<T>& other):mData(NULL) { clone(other); } | ||||
| 	Vector(const Vector<T>& other):mData(NULL), mAllocFunc(other.mAllocFunc), mFreeFunc(other.mFreeFunc) { clone(other); } | ||||
|  | ||||
| 	/** Build a Vector with explicit values. */ | ||||
| 	Vector(T* wData, T* wStart, T* wEnd) | ||||
| 		:mData(wData),mStart(wStart),mEnd(wEnd) | ||||
| 	Vector(T* wData, T* wStart, T* wEnd, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL) | ||||
| 		:mData(wData),mStart(wStart),mEnd(wEnd), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc) | ||||
| 	{ } | ||||
|  | ||||
| 	/** Build a vector from an existing block, NOT to be deleted upon destruction. */ | ||||
| 	Vector(T* wStart, size_t span) | ||||
| 		:mData(NULL),mStart(wStart),mEnd(wStart+span) | ||||
| 	Vector(T* wStart, size_t span, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL) | ||||
| 		:mData(NULL),mStart(wStart),mEnd(wStart+span),mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc) | ||||
| 	{ } | ||||
|  | ||||
| 	/** Build a Vector by concatenation. */ | ||||
| 	Vector(const Vector<T>& other1, const Vector<T>& other2) | ||||
| 		:mData(NULL) | ||||
| 	Vector(const Vector<T>& other1, const Vector<T>& other2, vector_alloc_func wAllocFunc=NULL, vector_free_func wFreeFunc=NULL) | ||||
| 		:mData(NULL), mAllocFunc(wAllocFunc), mFreeFunc(wFreeFunc) | ||||
| 	{ | ||||
| 		resize(other1.size()+other2.size()); | ||||
| 		memcpy(mStart, other1.mStart, other1.bytes()); | ||||
| @@ -162,6 +179,8 @@ template <class T> class Vector { | ||||
| 		mData=other.mData; | ||||
| 		mStart=other.mStart; | ||||
| 		mEnd=other.mEnd; | ||||
| 		mAllocFunc=other.mAllocFunc; | ||||
| 		mFreeFunc=other.mFreeFunc; | ||||
| 		other.mData=NULL; | ||||
| 	} | ||||
|  | ||||
| @@ -204,10 +223,15 @@ template <class T> class Vector { | ||||
| 	*/ | ||||
| 	void copyToSegment(Vector<T>& other, size_t start, size_t span) const | ||||
| 	{ | ||||
| 		T* base = other.mStart + start; | ||||
| 		assert(base+span<=other.mEnd); | ||||
| 		unsigned int i; | ||||
| 		T* dst = other.mStart + start; | ||||
| 		T* src = mStart; | ||||
| 		assert(dst+span<=other.mEnd); | ||||
| 		assert(mStart+span<=mEnd); | ||||
| 		memcpy(base,mStart,span*sizeof(T)); | ||||
| 		for (i = 0; i < span; i++, src++, dst++) | ||||
| 			*dst = *src; | ||||
| 		/*TODO if not non-trivially copyable type class, optimize: | ||||
| 		memcpy(dst,mStart,span*sizeof(T)); */ | ||||
| 	} | ||||
|  | ||||
| 	/** Copy all of this Vector to a segment of another Vector. */ | ||||
| @@ -282,7 +306,7 @@ template <class T> class Vector { | ||||
| 	T* end() { return mEnd; } | ||||
| 	bool isOwner() { return !!mData; }	// Do we own any memory ourselves? | ||||
| 	//@} | ||||
| 	 | ||||
|  | ||||
|  | ||||
| }; | ||||
|  | ||||
|   | ||||
							
								
								
									
										20
									
								
								CommonLibs/config_defs.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								CommonLibs/config_defs.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,20 @@ | ||||
| #pragma once | ||||
|  | ||||
| /* | ||||
|  * This file contains structures used by both VTY (C, dir CommonLibs) and | ||||
|  * osmo-trx (CXX, dir Transceiver52) | ||||
|  */ | ||||
|  | ||||
| enum FillerType { | ||||
|   FILLER_DUMMY, | ||||
|   FILLER_ZERO, | ||||
|   FILLER_NORM_RAND, | ||||
|   FILLER_EDGE_RAND, | ||||
|   FILLER_ACCESS_RAND, | ||||
| }; | ||||
|  | ||||
| enum ReferenceType { | ||||
|   REF_INTERNAL, | ||||
|   REF_EXTERNAL, | ||||
|   REF_GPS, | ||||
| }; | ||||
							
								
								
									
										36
									
								
								CommonLibs/debug.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								CommonLibs/debug.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,36 @@ | ||||
| #include <osmocom/core/logging.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include "debug.h" | ||||
|  | ||||
| /* default categories */ | ||||
| static const struct log_info_cat default_categories[] = { | ||||
| 	[DMAIN] = { | ||||
| 		.name = "DMAIN", | ||||
| 		.description = "Main generic category", | ||||
| 		.color = NULL, | ||||
| 		.enabled = 1, .loglevel = LOGL_NOTICE, | ||||
| 	}, | ||||
| 	[DTRXCTRL] = { | ||||
| 			.name = "DTRXCTRL", | ||||
| 			.description = "TRX CTRL interface", | ||||
| 			.color = "\033[1;33m", | ||||
| 			.enabled = 1, .loglevel = LOGL_NOTICE, | ||||
| 	}, | ||||
| 	[DDEV] = { | ||||
| 		.name = "DDEV", | ||||
| 		.description = "Device/Driver specific code", | ||||
| 		.color = NULL, | ||||
| 		.enabled = 1, .loglevel = LOGL_INFO, | ||||
| 	}, | ||||
| 	[DLMS] = { | ||||
| 		.name = "DLMS", | ||||
| 		.description = "Logging from within LimeSuite itself", | ||||
| 		.color = NULL, | ||||
| 		.enabled = 1, .loglevel = LOGL_NOTICE, | ||||
| 	}, | ||||
| }; | ||||
|  | ||||
| const struct log_info log_info = { | ||||
| 	.cat = default_categories, | ||||
| 	.num_cat = ARRAY_SIZE(default_categories), | ||||
| }; | ||||
							
								
								
									
										11
									
								
								CommonLibs/debug.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								CommonLibs/debug.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| #pragma once | ||||
|  | ||||
| extern const struct log_info log_info; | ||||
|  | ||||
| /* Debug Areas of the code */ | ||||
| enum { | ||||
| 	DMAIN, | ||||
| 	DTRXCTRL, | ||||
| 	DDEV, | ||||
| 	DLMS, | ||||
| }; | ||||
							
								
								
									
										35
									
								
								CommonLibs/osmo_signal.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								CommonLibs/osmo_signal.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,35 @@ | ||||
| /* Generic signalling/notification infrastructure */ | ||||
| /* (C) 2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de> | ||||
|  * | ||||
|  * Author: Pau Espin Pedrol <pespin@sysmocom.de> | ||||
|  * | ||||
|  * All Rights Reserved | ||||
|  * | ||||
|  * This program is free software; you can redistribute it and/or modify | ||||
|  * it under the terms of the GNU Affero General Public License as published by | ||||
|  * the Free Software Foundation; either version 3 of the License, or | ||||
|  * (at your option) any later version. | ||||
|  * | ||||
|  * This program is distributed in the hope that it will be useful, | ||||
|  * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||
|  * GNU Affero General Public License for more details. | ||||
|  * | ||||
|  * You should have received a copy of the GNU Affero General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/core/signal.h> | ||||
|  | ||||
| /* Signalling subsystems */ | ||||
| enum signal_subsystems { | ||||
| 	SS_TRANSC, | ||||
| }; | ||||
|  | ||||
| /* SS_TRANSC signals */ | ||||
| enum SS_TRANSC { | ||||
| 	S_TRANSC_STOP_REQUIRED, /* Transceiver fatal error, it should be stopped */ | ||||
| }; | ||||
							
								
								
									
										576
									
								
								CommonLibs/trx_vty.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										576
									
								
								CommonLibs/trx_vty.c
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,576 @@ | ||||
| /* | ||||
|  * Copyright (C) 2012-2017 sysmocom - s.f.m.c. GmbH | ||||
|  * 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. | ||||
|  * | ||||
|  * You should have received a copy of the GNU General Public License | ||||
|  * along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
|  * | ||||
|  */ | ||||
|  | ||||
| #include <string.h> | ||||
| #include <stdint.h> | ||||
| #include <inttypes.h> | ||||
| #include <netinet/in.h> | ||||
| #include <arpa/inet.h> | ||||
|  | ||||
| #include <osmocom/core/talloc.h> | ||||
| #include <osmocom/core/utils.h> | ||||
| #include <osmocom/core/rate_ctr.h> | ||||
|  | ||||
| #include <osmocom/vty/command.h> | ||||
| #include <osmocom/vty/vty.h> | ||||
| #include <osmocom/vty/misc.h> | ||||
|  | ||||
| #include "trx_vty.h" | ||||
| #include "../config.h" | ||||
|  | ||||
| static struct trx_ctx* g_trx_ctx; | ||||
|  | ||||
| static const struct value_string clock_ref_names[] = { | ||||
| 	{ REF_INTERNAL,	"internal" }, | ||||
| 	{ REF_EXTERNAL,	"external" }, | ||||
| 	{ REF_GPS,	"gpsdo" }, | ||||
| 	{ 0,		NULL } | ||||
| }; | ||||
|  | ||||
| static const struct value_string filler_names[] = { | ||||
| 	{ FILLER_DUMMY,		"Dummy bursts" }, | ||||
| 	{ FILLER_ZERO,		"Disabled" }, | ||||
| 	{ FILLER_NORM_RAND,	"Normal bursts with random payload" }, | ||||
| 	{ FILLER_EDGE_RAND,	"EDGE bursts with random payload" }, | ||||
| 	{ FILLER_ACCESS_RAND,	"Access bursts with random payload" }, | ||||
| 	{ 0,			NULL } | ||||
| }; | ||||
|  | ||||
| struct trx_ctx *trx_from_vty(struct vty *v) | ||||
| { | ||||
|         /* It can't hurt to force callers to continue to pass the vty instance | ||||
|          * to this function, in case we'd like to retrieve the global | ||||
|          * trx instance from the vty at some point in the future. But | ||||
|          * until then, just return the global pointer, which should have been | ||||
|          * initialized by trx_vty_init(). | ||||
|          */ | ||||
|         OSMO_ASSERT(g_trx_ctx); | ||||
|         return g_trx_ctx; | ||||
| } | ||||
|  | ||||
| enum trx_vty_node { | ||||
| 	TRX_NODE = _LAST_OSMOVTY_NODE + 1, | ||||
| 	CHAN_NODE, | ||||
| }; | ||||
|  | ||||
| static struct cmd_node trx_node = { | ||||
| 	TRX_NODE, | ||||
| 	"%s(config-trx)# ", | ||||
| 	1, | ||||
| }; | ||||
|  | ||||
| static struct cmd_node chan_node = { | ||||
| 	CHAN_NODE, | ||||
| 	"%s(config-trx-chan)# ", | ||||
| 	1, | ||||
| }; | ||||
|  | ||||
| DEFUN(cfg_trx, cfg_trx_cmd, | ||||
| 	"trx", | ||||
| 	"Configure the TRX\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	if (!trx) | ||||
| 		return CMD_WARNING; | ||||
|  | ||||
| 	vty->node = TRX_NODE; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_bind_ip, cfg_bind_ip_cmd, | ||||
| 	"bind-ip A.B.C.D", | ||||
| 	"Set the IP address for the local bind\n" | ||||
| 	"IPv4 Address\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_remote_ip, cfg_remote_ip_cmd, | ||||
| 	"remote-ip A.B.C.D", | ||||
| 	"Set the IP address for the remote BTS\n" | ||||
| 	"IPv4 Address\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_base_port, cfg_base_port_cmd, | ||||
| 	"base-port <1-65535>", | ||||
| 	"Set the TRX Base Port\n" | ||||
| 	"TRX Base Port\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.base_port = atoi(argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_dev_args, cfg_dev_args_cmd, | ||||
| 	"dev-args DESC", | ||||
| 	"Set the device-specific arguments to pass to the device\n" | ||||
| 	"Device-specific arguments\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	osmo_talloc_replace_string(trx, &trx->cfg.dev_args, argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_tx_sps, cfg_tx_sps_cmd, | ||||
| 	"tx-sps (1|4)", | ||||
| 	"Set the Tx Samples-per-Symbol\n" | ||||
| 	"Tx Samples-per-Symbol\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.tx_sps = atoi(argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_rx_sps, cfg_rx_sps_cmd, | ||||
| 	"rx-sps (1|4)", | ||||
| 	"Set the Rx Samples-per-Symbol\n" | ||||
| 	"Rx Samples-per-Symbol\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.rx_sps = atoi(argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_test_rtsc, cfg_test_rtsc_cmd, | ||||
| 	"test rtsc <0-7>", | ||||
| 	"Set the Random Normal Burst test mode with TSC\n" | ||||
| 	"TSC\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	if (trx->cfg.rach_delay_set) { | ||||
| 		vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s", | ||||
| 			VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	trx->cfg.rtsc_set = true; | ||||
| 	trx->cfg.rtsc = atoi(argv[0]); | ||||
| 	if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */ | ||||
| 		trx->cfg.filler = FILLER_NORM_RAND; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_test_rach_delay, cfg_test_rach_delay_cmd, | ||||
| 	"test rach-delay <0-68>", | ||||
| 	"Set the Random Access Burst test mode with delay\n" | ||||
| 	"RACH delay\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	if (trx->cfg.rtsc_set) { | ||||
| 		vty_out(vty, "rach-delay and rtsc options are mutual-exclusive%s", | ||||
| 			VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	if (trx->cfg.egprs) { | ||||
| 		vty_out(vty, "rach-delay and egprs options are mutual-exclusive%s", | ||||
| 			VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	trx->cfg.rach_delay_set = true; | ||||
| 	trx->cfg.rach_delay = atoi(argv[0]); | ||||
| 	trx->cfg.filler = FILLER_ACCESS_RAND; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_clock_ref, cfg_clock_ref_cmd, | ||||
| 	"clock-ref (internal|external|gpsdo)", | ||||
| 	"Set the Reference Clock\n" | ||||
| 	"Enable internal referece (default)\n" | ||||
| 	"Enable external 10 MHz reference\n" | ||||
| 	"Enable GPSDO reference\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.clock_ref = get_string_value(clock_ref_names, argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_multi_arfcn, cfg_multi_arfcn_cmd, | ||||
| 	"multi-arfcn (disable|enable)", | ||||
| 	"Enable multi-ARFCN transceiver (default=disable)\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	if (strcmp("disable", argv[0]) == 0) { | ||||
| 		trx->cfg.multi_arfcn = false; | ||||
| 	} else if (strcmp("enable", argv[0]) == 0) { | ||||
| 		trx->cfg.multi_arfcn = true; | ||||
| 	} else { | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_offset, cfg_offset_cmd, | ||||
| 	"offset FLOAT", | ||||
| 	"Set the baseband frequency offset (default=0, auto)\n" | ||||
| 	"Baseband Frequency Offset\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.offset = atof(argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_rssi_offset, cfg_rssi_offset_cmd, | ||||
| 	"rssi-offset FLOAT", | ||||
| 	"Set the RSSI to dBm offset in dB (default=0)\n" | ||||
| 	"RSSI to dBm offset in dB\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.rssi_offset = atof(argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_swap_channels, cfg_swap_channels_cmd, | ||||
| 	"swap-channels (disable|enable)", | ||||
| 	"Swap channels (default=disable)\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	if (strcmp("disable", argv[0]) == 0) { | ||||
| 		trx->cfg.swap_channels = false; | ||||
| 	} else if (strcmp("enable", argv[0]) == 0) { | ||||
| 		trx->cfg.swap_channels = true; | ||||
| 	} else { | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_egprs, cfg_egprs_cmd, | ||||
| 	"egprs (disable|enable)", | ||||
| 	"Enable EDGE receiver (default=disable)\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	if (strcmp("disable", argv[0]) == 0) { | ||||
| 		trx->cfg.egprs = false; | ||||
| 	} else if (strcmp("enable", argv[0]) == 0) { | ||||
| 		trx->cfg.egprs = true; | ||||
| 		trx->cfg.filler = FILLER_EDGE_RAND; | ||||
| 	} else { | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_rt_prio, cfg_rt_prio_cmd, | ||||
| 	"rt-prio <1-32>", | ||||
| 	"Set the SCHED_RR real-time priority\n" | ||||
| 	"Real time priority\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.sched_rr = atoi(argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_filler, cfg_filler_cmd, | ||||
| 	"filler dummy", | ||||
| 	"Enable C0 filler table\n" | ||||
| 	"Dummy method\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx->cfg.filler = FILLER_DUMMY; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_chan, cfg_chan_cmd, | ||||
| 	"chan <0-100>", | ||||
| 	"Select a channel to configure\n" | ||||
| 	"Channel index\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
| 	int idx = atoi(argv[0]); | ||||
|  | ||||
| 	if (idx >= TRX_CHAN_MAX) { | ||||
| 		vty_out(vty, "Chan list full.%s", VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} | ||||
| 	if (trx->cfg.num_chans < idx) { /* Unexisting or creating non-consecutive */ | ||||
| 		vty_out(vty, "Non-existent or non-consecutive chan %d.%s", | ||||
| 				idx, VTY_NEWLINE); | ||||
| 		return CMD_WARNING; | ||||
| 	} else if (trx->cfg.num_chans == idx)  { /* creating it */ | ||||
| 		trx->cfg.num_chans++; | ||||
| 		trx->cfg.chans[idx].trx = trx; | ||||
| 		trx->cfg.chans[idx].idx = idx; | ||||
| 	} | ||||
|  | ||||
| 	vty->node = CHAN_NODE; | ||||
| 	vty->index = &trx->cfg.chans[idx]; | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_chan_rx_path, cfg_chan_rx_path_cmd, | ||||
| 	"rx-path NAME", | ||||
| 	"Set the Rx Path\n" | ||||
| 	"Rx Path name\n") | ||||
| { | ||||
| 	struct trx_chan *chan = vty->index; | ||||
|  | ||||
| 	osmo_talloc_replace_string(chan->trx, &chan->rx_path, argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| DEFUN(cfg_chan_tx_path, cfg_chan_tx_path_cmd, | ||||
| 	"tx-path NAME", | ||||
| 	"Set the Tx Path\n" | ||||
| 	"Tx Path name\n") | ||||
| { | ||||
| 	struct trx_chan *chan = vty->index; | ||||
|  | ||||
| 	osmo_talloc_replace_string(chan->trx, &chan->tx_path, argv[0]); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static int dummy_config_write(struct vty *v) | ||||
| { | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static int config_write_trx(struct vty *vty) | ||||
| { | ||||
| 	struct trx_chan *chan; | ||||
| 	int i; | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	vty_out(vty, "trx%s", VTY_NEWLINE); | ||||
| 	if (trx->cfg.bind_addr) | ||||
| 		vty_out(vty, " bind-ip %s%s", trx->cfg.bind_addr, VTY_NEWLINE); | ||||
| 	if (trx->cfg.remote_addr) | ||||
| 		vty_out(vty, " remote-ip %s%s", trx->cfg.remote_addr, VTY_NEWLINE); | ||||
| 	if (trx->cfg.base_port != DEFAULT_TRX_PORT) | ||||
| 		vty_out(vty, " base-port %u%s", trx->cfg.base_port, VTY_NEWLINE); | ||||
| 	if (trx->cfg.dev_args) | ||||
| 		vty_out(vty, " dev-args %s%s", trx->cfg.dev_args, VTY_NEWLINE); | ||||
| 	if (trx->cfg.tx_sps != DEFAULT_TX_SPS) | ||||
| 		vty_out(vty, " tx-sps %u%s", trx->cfg.tx_sps, VTY_NEWLINE); | ||||
| 	if (trx->cfg.rx_sps != DEFAULT_RX_SPS) | ||||
| 		vty_out(vty, " rx-sps %u%s", trx->cfg.rx_sps, VTY_NEWLINE); | ||||
| 	if (trx->cfg.rtsc_set) | ||||
| 		vty_out(vty, " test rtsc %u%s", trx->cfg.rtsc, VTY_NEWLINE); | ||||
| 	if (trx->cfg.rach_delay_set) | ||||
| 		vty_out(vty, " test rach-delay %u%s", trx->cfg.rach_delay, VTY_NEWLINE); | ||||
| 	if (trx->cfg.clock_ref != REF_INTERNAL) | ||||
| 		vty_out(vty, " clock-ref %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE); | ||||
| 	vty_out(vty, " multi-arfcn %s%s", trx->cfg.multi_arfcn ? "enable" : "disable", VTY_NEWLINE); | ||||
| 	if (trx->cfg.offset != 0) | ||||
| 		vty_out(vty, " offset %f%s", trx->cfg.offset, VTY_NEWLINE); | ||||
| 	if (trx->cfg.rssi_offset != 0) | ||||
| 		vty_out(vty, " rssi-offset %f%s", trx->cfg.rssi_offset, VTY_NEWLINE); | ||||
| 	vty_out(vty, " swap-channels %s%s", trx->cfg.swap_channels ? "enable" : "disable", VTY_NEWLINE); | ||||
| 	vty_out(vty, " egprs %s%s", trx->cfg.egprs ? "enable" : "disable", VTY_NEWLINE); | ||||
| 	if (trx->cfg.sched_rr != 0) | ||||
| 		vty_out(vty, " rt-prio %u%s", trx->cfg.sched_rr, VTY_NEWLINE); | ||||
|  | ||||
| 	for (i = 0; i < trx->cfg.num_chans; i++) { | ||||
| 		chan = &trx->cfg.chans[i]; | ||||
| 		vty_out(vty, " chan %u%s", chan->idx, VTY_NEWLINE); | ||||
| 		if (chan->rx_path) | ||||
| 			vty_out(vty, "  rx-path %s%s", chan->rx_path, VTY_NEWLINE); | ||||
| 		if (chan->tx_path) | ||||
| 			vty_out(vty, "  tx-path %s%s", chan->tx_path, VTY_NEWLINE); | ||||
| 	} | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static void trx_dump_vty(struct vty *vty, struct trx_ctx *trx) | ||||
| { | ||||
| 	struct trx_chan *chan; | ||||
| 	int i; | ||||
| 	vty_out(vty, "TRX Config:%s", VTY_NEWLINE); | ||||
| 	vty_out(vty, " Local IP: %s%s", trx->cfg.bind_addr, VTY_NEWLINE); | ||||
| 	vty_out(vty, " Remote IP: %s%s", trx->cfg.remote_addr, VTY_NEWLINE); | ||||
| 	vty_out(vty, " TRX Base Port: %u%s", trx->cfg.base_port, VTY_NEWLINE); | ||||
| 	vty_out(vty, " Device args: %s%s", trx->cfg.dev_args, VTY_NEWLINE); | ||||
| 	vty_out(vty, " Tx Samples-per-Symbol: %u%s", trx->cfg.tx_sps, VTY_NEWLINE); | ||||
| 	vty_out(vty, " Rx Samples-per-Symbol: %u%s", trx->cfg.rx_sps, VTY_NEWLINE); | ||||
| 	vty_out(vty, " Test Mode: TSC: %u (%s)%s", trx->cfg.rtsc, | ||||
| 		trx->cfg.rtsc_set ? "Enabled" : "Disabled", VTY_NEWLINE); | ||||
| 	vty_out(vty, " Test Mode: RACH Delay: %u (%s)%s", trx->cfg.rach_delay, | ||||
| 		trx->cfg.rach_delay_set ? "Enabled" : "Disabled", VTY_NEWLINE); | ||||
| 	vty_out(vty, " C0 Filler Table: %s%s", get_value_string(filler_names, trx->cfg.filler), VTY_NEWLINE); | ||||
| 	vty_out(vty, " Clock Reference: %s%s", get_value_string(clock_ref_names, trx->cfg.clock_ref), VTY_NEWLINE); | ||||
| 	vty_out(vty, " Multi-Carrier: %s%s", trx->cfg.multi_arfcn ? "Enabled" : "Disabled", VTY_NEWLINE); | ||||
| 	vty_out(vty, " Tuning offset: %f%s", trx->cfg.offset, VTY_NEWLINE); | ||||
| 	vty_out(vty, " RSSI to dBm offset: %f%s", trx->cfg.rssi_offset, VTY_NEWLINE); | ||||
| 	vty_out(vty, " Swap channels: %s%s", trx->cfg.swap_channels ? "Enabled" : "Disabled", VTY_NEWLINE); | ||||
| 	vty_out(vty, " EDGE support: %s%s", trx->cfg.egprs ? "Enabled" : "Disabled", VTY_NEWLINE); | ||||
| 	vty_out(vty, " Real Time Priority: %u (%s)%s", trx->cfg.sched_rr, | ||||
| 		trx->cfg.sched_rr ? "Enabled" : "Disabled", VTY_NEWLINE); | ||||
| 	vty_out(vty, " Channels: %u%s", trx->cfg.num_chans, VTY_NEWLINE); | ||||
| 	for (i = 0; i < trx->cfg.num_chans; i++) { | ||||
| 		chan = &trx->cfg.chans[i]; | ||||
| 		vty_out(vty, "  Channel %u:%s", chan->idx, VTY_NEWLINE); | ||||
| 		if (chan->rx_path) | ||||
| 			vty_out(vty, "   Rx Path: %s%s", chan->rx_path, VTY_NEWLINE); | ||||
| 		if (chan->tx_path) | ||||
| 			vty_out(vty, "   Tx Path: %s%s", chan->tx_path, VTY_NEWLINE); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| DEFUN(show_trx, show_trx_cmd, | ||||
| 	"show trx", | ||||
| 	SHOW_STR "Display information on the TRX\n") | ||||
| { | ||||
| 	struct trx_ctx *trx = trx_from_vty(vty); | ||||
|  | ||||
| 	trx_dump_vty(vty, trx); | ||||
|  | ||||
| 	return CMD_SUCCESS; | ||||
| } | ||||
|  | ||||
| static int trx_vty_is_config_node(struct vty *vty, int node) | ||||
| { | ||||
| 	switch (node) { | ||||
| 	case TRX_NODE: | ||||
| 	case CHAN_NODE: | ||||
| 		return 1; | ||||
| 	default: | ||||
| 		return 0; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static int trx_vty_go_parent(struct vty *vty) | ||||
| { | ||||
| 	switch (vty->node) { | ||||
| 	case TRX_NODE: | ||||
| 		vty->node = CONFIG_NODE; | ||||
| 		vty->index = NULL; | ||||
| 		vty->index_sub = NULL; | ||||
| 		break; | ||||
| 	case CHAN_NODE: | ||||
| 		vty->node = TRX_NODE; | ||||
| 		vty->index = NULL; | ||||
| 		vty->index_sub = NULL; | ||||
| 		break; | ||||
| 	default: | ||||
| 		vty->node = CONFIG_NODE; | ||||
| 		vty->index = NULL; | ||||
| 		vty->index_sub = NULL; | ||||
| 	} | ||||
|  | ||||
| 	return vty->node; | ||||
| } | ||||
|  | ||||
| static const char trx_copyright[] = | ||||
| 	"Copyright (C) 2007-2014 Free Software Foundation, Inc.\r\n" | ||||
| 	"Copyright (C) 2013 Thomas Tsou <tom@tsou.cc>\r\n" | ||||
| 	"Copyright (C) 2015 Ettus Research LLC\r\n" | ||||
| 	"Copyright (C) 2017-2018 by sysmocom s.f.m.c. GmbH <info@sysmocom.de>\r\n" | ||||
| 	"License AGPLv3+: GNU AGPL version 3 or later <http://gnu.org/licenses/agpl-3.0.html>\r\n" | ||||
| 	"This is free software: you are free to change and redistribute it.\r\n" | ||||
| 	"There is NO WARRANTY, to the extent permitted by law.\r\n"; | ||||
|  | ||||
| struct vty_app_info g_vty_info = { | ||||
| 	.name		= "OsmoTRX", | ||||
| 	.version	= PACKAGE_VERSION, | ||||
| 	.copyright	= trx_copyright, | ||||
| 	.go_parent_cb	= trx_vty_go_parent, | ||||
| 	.is_config_node	= trx_vty_is_config_node, | ||||
| }; | ||||
|  | ||||
| struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx) | ||||
| { | ||||
| 	struct trx_ctx * trx = talloc_zero(talloc_ctx, struct trx_ctx); | ||||
|  | ||||
| 	trx->cfg.bind_addr =  talloc_strdup(trx, DEFAULT_TRX_IP); | ||||
| 	trx->cfg.remote_addr = talloc_strdup(trx, DEFAULT_TRX_IP); | ||||
| 	trx->cfg.base_port = DEFAULT_TRX_PORT; | ||||
| 	trx->cfg.tx_sps = DEFAULT_TX_SPS; | ||||
| 	trx->cfg.rx_sps = DEFAULT_RX_SPS; | ||||
| 	trx->cfg.filler = FILLER_ZERO; | ||||
|  | ||||
| 	return trx; | ||||
| } | ||||
|  | ||||
| int trx_vty_init(struct trx_ctx* trx) | ||||
| { | ||||
| 	g_trx_ctx = trx; | ||||
| 	install_element_ve(&show_trx_cmd); | ||||
|  | ||||
| 	install_element(CONFIG_NODE, &cfg_trx_cmd); | ||||
|  | ||||
| 	install_node(&trx_node, config_write_trx); | ||||
| 	install_element(TRX_NODE, &cfg_bind_ip_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_remote_ip_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_base_port_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_dev_args_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_tx_sps_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_rx_sps_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_test_rtsc_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_test_rach_delay_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_clock_ref_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_multi_arfcn_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_offset_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_rssi_offset_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_swap_channels_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_egprs_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_rt_prio_cmd); | ||||
| 	install_element(TRX_NODE, &cfg_filler_cmd); | ||||
|  | ||||
| 	install_element(TRX_NODE, &cfg_chan_cmd); | ||||
| 	install_node(&chan_node, dummy_config_write); | ||||
| 	install_element(CHAN_NODE, &cfg_chan_rx_path_cmd); | ||||
| 	install_element(CHAN_NODE, &cfg_chan_tx_path_cmd); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
							
								
								
									
										68
									
								
								CommonLibs/trx_vty.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								CommonLibs/trx_vty.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,68 @@ | ||||
| #pragma once | ||||
|  | ||||
| #include <osmocom/vty/command.h> | ||||
|  | ||||
| #include "config_defs.h" | ||||
|  | ||||
| extern struct vty_app_info g_vty_info; | ||||
|  | ||||
| #define TRX_CHAN_MAX 8 | ||||
|  | ||||
| /* Samples-per-symbol for downlink path | ||||
|  *     4 - Uses precision modulator (more computation, less distortion) | ||||
|  *     1 - Uses minimized modulator (less computation, more distortion) | ||||
|  * | ||||
|  *     Other values are invalid. Receive path (uplink) is always | ||||
|  *     downsampled to 1 sps. Default to 4 sps for all cases. | ||||
|  */ | ||||
| #define DEFAULT_TX_SPS		4 | ||||
|  | ||||
| /* | ||||
|  * Samples-per-symbol for uplink (receiver) path | ||||
|  *     Do not modify this value. EDGE configures 4 sps automatically on | ||||
|  *     B200/B210 devices only. Use of 4 sps on the receive path for other | ||||
|  *     configurations is not supported. | ||||
|  */ | ||||
| #define DEFAULT_RX_SPS		1 | ||||
|  | ||||
| /* Default configuration parameters */ | ||||
| #define DEFAULT_TRX_PORT	5700 | ||||
| #define DEFAULT_TRX_IP		"127.0.0.1" | ||||
| #define DEFAULT_CHANS		1 | ||||
|  | ||||
| struct trx_ctx; | ||||
|  | ||||
| struct trx_chan { | ||||
| 	struct trx_ctx *trx; /* backpointer */ | ||||
| 	unsigned int idx; /* channel index */ | ||||
| 	char *rx_path; | ||||
| 	char *tx_path; | ||||
| }; | ||||
|  | ||||
| struct trx_ctx { | ||||
| 	struct { | ||||
| 		char *bind_addr; | ||||
| 		char *remote_addr; | ||||
| 		char *dev_args; | ||||
| 		unsigned int base_port; | ||||
| 		unsigned int tx_sps; | ||||
| 		unsigned int rx_sps; | ||||
| 		unsigned int rtsc; | ||||
| 		bool rtsc_set; | ||||
| 		unsigned int rach_delay; | ||||
| 		bool rach_delay_set; | ||||
| 		enum ReferenceType clock_ref; | ||||
| 		enum FillerType filler; | ||||
| 		bool multi_arfcn; | ||||
| 		double offset; | ||||
| 		double rssi_offset; | ||||
| 		bool swap_channels; | ||||
| 		bool egprs; | ||||
| 		unsigned int sched_rr; | ||||
| 		unsigned int num_chans; | ||||
| 		struct trx_chan chans[TRX_CHAN_MAX]; | ||||
| 	} cfg; | ||||
| }; | ||||
|  | ||||
| int trx_vty_init(struct trx_ctx* trx); | ||||
| struct trx_ctx *vty_trx_ctx_alloc(void *talloc_ctx); | ||||
| @@ -54,7 +54,10 @@ const BitVector GSM::gEdgeTrainingSequence[] = { | ||||
|  | ||||
| const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000"); | ||||
|  | ||||
| const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000"); | ||||
| /* 3GPP TS 05.02, section 5.2.7 "Access burst (AB)", synch. sequence bits */ | ||||
| const BitVector GSM::gRACHSynchSequenceTS0("01001011011111111001100110101010001111000");  /* GSM, GMSK (default) */ | ||||
| const BitVector GSM::gRACHSynchSequenceTS1("01010100111110001000011000101111001001101");  /* EGPRS, 8-PSK */ | ||||
| const BitVector GSM::gRACHSynchSequenceTS2("11101111001001110101011000001101101110111");  /* EGPRS, GMSK */ | ||||
|  | ||||
| //                               |-head-||---------midamble----------------------||--------------data----------------||t| | ||||
| const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000"); | ||||
|   | ||||
| @@ -52,7 +52,9 @@ extern const BitVector gEdgeTrainingSequence[]; | ||||
| extern const BitVector gDummyBurst; | ||||
|  | ||||
| /** Random access burst synch. sequence */ | ||||
| extern const BitVector gRACHSynchSequence; | ||||
| extern const BitVector gRACHSynchSequenceTS0; | ||||
| extern const BitVector gRACHSynchSequenceTS1; | ||||
| extern const BitVector gRACHSynchSequenceTS2; | ||||
| /** Random access burst synch. sequence, GSM 05.02 5.2.7 */ | ||||
| extern const BitVector gRACHBurst; | ||||
|  | ||||
|   | ||||
| @@ -28,9 +28,11 @@ AM_CXXFLAGS = -Wall -pthread | ||||
|  | ||||
| # Order must be preserved | ||||
| SUBDIRS = \ | ||||
| 	doc \ | ||||
| 	CommonLibs \ | ||||
| 	GSM \ | ||||
| 	Transceiver52M \ | ||||
| 	contrib \ | ||||
| 	tests | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| @@ -40,6 +42,9 @@ EXTRA_DIST = \ | ||||
| 	COPYING \ | ||||
| 	README | ||||
|  | ||||
| AM_DISTCHECK_CONFIGURE_FLAGS = \ | ||||
| 	--with-systemdsystemunitdir=$$dc_install_base/$(systemdsystemunitdir) | ||||
|  | ||||
| .PHONY: release | ||||
|  | ||||
| @RELMAKE@ | ||||
|   | ||||
| @@ -18,9 +18,6 @@ | ||||
| # along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| # | ||||
|  | ||||
| top_srcdir = $(abs_top_srcdir) | ||||
| top_builddir = $(abs_top_builddir) | ||||
|  | ||||
| COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs | ||||
| GSM_INCLUDEDIR = $(top_srcdir)/GSM | ||||
|  | ||||
| @@ -33,9 +30,9 @@ COMMON_LA = $(top_builddir)/CommonLibs/libcommon.la | ||||
| GSM_LA = $(top_builddir)/GSM/libGSM.la | ||||
|  | ||||
| if ARCH_ARM | ||||
| ARCH_LA = $(top_builddir)/Transceiver52M/arm/libarch.la | ||||
| ARCH_LA = $(top_builddir)/Transceiver52M/arch/arm/libarch.la | ||||
| else | ||||
| ARCH_LA = $(top_builddir)/Transceiver52M/x86/libarch.la | ||||
| ARCH_LA = $(top_builddir)/Transceiver52M/arch/x86/libarch.la | ||||
| endif | ||||
|  | ||||
| MOSTLYCLEANFILES = *~ | ||||
|   | ||||
| @@ -28,8 +28,8 @@ | ||||
| #include "Channelizer.h" | ||||
|  | ||||
| extern "C" { | ||||
| #include "common/fft.h" | ||||
| #include "common/convolve.h" | ||||
| #include "fft.h" | ||||
| #include "convolve.h" | ||||
| } | ||||
|  | ||||
| static void deinterleave(const float *in, size_t ilen, | ||||
|   | ||||
| @@ -29,7 +29,7 @@ | ||||
| #include "ChannelizerBase.h" | ||||
|  | ||||
| extern "C" { | ||||
| #include "common/fft.h" | ||||
| #include "fft.h" | ||||
| } | ||||
|  | ||||
| static float sinc(float x) | ||||
| @@ -239,7 +239,7 @@ ChannelizerBase::~ChannelizerBase() | ||||
|  | ||||
| 	for (size_t i = 0; i < m; i++) { | ||||
| 		free(subFilters[i]); | ||||
| 		delete hist[i]; | ||||
| 		delete[] hist[i]; | ||||
| 	} | ||||
|  | ||||
| 	fft_free(fftInput); | ||||
|   | ||||
| @@ -21,16 +21,10 @@ | ||||
|  | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/common | ||||
| AM_CXXFLAGS = -lpthread | ||||
| SUBDIRS = arch device | ||||
|  | ||||
| SUBDIRS = arm x86 | ||||
|  | ||||
| if USRP1 | ||||
| AM_CPPFLAGS += $(USRP_CFLAGS) | ||||
| else | ||||
| AM_CPPFLAGS += $(UHD_CFLAGS) | ||||
| endif | ||||
| AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/arch/common -I${srcdir}/device | ||||
| AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) | ||||
|  | ||||
| rev2dir = $(datadir)/usrp/rev2 | ||||
| rev4dir = $(datadir)/usrp/rev4 | ||||
| @@ -38,11 +32,9 @@ rev4dir = $(datadir)/usrp/rev4 | ||||
| dist_rev2_DATA = std_inband.rbf | ||||
| dist_rev4_DATA = std_inband.rbf | ||||
|  | ||||
| EXTRA_DIST = \ | ||||
| 	README \ | ||||
| 	README.Talgorithm | ||||
| EXTRA_DIST = README | ||||
|  | ||||
| noinst_LTLIBRARIES = libtransceiver.la | ||||
| noinst_LTLIBRARIES = libtransceiver_common.la | ||||
|  | ||||
| COMMON_SOURCES = \ | ||||
| 	radioInterface.cpp \ | ||||
| @@ -54,50 +46,66 @@ COMMON_SOURCES = \ | ||||
| 	Transceiver.cpp \ | ||||
| 	ChannelizerBase.cpp \ | ||||
| 	Channelizer.cpp \ | ||||
| 	Synthesis.cpp \ | ||||
| 	common/fft.c | ||||
| 	Synthesis.cpp | ||||
|  | ||||
| libtransceiver_la_SOURCES = \ | ||||
| libtransceiver_common_la_SOURCES = \ | ||||
| 	$(COMMON_SOURCES) \ | ||||
| 	Resampler.cpp \ | ||||
| 	radioInterfaceResamp.cpp \ | ||||
| 	radioInterfaceMulti.cpp | ||||
|  | ||||
| bin_PROGRAMS = osmo-trx | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
| 	Complex.h \ | ||||
| 	radioInterface.h \ | ||||
| 	radioVector.h \ | ||||
| 	radioClock.h \ | ||||
| 	radioDevice.h \ | ||||
| 	radioBuffer.h \ | ||||
| 	sigProcLib.h \ | ||||
| 	signalVector.h \ | ||||
| 	Transceiver.h \ | ||||
| 	USRPDevice.h \ | ||||
| 	Resampler.h \ | ||||
| 	ChannelizerBase.h \ | ||||
| 	Channelizer.h \ | ||||
| 	Synthesis.h \ | ||||
| 	common/convolve.h \ | ||||
| 	common/convert.h \ | ||||
| 	common/scale.h \ | ||||
| 	common/mult.h \ | ||||
| 	common/fft.h | ||||
| 	Synthesis.h | ||||
|  | ||||
| osmo_trx_SOURCES = osmo-trx.cpp | ||||
| osmo_trx_LDADD = \ | ||||
| 	libtransceiver.la \ | ||||
| COMMON_LDADD = \ | ||||
| 	libtransceiver_common.la \ | ||||
| 	$(ARCH_LA) \ | ||||
| 	$(GSM_LA) \ | ||||
| 	$(COMMON_LA) \ | ||||
| 	$(FFTWF_LIBS) | ||||
| 	$(FFTWF_LIBS) \ | ||||
| 	$(LIBOSMOCORE_LIBS) \ | ||||
| 	$(LIBOSMOCTRL_LIBS) \ | ||||
| 	$(LIBOSMOVTY_LIBS) | ||||
|  | ||||
| if USRP1 | ||||
| libtransceiver_la_SOURCES += USRPDevice.cpp | ||||
| osmo_trx_LDADD += $(USRP_LIBS) | ||||
| else | ||||
| libtransceiver_la_SOURCES += UHDDevice.cpp | ||||
| osmo_trx_LDADD += $(UHD_LIBS) | ||||
| bin_PROGRAMS = | ||||
|  | ||||
| if DEVICE_UHD | ||||
| bin_PROGRAMS += osmo-trx-uhd | ||||
| osmo_trx_uhd_SOURCES = osmo-trx.cpp | ||||
| osmo_trx_uhd_LDADD = \ | ||||
| 	$(builddir)/device/uhd/libdevice.la \ | ||||
| 	$(COMMON_LDADD) \ | ||||
| 	$(UHD_LIBS) | ||||
| osmo_trx_uhd_CPPFLAGS  = $(AM_CPPFLAGS) $(UHD_CFLAGS) | ||||
| endif | ||||
|  | ||||
| if DEVICE_USRP1 | ||||
| bin_PROGRAMS += osmo-trx-usrp1 | ||||
| osmo_trx_usrp1_SOURCES = osmo-trx.cpp | ||||
| osmo_trx_usrp1_LDADD = \ | ||||
| 	$(builddir)/device/usrp1/libdevice.la \ | ||||
| 	$(COMMON_LDADD) \ | ||||
| 	$(USRP_LIBS) | ||||
| osmo_trx_usrp1_CPPFLAGS  = $(AM_CPPFLAGS) $(USRP_CFLAGS) | ||||
| endif | ||||
|  | ||||
| if DEVICE_LMS | ||||
| bin_PROGRAMS += osmo-trx-lms | ||||
| osmo_trx_lms_SOURCES = osmo-trx.cpp | ||||
| osmo_trx_lms_LDADD = \ | ||||
| 	$(builddir)/device/lms/libdevice.la \ | ||||
| 	$(COMMON_LDADD) \ | ||||
| 	$(LMS_LIBS) | ||||
| osmo_trx_lms_CPPFLAGS  = $(AM_CPPFLAGS) $(LMS_CFLAGS) | ||||
| endif | ||||
|   | ||||
| @@ -29,8 +29,8 @@ | ||||
| #include "Synthesis.h" | ||||
|  | ||||
| extern "C" { | ||||
| #include "common/fft.h" | ||||
| #include "common/convolve.h" | ||||
| #include "fft.h" | ||||
| #include "convolve.h" | ||||
| } | ||||
|  | ||||
| static void interleave(float **in, size_t ilen, | ||||
|   | ||||
| @@ -27,6 +27,10 @@ | ||||
| #include "Transceiver.h" | ||||
| #include <Logger.h> | ||||
|  | ||||
| extern "C" { | ||||
| #include "osmo_signal.h" | ||||
| } | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| @@ -35,12 +39,6 @@ using namespace GSM; | ||||
|  | ||||
| #define USB_LATENCY_INTRVL		10,0 | ||||
|  | ||||
| #if USE_UHD | ||||
| #  define USB_LATENCY_MIN		6,7 | ||||
| #else | ||||
| #  define USB_LATENCY_MIN		1,1 | ||||
| #endif | ||||
|  | ||||
| /* Number of running values use in noise average */ | ||||
| #define NOISE_CNT			20 | ||||
|  | ||||
| @@ -71,7 +69,7 @@ TransceiverState::~TransceiverState() | ||||
|   } | ||||
| } | ||||
|  | ||||
| bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay) | ||||
| bool TransceiverState::init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay) | ||||
| { | ||||
|   signalVector *burst; | ||||
|  | ||||
| @@ -81,19 +79,19 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, un | ||||
|   for (size_t n = 0; n < 8; n++) { | ||||
|     for (size_t i = 0; i < 102; i++) { | ||||
|       switch (filler) { | ||||
|       case Transceiver::FILLER_DUMMY: | ||||
|       case FILLER_DUMMY: | ||||
|         burst = generateDummyBurst(sps, n); | ||||
|         break; | ||||
|       case Transceiver::FILLER_NORM_RAND: | ||||
|       case FILLER_NORM_RAND: | ||||
|         burst = genRandNormalBurst(rtsc, sps, n); | ||||
|         break; | ||||
|       case Transceiver::FILLER_EDGE_RAND: | ||||
|       case FILLER_EDGE_RAND: | ||||
|         burst = generateEdgeBurst(rtsc); | ||||
|         break; | ||||
|       case Transceiver::FILLER_ACCESS_RAND: | ||||
|       case FILLER_ACCESS_RAND: | ||||
|         burst = genRandAccessBurst(rach_delay, sps, n); | ||||
|         break; | ||||
|       case Transceiver::FILLER_ZERO: | ||||
|       case FILLER_ZERO: | ||||
|       default: | ||||
|         burst = generateEmptyBurst(sps, n); | ||||
|       } | ||||
| @@ -102,8 +100,8 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, un | ||||
|       fillerTable[i][n] = burst; | ||||
|     } | ||||
|  | ||||
|     if ((filler == Transceiver::FILLER_NORM_RAND) || | ||||
|         (filler == Transceiver::FILLER_EDGE_RAND)) { | ||||
|     if ((filler == FILLER_NORM_RAND) || | ||||
|         (filler == FILLER_EDGE_RAND)) { | ||||
|         chanType[n] = TSC; | ||||
|     } | ||||
|   } | ||||
| @@ -121,7 +119,7 @@ Transceiver::Transceiver(int wBasePort, | ||||
|   : mBasePort(wBasePort), mLocalAddr(TRXAddress), mRemoteAddr(GSMcoreAddress), | ||||
|     mClockSocket(TRXAddress, wBasePort, GSMcoreAddress, wBasePort + 100), | ||||
|     mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface), | ||||
|     rssiOffset(wRssiOffset), | ||||
|     rssiOffset(wRssiOffset), sig_cbfn(NULL), | ||||
|     mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mEdge(false), mOn(false), mForceClockInterface(false), | ||||
|     mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(0), mMaxExpectedDelayNB(0), | ||||
|     mWriteBurstToDiskMask(0) | ||||
| @@ -161,7 +159,7 @@ Transceiver::~Transceiver() | ||||
|  * are still expected to report clock indications through control channel | ||||
|  * activity. | ||||
|  */ | ||||
| bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge) | ||||
| bool Transceiver::init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge) | ||||
| { | ||||
|   int d_srcport, d_dstport, c_srcport, c_dstport; | ||||
|  | ||||
| @@ -225,6 +223,17 @@ bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay, bool edge) | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void Transceiver::setSignalHandler(osmo_signal_cbfn cbfn) | ||||
| { | ||||
|   if (this->sig_cbfn) | ||||
|     osmo_signal_unregister_handler(SS_TRANSC, this->sig_cbfn, NULL); | ||||
|  | ||||
|   if (cbfn) { | ||||
|     this->sig_cbfn = cbfn; | ||||
|     osmo_signal_register_handler(SS_TRANSC, this->sig_cbfn, NULL); | ||||
|   } | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Start the transceiver | ||||
|  * | ||||
| @@ -379,7 +388,8 @@ void Transceiver::pushRadioVector(GSM::Time &nowTime) | ||||
|     state = &mStates[i]; | ||||
|  | ||||
|     while ((burst = mTxPriorityQueues[i].getStaleBurst(nowTime))) { | ||||
|       LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface"; | ||||
|       LOG(NOTICE) << "chan " << i << " dumping STALE burst in TRX->SDR interface (" | ||||
|                   << burst->getTime() <<" vs " << nowTime << "), retrans=" << state->mRetrans; | ||||
|       if (state->mRetrans) | ||||
|         updateFillerTable(i, burst); | ||||
|       delete burst; | ||||
| @@ -657,38 +667,66 @@ void Transceiver::reset() | ||||
| } | ||||
|  | ||||
|  | ||||
| #define MAX_PACKET_LENGTH 100 | ||||
|  | ||||
| /** | ||||
|  * Matches a buffer with a command. | ||||
|  * @param  buf    a buffer to look command in | ||||
|  * @param  cmd    a command to look in buffer | ||||
|  * @param  params pointer to arguments, or NULL | ||||
|  * @return        true if command matches, otherwise false | ||||
|  */ | ||||
| static bool match_cmd(char *buf, | ||||
|   const char *cmd, char **params) | ||||
| { | ||||
|   size_t cmd_len = strlen(cmd); | ||||
|  | ||||
|   /* Check a command itself */ | ||||
|   if (strncmp(buf, cmd, cmd_len)) | ||||
|     return false; | ||||
|  | ||||
|   /* A command has arguments */ | ||||
|   if (params != NULL) { | ||||
|     /* Make sure there is a space */ | ||||
|     if (buf[cmd_len] != ' ') | ||||
|       return false; | ||||
|  | ||||
|     /* Update external pointer */ | ||||
|     *params = buf + cmd_len + 1; | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| void Transceiver::driveControl(size_t chan) | ||||
| { | ||||
|   int MAX_PACKET_LENGTH = 100; | ||||
|   char buffer[MAX_PACKET_LENGTH + 1]; | ||||
|   char response[MAX_PACKET_LENGTH + 1]; | ||||
|   char *command, *params; | ||||
|   int msgLen; | ||||
|  | ||||
|   // check control socket | ||||
|   char buffer[MAX_PACKET_LENGTH]; | ||||
|   int msgLen = -1; | ||||
|   buffer[0] = '\0'; | ||||
|   /* Attempt to read from control socket */ | ||||
|   msgLen = mCtrlSockets[chan]->read(buffer, MAX_PACKET_LENGTH); | ||||
|   if (msgLen < 1) | ||||
|     return; | ||||
|  | ||||
|   msgLen = mCtrlSockets[chan]->read(buffer, sizeof(buffer)); | ||||
|   /* Zero-terminate received string */ | ||||
|   buffer[msgLen] = '\0'; | ||||
|  | ||||
|   if (msgLen < 1) { | ||||
|   /* Verify a command signature */ | ||||
|   if (strncmp(buffer, "CMD ", 4)) { | ||||
|     LOGC(DTRXCTRL, WARNING) << "bogus message on control interface"; | ||||
|     return; | ||||
|   } | ||||
|  | ||||
|   char cmdcheck[4]; | ||||
|   char command[MAX_PACKET_LENGTH]; | ||||
|   char response[MAX_PACKET_LENGTH]; | ||||
|   /* Set command pointer */ | ||||
|   command = buffer + 4; | ||||
|   LOGC(DTRXCTRL, INFO) << "chan " << chan << ": command is '" << command << "'"; | ||||
|  | ||||
|   sscanf(buffer,"%3s %s",cmdcheck,command); | ||||
|  | ||||
|   if (strcmp(cmdcheck,"CMD")!=0) { | ||||
|     LOG(WARNING) << "bogus message on control interface"; | ||||
|     return; | ||||
|   } | ||||
|   LOG(INFO) << "command is " << buffer; | ||||
|  | ||||
|   if (strcmp(command,"POWEROFF")==0) { | ||||
|   if (match_cmd(command, "POWEROFF", NULL)) { | ||||
|     stop(); | ||||
|     sprintf(response,"RSP POWEROFF 0"); | ||||
|   } | ||||
|   else if (strcmp(command,"POWERON")==0) { | ||||
|   } else if (match_cmd(command, "POWERON", NULL)) { | ||||
|     if (!start()) { | ||||
|       sprintf(response,"RSP POWERON 1"); | ||||
|     } else { | ||||
| @@ -698,41 +736,43 @@ void Transceiver::driveControl(size_t chan) | ||||
|           mHandover[i][j] = false; | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|   else if (strcmp(command,"HANDOVER")==0){ | ||||
|     int ts=0,ss=0; | ||||
|     sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss); | ||||
|     mHandover[ts][ss] = true; | ||||
|     sprintf(response,"RSP HANDOVER 0 %d %d",ts,ss); | ||||
|   } | ||||
|   else if (strcmp(command,"NOHANDOVER")==0){ | ||||
|     int ts=0,ss=0; | ||||
|     sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss); | ||||
|     mHandover[ts][ss] = false; | ||||
|     sprintf(response,"RSP NOHANDOVER 0 %d %d",ts,ss); | ||||
|   } | ||||
|   else if (strcmp(command,"SETMAXDLY")==0) { | ||||
|   } else if (match_cmd(command, "HANDOVER", ¶ms)) { | ||||
|     unsigned ts = 0, ss = 0; | ||||
|     sscanf(params, "%u %u", &ts, &ss); | ||||
|     if (ts > 7 || ss > 7) { | ||||
|       sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss); | ||||
|     } else { | ||||
|       mHandover[ts][ss] = true; | ||||
|       sprintf(response, "RSP HANDOVER 0 %u %u", ts, ss); | ||||
|     } | ||||
|   } else if (match_cmd(command, "NOHANDOVER", ¶ms)) { | ||||
|     unsigned ts = 0, ss = 0; | ||||
|     sscanf(params, "%u %u", &ts, &ss); | ||||
|     if (ts > 7 || ss > 7) { | ||||
|       sprintf(response, "RSP NOHANDOVER 1 %u %u", ts, ss); | ||||
|     } else { | ||||
|       mHandover[ts][ss] = false; | ||||
|       sprintf(response, "RSP NOHANDOVER 0 %u %u", ts, ss); | ||||
|     } | ||||
|   } else if (match_cmd(command, "SETMAXDLY", ¶ms)) { | ||||
|     //set expected maximum time-of-arrival | ||||
|     int maxDelay; | ||||
|     sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay); | ||||
|     sscanf(params, "%d", &maxDelay); | ||||
|     mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km | ||||
|     sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay); | ||||
|   } | ||||
|   else if (strcmp(command,"SETMAXDLYNB")==0) { | ||||
|   } else if (match_cmd(command, "SETMAXDLYNB", ¶ms)) { | ||||
|     //set expected maximum time-of-arrival | ||||
|     int maxDelay; | ||||
|     sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay); | ||||
|     sscanf(params, "%d", &maxDelay); | ||||
|     mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km | ||||
|     sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay); | ||||
|   } | ||||
|   else if (strcmp(command,"SETRXGAIN")==0) { | ||||
|   } else if (match_cmd(command, "SETRXGAIN", ¶ms)) { | ||||
|     //set expected maximum time-of-arrival | ||||
|     int newGain; | ||||
|     sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain); | ||||
|     sscanf(params, "%d", &newGain); | ||||
|     newGain = mRadioInterface->setRxGain(newGain, chan); | ||||
|     sprintf(response,"RSP SETRXGAIN 0 %d",newGain); | ||||
|   } | ||||
|   else if (strcmp(command,"NOISELEV")==0) { | ||||
|   } else if (match_cmd(command, "NOISELEV", NULL)) { | ||||
|     if (mOn) { | ||||
|       float lev = mStates[chan].mNoiseLev; | ||||
|       sprintf(response,"RSP NOISELEV 0 %d", | ||||
| @@ -741,86 +781,78 @@ void Transceiver::driveControl(size_t chan) | ||||
|     else { | ||||
|       sprintf(response,"RSP NOISELEV 1  0"); | ||||
|     } | ||||
|   } | ||||
|   else if (!strcmp(command, "SETPOWER")) { | ||||
|   } else if (match_cmd(command, "SETPOWER", ¶ms)) { | ||||
|     int power; | ||||
|     sscanf(buffer, "%3s %s %d", cmdcheck, command, &power); | ||||
|     sscanf(params, "%d", &power); | ||||
|     power = mRadioInterface->setPowerAttenuation(power, chan); | ||||
|     mStates[chan].mPower = power; | ||||
|     sprintf(response, "RSP SETPOWER 0 %d", power); | ||||
|   } | ||||
|   else if (!strcmp(command,"ADJPOWER")) { | ||||
|   } else if (match_cmd(command, "ADJPOWER", ¶ms)) { | ||||
|     int power, step; | ||||
|     sscanf(buffer, "%3s %s %d", cmdcheck, command, &step); | ||||
|     sscanf(params, "%d", &step); | ||||
|     power = mStates[chan].mPower + step; | ||||
|     power = mRadioInterface->setPowerAttenuation(power, chan); | ||||
|     mStates[chan].mPower = power; | ||||
|     sprintf(response, "RSP ADJPOWER 0 %d", power); | ||||
|   } | ||||
|   else if (strcmp(command,"RXTUNE")==0) { | ||||
|   } else if (match_cmd(command, "RXTUNE", ¶ms)) { | ||||
|     // tune receiver | ||||
|     int freqKhz; | ||||
|     sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz); | ||||
|     sscanf(params, "%d", &freqKhz); | ||||
|     mRxFreq = freqKhz * 1e3; | ||||
|     if (!mRadioInterface->tuneRx(mRxFreq, chan)) { | ||||
|        LOG(ALERT) << "RX failed to tune"; | ||||
|        LOGC(DTRXCTRL, ALERT) << "RX failed to tune"; | ||||
|        sprintf(response,"RSP RXTUNE 1 %d",freqKhz); | ||||
|     } | ||||
|     else | ||||
|        sprintf(response,"RSP RXTUNE 0 %d",freqKhz); | ||||
|   } | ||||
|   else if (strcmp(command,"TXTUNE")==0) { | ||||
|   } else if (match_cmd(command, "TXTUNE", ¶ms)) { | ||||
|     // tune txmtr | ||||
|     int freqKhz; | ||||
|     sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz); | ||||
|     sscanf(params, "%d", &freqKhz); | ||||
|     mTxFreq = freqKhz * 1e3; | ||||
|     if (!mRadioInterface->tuneTx(mTxFreq, chan)) { | ||||
|        LOG(ALERT) << "TX failed to tune"; | ||||
|        LOGC(DTRXCTRL, ALERT) << "TX failed to tune"; | ||||
|        sprintf(response,"RSP TXTUNE 1 %d",freqKhz); | ||||
|     } | ||||
|     else | ||||
|        sprintf(response,"RSP TXTUNE 0 %d",freqKhz); | ||||
|   } | ||||
|   else if (!strcmp(command,"SETTSC")) { | ||||
|   } else if (match_cmd(command, "SETTSC", ¶ms)) { | ||||
|     // set TSC | ||||
|     unsigned TSC; | ||||
|     sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC); | ||||
|     sscanf(params, "%u", &TSC); | ||||
|     if (TSC > 7) { | ||||
|       sprintf(response, "RSP SETTSC 1 %d", TSC); | ||||
|     } else { | ||||
|       LOG(NOTICE) << "Changing TSC from " << mTSC << " to " << TSC; | ||||
|       LOGC(DTRXCTRL, NOTICE) << "Changing TSC from " << mTSC << " to " << TSC; | ||||
|       mTSC = TSC; | ||||
|       sprintf(response,"RSP SETTSC 0 %d", TSC); | ||||
|     } | ||||
|   } | ||||
|   else if (strcmp(command,"SETSLOT")==0) { | ||||
|   } else if (match_cmd(command, "SETSLOT", ¶ms)) { | ||||
|     // set slot type | ||||
|     int  corrCode; | ||||
|     int  timeslot; | ||||
|     sscanf(buffer,"%3s %s %d %d",cmdcheck,command,×lot,&corrCode); | ||||
|     sscanf(params, "%d %d", ×lot, &corrCode); | ||||
|     if ((timeslot < 0) || (timeslot > 7)) { | ||||
|       LOG(WARNING) << "bogus message on control interface"; | ||||
|       LOGC(DTRXCTRL, WARNING) << "bogus message on control interface"; | ||||
|       sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode); | ||||
|       return; | ||||
|     } | ||||
|     mStates[chan].chanType[timeslot] = (ChannelCombination) corrCode; | ||||
|     setModulus(timeslot, chan); | ||||
|     sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode); | ||||
|  | ||||
|   } | ||||
|   else if (strcmp(command,"_SETBURSTTODISKMASK")==0) { | ||||
|   } else if (match_cmd(command, "_SETBURSTTODISKMASK", ¶ms)) { | ||||
|     // debug command! may change or disapear without notice | ||||
|     // set a mask which bursts to dump to disk | ||||
|     int mask; | ||||
|     sscanf(buffer,"%3s %s %d",cmdcheck,command,&mask); | ||||
|     sscanf(params, "%d", &mask); | ||||
|     mWriteBurstToDiskMask = mask; | ||||
|     sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask); | ||||
|   } | ||||
|   else { | ||||
|     LOG(WARNING) << "bogus command " << command << " on control interface."; | ||||
|   } else { | ||||
|     LOGC(DTRXCTRL, WARNING) << "bogus command " << command << " on control interface."; | ||||
|     sprintf(response,"RSP ERR 1"); | ||||
|   } | ||||
|  | ||||
|   LOGC(DTRXCTRL, INFO) << "chan " << chan << ": response is '" << response << "'"; | ||||
|   mCtrlSockets[chan]->write(response, strlen(response) + 1); | ||||
| } | ||||
|  | ||||
| @@ -869,8 +901,12 @@ bool Transceiver::driveTxPriorityQueue(size_t chan) | ||||
|  | ||||
| void Transceiver::driveReceiveRadio() | ||||
| { | ||||
|   if (!mRadioInterface->driveReceiveRadio()) { | ||||
|   int rc = mRadioInterface->driveReceiveRadio(); | ||||
|   if (rc == 0) { | ||||
|     usleep(100000); | ||||
|   } else if (rc < 0) { | ||||
|     LOG(FATAL) << "radio Interface receive failed, requesting stop."; | ||||
|     osmo_signal_dispatch(SS_TRANSC, S_TRANSC_STOP_REQUIRED, this); | ||||
|   } else if (mForceClockInterface || mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0)) { | ||||
|     mForceClockInterface = false; | ||||
|     writeClockInterface(); | ||||
| @@ -966,14 +1002,15 @@ void Transceiver::driveTxFIFO() | ||||
|           // only update latency at the defined frame interval | ||||
|           if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) { | ||||
|             mTransmitLatency = mTransmitLatency + GSM::Time(1,0); | ||||
|             LOG(INFO) << "new latency: " << mTransmitLatency; | ||||
|             LOG(INFO) << "new latency: " << mTransmitLatency << " (underrun " | ||||
|                       << radioClock->get() << " vs " << mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL) << ")"; | ||||
|             mLatencyUpdateTime = radioClock->get(); | ||||
|           } | ||||
|         } | ||||
|         else { | ||||
|           // if underrun hasn't occurred in the last sec (216 frames) drop | ||||
|           //    transmit latency by a timeslot | ||||
|           if (mTransmitLatency > GSM::Time(USB_LATENCY_MIN)) { | ||||
|           if (mTransmitLatency > mRadioInterface->minLatency()) { | ||||
|               if (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) { | ||||
|               mTransmitLatency.decTN(); | ||||
|               LOG(INFO) << "reduced latency: " << mTransmitLatency; | ||||
| @@ -1009,11 +1046,15 @@ void Transceiver::writeClockInterface() | ||||
|  | ||||
| void *RxUpperLoopAdapter(TransceiverChannel *chan) | ||||
| { | ||||
|   char thread_name[16]; | ||||
|   Transceiver *trx = chan->trx; | ||||
|   size_t num = chan->num; | ||||
|  | ||||
|   delete chan; | ||||
|  | ||||
|   snprintf(thread_name, 16, "RxUpper%zu", num); | ||||
|   set_selfthread_name(thread_name); | ||||
|  | ||||
|   trx->setPriority(0.42); | ||||
|  | ||||
|   while (1) { | ||||
| @@ -1025,6 +1066,8 @@ void *RxUpperLoopAdapter(TransceiverChannel *chan) | ||||
|  | ||||
| void *RxLowerLoopAdapter(Transceiver *transceiver) | ||||
| { | ||||
|   set_selfthread_name("RxLower"); | ||||
|  | ||||
|   transceiver->setPriority(0.45); | ||||
|  | ||||
|   while (1) { | ||||
| @@ -1036,6 +1079,8 @@ void *RxLowerLoopAdapter(Transceiver *transceiver) | ||||
|  | ||||
| void *TxLowerLoopAdapter(Transceiver *transceiver) | ||||
| { | ||||
|   set_selfthread_name("TxLower"); | ||||
|  | ||||
|   transceiver->setPriority(0.44); | ||||
|  | ||||
|   while (1) { | ||||
| @@ -1047,11 +1092,15 @@ void *TxLowerLoopAdapter(Transceiver *transceiver) | ||||
|  | ||||
| void *ControlServiceLoopAdapter(TransceiverChannel *chan) | ||||
| { | ||||
|   char thread_name[16]; | ||||
|   Transceiver *trx = chan->trx; | ||||
|   size_t num = chan->num; | ||||
|  | ||||
|   delete chan; | ||||
|  | ||||
|   snprintf(thread_name, 16, "CtrlService%zu", num); | ||||
|   set_selfthread_name(thread_name); | ||||
|  | ||||
|   while (1) { | ||||
|     trx->driveControl(num); | ||||
|     pthread_testcancel(); | ||||
| @@ -1061,11 +1110,15 @@ void *ControlServiceLoopAdapter(TransceiverChannel *chan) | ||||
|  | ||||
| void *TxUpperLoopAdapter(TransceiverChannel *chan) | ||||
| { | ||||
|   char thread_name[16]; | ||||
|   Transceiver *trx = chan->trx; | ||||
|   size_t num = chan->num; | ||||
|  | ||||
|   delete chan; | ||||
|  | ||||
|   snprintf(thread_name, 16, "TxUpper%zu", num); | ||||
|   set_selfthread_name(thread_name); | ||||
|  | ||||
|   trx->setPriority(0.40); | ||||
|  | ||||
|   while (1) { | ||||
|   | ||||
| @@ -30,6 +30,11 @@ | ||||
| #include <sys/types.h> | ||||
| #include <sys/socket.h> | ||||
|  | ||||
| extern "C" { | ||||
| #include <osmocom/core/signal.h> | ||||
| #include "config_defs.h" | ||||
| } | ||||
|  | ||||
| class Transceiver; | ||||
|  | ||||
| /** Channel descriptor for transceiver object and channel number pair */ | ||||
| @@ -54,7 +59,7 @@ struct TransceiverState { | ||||
|   ~TransceiverState(); | ||||
|  | ||||
|   /* Initialize a multiframe slot in the filler table */ | ||||
|   bool init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay); | ||||
|   bool init(FillerType filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay); | ||||
|  | ||||
|   int chanType[8]; | ||||
|  | ||||
| @@ -109,7 +114,7 @@ public: | ||||
|   ~Transceiver(); | ||||
|  | ||||
|   /** Start the control loop */ | ||||
|   bool init(int filler, size_t rtsc, unsigned rach_delay, bool edge); | ||||
|   bool init(FillerType filler, size_t rtsc, unsigned rach_delay, bool edge); | ||||
|  | ||||
|   /** attach the radioInterface receive FIFO */ | ||||
|   bool receiveFIFO(VectorFIFO *wFIFO, size_t chan) | ||||
| @@ -124,6 +129,8 @@ public: | ||||
|   /** accessor for number of channels */ | ||||
|   size_t numChans() const { return mChans; }; | ||||
|  | ||||
|   void setSignalHandler(osmo_signal_cbfn cbfn); | ||||
|  | ||||
|   /** Codes for channel combinations */ | ||||
|   typedef enum { | ||||
|     FILL,               ///< Channel is transmitted, but unused | ||||
| @@ -144,14 +151,6 @@ public: | ||||
|     LOOPBACK            ///< similar go VII, used in loopback testing | ||||
|   } ChannelCombination; | ||||
|  | ||||
|   enum FillerType { | ||||
|     FILLER_DUMMY, | ||||
|     FILLER_ZERO, | ||||
|     FILLER_NORM_RAND, | ||||
|     FILLER_EDGE_RAND, | ||||
|     FILLER_ACCESS_RAND, | ||||
|   }; | ||||
|  | ||||
| private: | ||||
|   int mBasePort; | ||||
|   std::string mLocalAddr; | ||||
| @@ -181,6 +180,8 @@ private: | ||||
|  | ||||
|   double rssiOffset;                      ///< RSSI to dBm conversion offset | ||||
|  | ||||
|   osmo_signal_cbfn *sig_cbfn;              ///< Registered Signal Handler to announce events. | ||||
|  | ||||
|   /** modulate and add a burst to the transmit queue */ | ||||
|   void addRadioVector(size_t chan, BitVector &bits, | ||||
|                       int RSSI, GSM::Time &wTime); | ||||
|   | ||||
							
								
								
									
										8
									
								
								Transceiver52M/arch/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Transceiver52M/arch/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| SUBDIRS = common | ||||
| if ARCH_ARM | ||||
| SUBDIRS += arm | ||||
| else | ||||
| SUBDIRS += x86 | ||||
| endif | ||||
| @@ -1,17 +1,17 @@ | ||||
| if ARCH_ARM | ||||
| if ARCH_ARM_A15 | ||||
| ARCH_FLAGS = -mfpu=neon-vfpv4 | ||||
| else | ||||
| ARCH_FLAGS = -mfpu=neon | ||||
| endif | ||||
| 
 | ||||
| AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I../common | ||||
| AM_CFLAGS = -Wall $(ARCH_FLAGS) -std=gnu99 -I${srcdir}/../common | ||||
| AM_CCASFLAGS = $(ARCH_FLAGS) | ||||
| 
 | ||||
| noinst_LTLIBRARIES = libarch.la | ||||
| 
 | ||||
| libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la | ||||
| 
 | ||||
| libarch_la_SOURCES = \
 | ||||
| 	../common/convolve_base.c \
 | ||||
| 	convert.c \
 | ||||
| 	convert_neon.S \
 | ||||
| 	convolve.c \
 | ||||
| @@ -20,4 +20,3 @@ libarch_la_SOURCES = \ | ||||
| 	scale_neon.S \
 | ||||
| 	mult.c \
 | ||||
| 	mult_neon.S | ||||
| endif | ||||
							
								
								
									
										15
									
								
								Transceiver52M/arch/common/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								Transceiver52M/arch/common/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,15 @@ | ||||
| AM_CFLAGS = -Wall -std=gnu99 | ||||
|  | ||||
| noinst_LTLIBRARIES = libarch_common.la | ||||
|  | ||||
| noinst_HEADERS = \ | ||||
|         convolve.h \ | ||||
|         convert.h \ | ||||
|         scale.h \ | ||||
|         mult.h \ | ||||
|         fft.h | ||||
|  | ||||
| libarch_common_la_SOURCES = \ | ||||
|         convolve_base.c \ | ||||
|         convert_base.c \ | ||||
|         fft.c | ||||
| @@ -1,7 +1,7 @@ | ||||
| #ifndef _CONVOLVE_H_ | ||||
| #define _CONVOLVE_H_ | ||||
| 
 | ||||
| void *convolve_h_alloc(int num); | ||||
| void *convolve_h_alloc(size_t num); | ||||
| 
 | ||||
| int convolve_real(const float *x, int x_len, | ||||
| 		  const float *h, int h_len, | ||||
| @@ -146,7 +146,7 @@ int base_convolve_complex(const float *x, int x_len, | ||||
| } | ||||
| 
 | ||||
| /* Aligned filter tap allocation */ | ||||
| void *convolve_h_alloc(int len) | ||||
| void *convolve_h_alloc(size_t len) | ||||
| { | ||||
| #ifdef HAVE_SSE3 | ||||
| 	return memalign(16, len * 2 * sizeof(float)); | ||||
| @@ -1,11 +1,15 @@ | ||||
| if !ARCH_ARM | ||||
| AM_CFLAGS = -Wall -std=gnu99 -I${srcdir}/../common | ||||
| 
 | ||||
| noinst_LTLIBRARIES = libarch.la | ||||
| noinst_LTLIBRARIES += libarch_sse_3.la | ||||
| noinst_LTLIBRARIES += libarch_sse_4_1.la | ||||
| 
 | ||||
| libarch_la_LIBADD = | ||||
| noinst_HEADERS = \
 | ||||
| 	convert_sse_3.h \
 | ||||
| 	convert_sse_4_1.h \
 | ||||
| 	convolve_sse_3.h | ||||
| 
 | ||||
| libarch_la_LIBADD = $(top_builddir)/Transceiver52M/arch/common/libarch_common.la | ||||
| 
 | ||||
| # SSE 3 specific code
 | ||||
| if HAVE_SSE3 | ||||
| @@ -25,8 +29,5 @@ libarch_la_LIBADD += libarch_sse_4_1.la | ||||
| endif | ||||
| 
 | ||||
| libarch_la_SOURCES = \
 | ||||
| 	../common/convolve_base.c \
 | ||||
| 	../common/convert_base.c \
 | ||||
| 	convert.c \
 | ||||
| 	convolve.c | ||||
| endif | ||||
							
								
								
									
										17
									
								
								Transceiver52M/device/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								Transceiver52M/device/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,17 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| noinst_HEADERS = radioDevice.h | ||||
|  | ||||
| SUBDIRS = | ||||
|  | ||||
| if DEVICE_USRP1 | ||||
| SUBDIRS += usrp1 | ||||
| endif | ||||
|  | ||||
| if DEVICE_UHD | ||||
| SUBDIRS += uhd | ||||
| endif | ||||
|  | ||||
| if DEVICE_LMS | ||||
| SUBDIRS += lms | ||||
| endif | ||||
							
								
								
									
										700
									
								
								Transceiver52M/device/lms/LMSDevice.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										700
									
								
								Transceiver52M/device/lms/LMSDevice.cpp
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,700 @@ | ||||
| /* | ||||
| * Copyright 2018 sysmocom - s.f.m.c. GmbH | ||||
| * | ||||
| 	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 <stdint.h> | ||||
| #include <string.h> | ||||
| #include <stdlib.h> | ||||
| #include "Logger.h" | ||||
| #include "Threads.h" | ||||
| #include "LMSDevice.h" | ||||
| #include "Utils.h" | ||||
|  | ||||
| #include <lime/LimeSuite.h> | ||||
|  | ||||
| #include <osmocom/core/utils.h> | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
|  | ||||
| using namespace std; | ||||
|  | ||||
| constexpr double LMSDevice::masterClockRate; | ||||
|  | ||||
| #define MAX_ANTENNA_LIST_SIZE 10 | ||||
| #define LMS_SAMPLE_RATE GSMRATE*32 | ||||
| #define GSM_CARRIER_BW 270000.0 /* 270kHz */ | ||||
| #define LMS_MIN_BW_SUPPORTED 2.5e6 /* 2.5mHz, minimum supported by LMS */ | ||||
| #define LMS_CALIBRATE_BW_HZ OSMO_MAX(GSM_CARRIER_BW, LMS_MIN_BW_SUPPORTED) | ||||
|  | ||||
| static int compat_LMS_VCTCXORead(lms_device_t *dev, uint16_t *val, bool memory) | ||||
| { | ||||
| #if HAVE_LMS_VCTCXO_EEPROM_SAVING | ||||
| 	return LMS_VCTCXORead(dev, val, memory); | ||||
| #else | ||||
| 	return LMS_VCTCXORead(dev, val); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| static int compat_LMS_VCTCXOWrite(lms_device_t *dev, uint16_t val, bool memory) | ||||
| { | ||||
| #if HAVE_LMS_VCTCXO_EEPROM_SAVING | ||||
| 	return LMS_VCTCXOWrite(dev, val, memory); | ||||
| #else | ||||
| 	return LMS_VCTCXOWrite(dev, val); | ||||
| #endif | ||||
| } | ||||
|  | ||||
| LMSDevice::LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset, | ||||
| 		     const std::vector<std::string>& tx_paths, | ||||
| 		     const std::vector<std::string>& rx_paths): | ||||
| 	RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths), | ||||
| 	m_lms_dev(NULL) | ||||
| { | ||||
| 	LOGC(DDEV, INFO) << "creating LMS device..."; | ||||
|  | ||||
| 	m_lms_stream_rx.resize(chans); | ||||
| 	m_lms_stream_tx.resize(chans); | ||||
|  | ||||
| 	m_last_rx_underruns.resize(chans, 0); | ||||
| 	m_last_rx_overruns.resize(chans, 0); | ||||
| 	m_last_tx_underruns.resize(chans, 0); | ||||
| 	m_last_tx_overruns.resize(chans, 0); | ||||
| } | ||||
|  | ||||
| LMSDevice::~LMSDevice() | ||||
| { | ||||
| 	LOGC(DDEV, INFO) << "Closing LMS device"; | ||||
| 	if (m_lms_dev) { | ||||
| 		LMS_Close(m_lms_dev); | ||||
| 		m_lms_dev = NULL; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void lms_log_callback(int lvl, const char *msg) | ||||
| { | ||||
| 	/* map lime specific log levels */ | ||||
| 	static const int lvl_map[5] = { | ||||
| 		[0] = LOGL_FATAL, | ||||
| 		[LMS_LOG_ERROR] = LOGL_ERROR, | ||||
| 		[LMS_LOG_WARNING] = LOGL_NOTICE, | ||||
| 		[LMS_LOG_INFO] = LOGL_INFO, | ||||
| 		[LMS_LOG_DEBUG] = LOGL_DEBUG, | ||||
| 	}; | ||||
| 	/* protect against future higher log level values (lower importance) */ | ||||
| 	if ((unsigned int) lvl >= ARRAY_SIZE(lvl_map)) | ||||
| 		lvl = ARRAY_SIZE(lvl_map)-1; | ||||
|  | ||||
| 	LOGLV(DLMS, lvl_map[lvl]) << msg; | ||||
| } | ||||
|  | ||||
| static void thread_enable_cancel(bool cancel) | ||||
| { | ||||
| 	cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) : | ||||
| 		 pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL); | ||||
| } | ||||
|  | ||||
| static void print_range(const char* name, lms_range_t *range) | ||||
| { | ||||
| 	LOGC(DDEV, INFO) << name << ": Min=" << range->min << " Max=" << range->max | ||||
| 		   << " Step=" << range->step; | ||||
| } | ||||
|  | ||||
| /*! Find the device string that matches all filters from \a args. | ||||
|  *  \param[in] info_list device addresses found by LMS_GetDeviceList() | ||||
|  *  \param[in] count length of info_list | ||||
|  *  \param[in] args dev-args value from osmo-trx.cfg, containing comma separated key=value pairs | ||||
|  *  \return index of first matching device or -1 (no match) */ | ||||
| int info_list_find(lms_info_str_t* info_list, unsigned int count, const std::string &args) | ||||
| { | ||||
| 	unsigned int i, j; | ||||
| 	vector<string> filters; | ||||
|  | ||||
| 	filters = comma_delimited_to_vector(args.c_str()); | ||||
|  | ||||
| 	/* iterate over device addresses */ | ||||
| 	for (i=0; i < count; i++) { | ||||
| 		/* check if all filters match */ | ||||
| 		bool match = true; | ||||
| 		for (j=0; j < filters.size(); j++) { | ||||
| 			if (!strstr(info_list[i], filters[j].c_str())) { | ||||
| 				match = false; | ||||
| 				break; | ||||
| 			} | ||||
| 		} | ||||
|  | ||||
| 		if (match) | ||||
| 			return i; | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| int LMSDevice::open(const std::string &args, int ref, bool swap_channels) | ||||
| { | ||||
| 	//lms_info_str_t dev_str; | ||||
| 	lms_info_str_t* info_list; | ||||
| 	lms_range_t range_lpfbw_rx, range_lpfbw_tx, range_sr; | ||||
| 	float_type sr_host, sr_rf, lpfbw_rx, lpfbw_tx; | ||||
| 	uint16_t dac_val; | ||||
| 	unsigned int i, n; | ||||
| 	int rc, dev_id; | ||||
|  | ||||
| 	LOGC(DDEV, INFO) << "Opening LMS device.."; | ||||
|  | ||||
| 	LMS_RegisterLogHandler(&lms_log_callback); | ||||
|  | ||||
| 	if ((n = LMS_GetDeviceList(NULL)) < 0) | ||||
| 		LOGC(DDEV, ERROR) << "LMS_GetDeviceList(NULL) failed"; | ||||
| 	LOGC(DDEV, INFO) << "Devices found: " << n; | ||||
| 	if (n < 1) | ||||
| 	    return -1; | ||||
|  | ||||
| 	info_list = new lms_info_str_t[n]; | ||||
|  | ||||
| 	if (LMS_GetDeviceList(info_list) < 0) | ||||
| 		LOGC(DDEV, ERROR) << "LMS_GetDeviceList(info_list) failed"; | ||||
|  | ||||
| 	for (i = 0; i < n; i++) | ||||
| 		LOGC(DDEV, INFO) << "Device [" << i << "]: " << info_list[i]; | ||||
|  | ||||
| 	dev_id = info_list_find(info_list, n, args); | ||||
| 	if (dev_id == -1) { | ||||
| 		LOGC(DDEV, ERROR) << "No LMS device found with address '" << args << "'"; | ||||
| 		delete[] info_list; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	LOGC(DDEV, INFO) << "Using device[" << dev_id << "]"; | ||||
| 	rc = LMS_Open(&m_lms_dev, info_list[dev_id], NULL); | ||||
| 	if (rc != 0) { | ||||
| 		LOGC(DDEV, ERROR) << "LMS_GetDeviceList() failed)"; | ||||
| 		delete [] info_list; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	delete [] info_list; | ||||
|  | ||||
| 	LOGC(DDEV, INFO) << "Init LMS device"; | ||||
| 	if (LMS_Init(m_lms_dev) != 0) { | ||||
| 		LOGC(DDEV, ERROR) << "LMS_Init() failed"; | ||||
| 		goto out_close; | ||||
| 	} | ||||
|  | ||||
| 	if (LMS_GetSampleRateRange(m_lms_dev, LMS_CH_RX, &range_sr)) | ||||
| 		goto out_close; | ||||
| 	print_range("Sample Rate", &range_sr); | ||||
|  | ||||
| 	LOGC(DDEV, INFO) << "Setting sample rate to " << GSMRATE*tx_sps << " " << tx_sps; | ||||
| 	if (LMS_SetSampleRate(m_lms_dev, GSMRATE*tx_sps, 32) < 0) | ||||
| 		goto out_close; | ||||
|  | ||||
| 	if (LMS_GetSampleRate(m_lms_dev, LMS_CH_RX, 0, &sr_host, &sr_rf)) | ||||
| 		goto out_close; | ||||
| 	LOGC(DDEV, INFO) << "Sample Rate: Host=" << sr_host << " RF=" << sr_rf; | ||||
|  | ||||
| 	/* FIXME: make this device/model dependent, like UHDDevice:dev_param_map! */ | ||||
| 	ts_offset = static_cast<TIMESTAMP>(8.9e-5 * GSMRATE * tx_sps); /* time * sample_rate */ | ||||
|  | ||||
| 	switch (ref) { | ||||
| 	case REF_INTERNAL: | ||||
| 		LOGC(DDEV, INFO) << "Setting Internal clock reference"; | ||||
| 		/* Ugly API: Selecting clock source implicit by writing to VCTCXO DAC ?!? */ | ||||
| 		if (compat_LMS_VCTCXORead(m_lms_dev, &dac_val, false) < 0) | ||||
| 			goto out_close; | ||||
| 		LOGC(DDEV, INFO) << "Setting VCTCXO to " << dac_val; | ||||
| 		if (compat_LMS_VCTCXOWrite(m_lms_dev, dac_val, false) < 0) | ||||
| 			goto out_close; | ||||
| 		break; | ||||
| 	case REF_EXTERNAL: | ||||
| 		LOGC(DDEV, INFO) << "Setting External clock reference to " << 10000000.0; | ||||
| 		/* Assume an external 10 MHz reference clock */ | ||||
| 		if (LMS_SetClockFreq(m_lms_dev, LMS_CLOCK_EXTREF, 10000000.0) < 0) | ||||
| 			goto out_close; | ||||
| 		break; | ||||
| 	default: | ||||
| 		LOGC(DDEV, ALERT) << "Invalid reference type"; | ||||
| 		goto out_close; | ||||
| 	} | ||||
|  | ||||
| 	if (LMS_GetLPFBWRange(m_lms_dev, LMS_CH_RX, &range_lpfbw_rx)) | ||||
| 		goto out_close; | ||||
| 	print_range("LPFBWRange Rx", &range_lpfbw_rx); | ||||
| 	if (LMS_GetLPFBWRange(m_lms_dev, LMS_CH_RX, &range_lpfbw_tx)) | ||||
| 		goto out_close; | ||||
| 	print_range("LPFBWRange Tx", &range_lpfbw_tx); | ||||
| 	lpfbw_rx = OSMO_MIN(OSMO_MAX(1.4001e6, range_lpfbw_rx.min), range_lpfbw_rx.max); | ||||
| 	lpfbw_tx = OSMO_MIN(OSMO_MAX(5.2e6, range_lpfbw_tx.min), range_lpfbw_tx.max); | ||||
|  | ||||
| 	LOGC(DDEV, INFO) << "LPFBW: Rx=" << lpfbw_rx << " Tx=" << lpfbw_tx; | ||||
|  | ||||
| 	if (!set_antennas()) { | ||||
| 		LOGC(DDEV, ALERT) << "LMS antenna setting failed"; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* Perform Rx and Tx calibration */ | ||||
| 	for (i=0; i<chans; i++) { | ||||
| 		LOGC(DDEV, INFO) << "Setting LPFBW chan " << i; | ||||
| 		if (LMS_SetLPFBW(m_lms_dev, LMS_CH_RX, i, lpfbw_rx) < 0) | ||||
| 			goto out_close; | ||||
| 		if (LMS_SetLPFBW(m_lms_dev, LMS_CH_TX, i, lpfbw_tx) < 0) | ||||
| 			goto out_close; | ||||
| 		LOGC(DDEV, INFO) << "Calibrating chan " << i; | ||||
| 		if (LMS_Calibrate(m_lms_dev, LMS_CH_RX, i, LMS_CALIBRATE_BW_HZ, 0) < 0) | ||||
| 			goto out_close; | ||||
| 		if (LMS_Calibrate(m_lms_dev, LMS_CH_TX, i, LMS_CALIBRATE_BW_HZ, 0) < 0) | ||||
| 			goto out_close; | ||||
| 	} | ||||
|  | ||||
| 	samplesRead = 0; | ||||
| 	samplesWritten = 0; | ||||
| 	started = false; | ||||
|  | ||||
| 	return NORMAL; | ||||
|  | ||||
| out_close: | ||||
| 	LOGC(DDEV, ALERT) << "Error in LMS open, closing: " << LMS_GetLastErrorMessage(); | ||||
| 	LMS_Close(m_lms_dev); | ||||
| 	m_lms_dev = NULL; | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::start() | ||||
| { | ||||
| 	LOGC(DDEV, INFO) << "starting LMS..."; | ||||
|  | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	if (started) { | ||||
| 		LOGC(DDEV, ERR) << "Device already started"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	/* configure the channels/streams */ | ||||
| 	for (i=0; i<chans; i++) { | ||||
| 		if (LMS_EnableChannel(m_lms_dev, LMS_CH_RX, i, true) < 0) | ||||
| 			return false; | ||||
|  | ||||
| 		if (LMS_EnableChannel(m_lms_dev, LMS_CH_TX, i, true) < 0) | ||||
| 			return false; | ||||
|  | ||||
| 		// Set gains to midpoint | ||||
| 		setTxGain((minTxGain() + maxTxGain()) / 2, i); | ||||
| 		setRxGain((minRxGain() + maxRxGain()) / 2, i); | ||||
|  | ||||
| 		m_lms_stream_rx[i] = {}; | ||||
| 		m_lms_stream_rx[i].isTx = false; | ||||
| 		m_lms_stream_rx[i].channel = i; | ||||
| 		m_lms_stream_rx[i].fifoSize = 1024 * 1024; | ||||
| 		m_lms_stream_rx[i].throughputVsLatency = 0.3; | ||||
| 		m_lms_stream_rx[i].dataFmt = lms_stream_t::LMS_FMT_I16; | ||||
|  | ||||
| 		m_lms_stream_tx[i] = {}; | ||||
| 		m_lms_stream_tx[i].isTx = true; | ||||
| 		m_lms_stream_tx[i].channel = i; | ||||
| 		m_lms_stream_tx[i].fifoSize = 1024 * 1024; | ||||
| 		m_lms_stream_tx[i].throughputVsLatency = 0.3; | ||||
| 		m_lms_stream_tx[i].dataFmt = lms_stream_t::LMS_FMT_I16; | ||||
|  | ||||
| 		if (LMS_SetupStream(m_lms_dev, &m_lms_stream_rx[i]) < 0) | ||||
| 			return false; | ||||
|  | ||||
| 		if (LMS_SetupStream(m_lms_dev, &m_lms_stream_tx[i]) < 0) | ||||
| 			return false; | ||||
| 	} | ||||
|  | ||||
| 	/* now start the streams in a second loop, as we can no longer call | ||||
| 	 * LMS_SetupStream() after LMS_StartStream() of the first stream */ | ||||
| 	for (i = 0; i < chans; i++) { | ||||
| 		if (LMS_StartStream(&m_lms_stream_rx[i]) < 0) | ||||
| 			return false; | ||||
|  | ||||
| 		if (LMS_StartStream(&m_lms_stream_tx[i]) < 0) | ||||
| 			return false; | ||||
| 	} | ||||
|  | ||||
| 	flush_recv(10); | ||||
|  | ||||
| 	started = true; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::stop() | ||||
| { | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	if (!started) | ||||
| 		return true; | ||||
|  | ||||
| 	for (i=0; i<chans; i++) { | ||||
| 		LMS_StopStream(&m_lms_stream_tx[i]); | ||||
| 		LMS_StopStream(&m_lms_stream_rx[i]); | ||||
| 	} | ||||
|  | ||||
| 	for (i=0; i<chans; i++) { | ||||
| 		LMS_DestroyStream(m_lms_dev, &m_lms_stream_tx[i]); | ||||
| 		LMS_DestroyStream(m_lms_dev, &m_lms_stream_rx[i]); | ||||
| 		LMS_EnableChannel(m_lms_dev, LMS_CH_RX, i, false); | ||||
| 		LMS_EnableChannel(m_lms_dev, LMS_CH_TX, i, false); | ||||
| 	} | ||||
|  | ||||
| 	started = false; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| double LMSDevice::maxTxGain() | ||||
| { | ||||
| 	return 73.0; | ||||
| } | ||||
|  | ||||
| double LMSDevice::minTxGain() | ||||
| { | ||||
| 	return 0.0; | ||||
| } | ||||
|  | ||||
| double LMSDevice::maxRxGain() | ||||
| { | ||||
| 	return 73.0; | ||||
| } | ||||
|  | ||||
| double LMSDevice::minRxGain() | ||||
| { | ||||
| 	return 0.0; | ||||
| } | ||||
|  | ||||
| double LMSDevice::setTxGain(double dB, size_t chan) | ||||
| { | ||||
| 	if (dB > maxTxGain()) | ||||
| 		dB = maxTxGain(); | ||||
| 	if (dB < minTxGain()) | ||||
| 		dB = minTxGain(); | ||||
|  | ||||
| 	LOGC(DDEV, NOTICE) << "chan " << chan <<": Setting TX gain to " << dB << " dB"; | ||||
|  | ||||
| 	if (LMS_SetGaindB(m_lms_dev, LMS_CH_TX, chan, dB) < 0) | ||||
| 		LOGC(DDEV, ERR) << "chan " << chan <<": Error setting TX gain to " << dB << " dB"; | ||||
|  | ||||
| 	return dB; | ||||
| } | ||||
|  | ||||
| double LMSDevice::setRxGain(double dB, size_t chan) | ||||
| { | ||||
| 	if (dB > maxRxGain()) | ||||
| 		dB = maxRxGain(); | ||||
| 	if (dB < minRxGain()) | ||||
| 		dB = minRxGain(); | ||||
|  | ||||
| 	LOGC(DDEV, NOTICE) << "chan "<< chan << ": Setting RX gain to " << dB << " dB"; | ||||
|  | ||||
| 	if (LMS_SetGaindB(m_lms_dev, LMS_CH_RX, chan, dB) < 0) | ||||
| 		LOGC(DDEV, ERR) << "chan "<< chan << ": Error setting RX gain to " << dB << " dB"; | ||||
|  | ||||
| 	return dB; | ||||
| } | ||||
|  | ||||
| int LMSDevice::get_ant_idx(const std::string & name, bool dir_tx, size_t chan) | ||||
| { | ||||
| 	lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */ | ||||
| 	const char* c_name = name.c_str(); | ||||
| 	int num_names; | ||||
| 	int i; | ||||
|  | ||||
| 	num_names = LMS_GetAntennaList(m_lms_dev, dir_tx, chan, name_list); | ||||
| 	for (i = 0; i < num_names; i++) { | ||||
| 		if (!strcmp(c_name, name_list[i])) | ||||
| 			return i; | ||||
| 	} | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::flush_recv(size_t num_pkts) | ||||
| { | ||||
| 	#define CHUNK 625 | ||||
| 	int len = CHUNK * tx_sps; | ||||
| 	short *buffer = new short[len * 2]; | ||||
| 	int rc; | ||||
| 	lms_stream_meta_t rx_metadata = {}; | ||||
| 	rx_metadata.flushPartialPacket = false; | ||||
| 	rx_metadata.waitForTimestamp = false; | ||||
|  | ||||
| 	ts_initial = 0; | ||||
|  | ||||
| 	while (!ts_initial || (num_pkts-- > 0)) { | ||||
| 		rc = LMS_RecvStream(&m_lms_stream_rx[0], &buffer[0], len, &rx_metadata, 100); | ||||
| 		LOGC(DDEV, DEBUG) << "Flush: Recv buffer of len " << rc << " at " << std::hex << rx_metadata.timestamp; | ||||
| 		if (rc != len) { | ||||
| 			LOGC(DDEV, ALERT) << "LMS: Device receive timed out"; | ||||
| 			delete[] buffer; | ||||
| 			return false; | ||||
| 		} | ||||
|  | ||||
| 		ts_initial = rx_metadata.timestamp + len; | ||||
| 	} | ||||
|  | ||||
| 	LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl; | ||||
| 	delete[] buffer; | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::setRxAntenna(const std::string & ant, size_t chan) | ||||
| { | ||||
| 	int idx; | ||||
|  | ||||
| 	if (chan >= rx_paths.size()) { | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	idx = get_ant_idx(ant, LMS_CH_RX, chan); | ||||
| 	if (idx < 0) { | ||||
| 		LOGC(DDEV, ALERT) << "Invalid Rx Antenna"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if (LMS_SetAntenna(m_lms_dev, LMS_CH_RX, chan, idx) < 0) { | ||||
| 		LOGC(DDEV, ALERT) << "Unable to set Rx Antenna"; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| std::string LMSDevice::getRxAntenna(size_t chan) | ||||
| { | ||||
| 	lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */ | ||||
| 	int idx; | ||||
|  | ||||
| 	if (chan >= rx_paths.size()) { | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return ""; | ||||
| 	} | ||||
|  | ||||
| 	idx = LMS_GetAntenna(m_lms_dev, LMS_CH_RX, chan); | ||||
| 	if (idx < 0) { | ||||
| 		LOGC(DDEV, ALERT) << "Error getting Rx Antenna"; | ||||
| 		return ""; | ||||
| 	} | ||||
|  | ||||
| 	if (LMS_GetAntennaList(m_lms_dev, LMS_CH_RX, chan, name_list) < idx) { | ||||
| 		LOGC(DDEV, ALERT) << "Error getting Rx Antenna List"; | ||||
| 		return ""; | ||||
| 	} | ||||
|  | ||||
| 	return name_list[idx]; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::setTxAntenna(const std::string & ant, size_t chan) | ||||
| { | ||||
| 	int idx; | ||||
|  | ||||
| 	if (chan >= tx_paths.size()) { | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	idx = get_ant_idx(ant, LMS_CH_TX, chan); | ||||
| 	if (idx < 0) { | ||||
| 		LOGC(DDEV, ALERT) << "Invalid Rx Antenna"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	if (LMS_SetAntenna(m_lms_dev, LMS_CH_TX, chan, idx) < 0) { | ||||
| 		LOGC(DDEV, ALERT) << "Unable to set Rx Antenna"; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| std::string LMSDevice::getTxAntenna(size_t chan) | ||||
| { | ||||
| 	lms_name_t name_list[MAX_ANTENNA_LIST_SIZE]; /* large enough list for antenna names. */ | ||||
| 	int idx; | ||||
|  | ||||
| 	if (chan >= tx_paths.size()) { | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return ""; | ||||
| 	} | ||||
|  | ||||
| 	idx = LMS_GetAntenna(m_lms_dev, LMS_CH_TX, chan); | ||||
| 	if (idx < 0) { | ||||
| 		LOGC(DDEV, ALERT) << "Error getting Tx Antenna"; | ||||
| 		return ""; | ||||
| 	} | ||||
|  | ||||
| 	if (LMS_GetAntennaList(m_lms_dev, LMS_CH_TX, chan, name_list) < idx) { | ||||
| 		LOGC(DDEV, ALERT) << "Error getting Tx Antenna List"; | ||||
| 		return ""; | ||||
| 	} | ||||
|  | ||||
| 	return name_list[idx]; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::requiresRadioAlign() | ||||
| { | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| GSM::Time LMSDevice::minLatency() { | ||||
| 	/* Empirical data from a handful of | ||||
| 	relatively recent machines shows that the B100 will underrun when | ||||
| 	the transmit threshold is reduced to a time of 6 and a half frames, | ||||
| 	so we set a minimum 7 frame threshold. */ | ||||
| 	return GSM::Time(6,7); | ||||
| } | ||||
|  | ||||
| void LMSDevice::update_stream_stats(size_t chan, bool * underrun, bool * overrun) | ||||
| { | ||||
| 	lms_stream_status_t status; | ||||
| 	if (LMS_GetStreamStatus(&m_lms_stream_rx[chan], &status) == 0) { | ||||
| 		if (status.underrun > m_last_rx_underruns[chan]) | ||||
| 			*underrun = true; | ||||
| 		m_last_rx_underruns[chan] = status.underrun; | ||||
|  | ||||
| 		if (status.overrun > m_last_rx_overruns[chan]) | ||||
| 			*overrun = true; | ||||
| 		m_last_rx_overruns[chan] = status.overrun; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // NOTE: Assumes sequential reads | ||||
| int LMSDevice::readSamples(std::vector < short *>&bufs, int len, bool * overrun, | ||||
| 			   TIMESTAMP timestamp, bool * underrun, unsigned *RSSI) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	unsigned int i; | ||||
| 	lms_stream_meta_t rx_metadata = {}; | ||||
| 	rx_metadata.flushPartialPacket = false; | ||||
| 	rx_metadata.waitForTimestamp = false; | ||||
| 	rx_metadata.timestamp = 0; | ||||
|  | ||||
| 	if (bufs.size() != chans) { | ||||
| 		LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size(); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	*overrun = false; | ||||
| 	*underrun = false; | ||||
| 	for (i = 0; i<chans; i++) { | ||||
| 		thread_enable_cancel(false); | ||||
| 		rc = LMS_RecvStream(&m_lms_stream_rx[i], bufs[i], len, &rx_metadata, 100); | ||||
| 		update_stream_stats(i, underrun, overrun); | ||||
| 		if (rc != len) { | ||||
| 			LOGC(DDEV, ALERT) << "LMS: Device receive timed out (" << rc << " vs exp " << len << ")."; | ||||
| 			thread_enable_cancel(true); | ||||
| 			return -1; | ||||
| 		} | ||||
| 		if (timestamp != (TIMESTAMP)rx_metadata.timestamp) | ||||
| 			LOGC(DDEV, ALERT) << "chan "<< i << " recv buffer of len " << rc << " expect " << std::hex << timestamp << " got " << std::hex << (TIMESTAMP)rx_metadata.timestamp << " (" << std::hex << rx_metadata.timestamp <<") diff=" << rx_metadata.timestamp - timestamp; | ||||
| 		thread_enable_cancel(true); | ||||
| 	} | ||||
|  | ||||
| 	samplesRead += rc; | ||||
|  | ||||
| 	if (((TIMESTAMP) rx_metadata.timestamp) < timestamp) | ||||
| 		rc = 0; | ||||
|  | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| int LMSDevice::writeSamples(std::vector < short *>&bufs, int len, | ||||
| 			    bool * underrun, unsigned long long timestamp, | ||||
| 			    bool isControl) | ||||
| { | ||||
| 	int rc = 0; | ||||
| 	unsigned int i; | ||||
| 	lms_stream_status_t status; | ||||
| 	lms_stream_meta_t tx_metadata = {}; | ||||
| 	tx_metadata.flushPartialPacket = false; | ||||
| 	tx_metadata.waitForTimestamp = true; | ||||
| 	tx_metadata.timestamp = timestamp - ts_offset;	/* Shift Tx time by offset */ | ||||
|  | ||||
| 	if (isControl) { | ||||
| 		LOGC(DDEV, ERR) << "Control packets not supported"; | ||||
| 		return 0; | ||||
| 	} | ||||
|  | ||||
| 	if (bufs.size() != chans) { | ||||
| 		LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size(); | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	*underrun = false; | ||||
|  | ||||
| 	for (i = 0; i<chans; i++) { | ||||
| 		LOGC(DDEV, DEBUG) << "chan "<< i << " send buffer of len " << len << " timestamp " << std::hex << tx_metadata.timestamp; | ||||
| 		thread_enable_cancel(false); | ||||
| 		rc = LMS_SendStream(&m_lms_stream_tx[i], bufs[i], len, &tx_metadata, 100); | ||||
| 		if (rc != len) { | ||||
| 			LOGC(DDEV, ALERT) << "LMS: Device send timed out"; | ||||
| 		} | ||||
|  | ||||
| 		if (LMS_GetStreamStatus(&m_lms_stream_tx[i], &status) == 0) { | ||||
| 			if (status.underrun > m_last_tx_underruns[i]) | ||||
| 				*underrun = true; | ||||
| 			m_last_tx_underruns[i] = status.underrun; | ||||
| 		} | ||||
| 		thread_enable_cancel(true); | ||||
| 	} | ||||
|  | ||||
| 	samplesWritten += rc; | ||||
|  | ||||
| 	return rc; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::updateAlignment(TIMESTAMP timestamp) | ||||
| { | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::setTxFreq(double wFreq, size_t chan) | ||||
| { | ||||
| 	LOGC(DDEV, NOTICE) << "chan "<< chan << ": Setting Tx Freq to " << wFreq << " Hz"; | ||||
|  | ||||
| 	if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_TX, chan, wFreq) < 0) { | ||||
| 		LOGC(DDEV, ERROR) << "chan "<< chan << ": Error setting Tx Freq to " << wFreq << " Hz"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| bool LMSDevice::setRxFreq(double wFreq, size_t chan) | ||||
| { | ||||
| 	LOGC(DDEV, NOTICE) << "chan "<< chan << ": Setting Rx Freq to " << wFreq << " Hz"; | ||||
|  | ||||
| 	if (LMS_SetLOFrequency(m_lms_dev, LMS_CH_RX, chan, wFreq) < 0) { | ||||
| 		LOGC(DDEV, ERROR) << "chan "<< chan << ": Error setting Rx Freq to " << wFreq << " Hz"; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	return true; | ||||
| } | ||||
|  | ||||
| RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, | ||||
| 			       InterfaceType iface, size_t chans, double lo_offset, | ||||
| 			       const std::vector < std::string > &tx_paths, | ||||
| 			       const std::vector < std::string > &rx_paths) | ||||
| { | ||||
| 	if (tx_sps != rx_sps) { | ||||
| 		LOGC(DDEV, ERROR) << "LMS Requires tx_sps == rx_sps"; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (lo_offset != 0.0) { | ||||
| 		LOGC(DDEV, ERROR) << "LMS doesn't support lo_offset"; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	return new LMSDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths); | ||||
| } | ||||
							
								
								
									
										211
									
								
								Transceiver52M/device/lms/LMSDevice.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										211
									
								
								Transceiver52M/device/lms/LMSDevice.h
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,211 @@ | ||||
| /* | ||||
| * Copyright 2018 sysmocom - s.f.m.c. GmbH | ||||
| * | ||||
| * This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion. | ||||
| * | ||||
| * This use of this software may be subject to additional restrictions. | ||||
| * See the LEGAL file in the main directory for details. | ||||
|  | ||||
|     This program is distributed in the hope that it will be useful, | ||||
|     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||
|     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. | ||||
|  | ||||
| */ | ||||
|  | ||||
| #ifndef _LMS_DEVICE_H_ | ||||
| #define _LMS_DEVICE_H_ | ||||
|  | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
|  | ||||
| #include "radioDevice.h" | ||||
|  | ||||
| #include <sys/time.h> | ||||
| #include <math.h> | ||||
| #include <limits.h> | ||||
| #include <string> | ||||
| #include <iostream> | ||||
| #include <lime/LimeSuite.h> | ||||
|  | ||||
| /* Definition of LIMESDR_TX_AMPL limits maximum amplitude of I and Q | ||||
|  * channels separately. Hence LIMESDR_TX_AMPL value must be 1/sqrt(2) = | ||||
|  * 0.7071.... to get an amplitude of 1 of the complex signal: | ||||
|  * 	A^2 = I^2 + Q^2 | ||||
|  * 	A^2 = (1/sqrt(2))^2 + (1/sqrt(2))^2 | ||||
|  * 	A^2 = 1/2 + 1/2 | ||||
|  * 	A^2 = 1 */ | ||||
| #define LIMESDR_TX_AMPL  0.707 | ||||
|  | ||||
| /** A class to handle a LimeSuite supported device */ | ||||
| class LMSDevice:public RadioDevice { | ||||
|  | ||||
| private: | ||||
|  | ||||
| 	static constexpr double masterClockRate = 52.0e6; | ||||
|  | ||||
| 	lms_device_t *m_lms_dev; | ||||
| 	std::vector<lms_stream_t> m_lms_stream_rx; | ||||
| 	std::vector<lms_stream_t> m_lms_stream_tx; | ||||
|  | ||||
| 	std::vector<uint32_t> m_last_rx_underruns; | ||||
| 	std::vector<uint32_t> m_last_rx_overruns; | ||||
| 	std::vector<uint32_t> m_last_tx_underruns; | ||||
| 	std::vector<uint32_t> m_last_tx_overruns; | ||||
|  | ||||
| 	double actualSampleRate;	///< the actual USRP sampling rate | ||||
|  | ||||
| 	unsigned long long samplesRead;	///< number of samples read from LMS | ||||
| 	unsigned long long samplesWritten;	///< number of samples sent to LMS | ||||
|  | ||||
| 	bool started;		///< flag indicates LMS has started | ||||
| 	bool skipRx;		///< set if LMS is transmit-only. | ||||
|  | ||||
| 	TIMESTAMP ts_initial, ts_offset; | ||||
|  | ||||
| 	double rxGain; | ||||
|  | ||||
| 	int get_ant_idx(const std::string & name, bool dir_tx, size_t chan); | ||||
| 	bool flush_recv(size_t num_pkts); | ||||
| 	void update_stream_stats(size_t chan, bool * underrun, bool * overrun); | ||||
|  | ||||
| public: | ||||
|  | ||||
| 	/** Object constructor */ | ||||
| 	LMSDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset, | ||||
| 		  const std::vector<std::string>& tx_paths, | ||||
| 		  const std::vector<std::string>& rx_paths); | ||||
| 	~LMSDevice(); | ||||
|  | ||||
| 	/** Instantiate the LMS */ | ||||
| 	int open(const std::string &args, int ref, bool swap_channels); | ||||
|  | ||||
| 	/** Start the LMS */ | ||||
| 	bool start(); | ||||
|  | ||||
| 	/** Stop the LMS */ | ||||
| 	bool stop(); | ||||
|  | ||||
| 	/** Set priority not supported */ | ||||
| 	void setPriority(float prio = 0.5) { | ||||
| 	} | ||||
|  | ||||
| 	enum TxWindowType getWindowType() { | ||||
| 		return TX_WINDOW_LMS1; | ||||
| 	} | ||||
|  | ||||
| 	/** | ||||
| 	Read samples from the LMS. | ||||
| 	@param buf preallocated buf to contain read result | ||||
| 	@param len number of samples desired | ||||
| 	@param overrun Set if read buffer has been overrun, e.g. data not being read fast enough | ||||
| 	@param timestamp The timestamp of the first samples to be read | ||||
| 	@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough | ||||
| 	@param RSSI The received signal strength of the read result | ||||
| 	@return The number of samples actually read | ||||
| 	*/ | ||||
| 	int readSamples(std::vector < short *>&buf, int len, bool * overrun, | ||||
| 			TIMESTAMP timestamp = 0xffffffff, bool * underrun = | ||||
| 			NULL, unsigned *RSSI = NULL); | ||||
| 	/** | ||||
| 	Write samples to the LMS. | ||||
| 	@param buf Contains the data to be written. | ||||
| 	@param len number of samples to write. | ||||
| 	@param underrun Set if LMS does not have data to transmit, e.g. data not being sent fast enough | ||||
| 	@param timestamp The timestamp of the first sample of the data buffer. | ||||
| 	@param isControl Set if data is a control packet, e.g. a ping command | ||||
| 	@return The number of samples actually written | ||||
| 	*/ | ||||
| 	int writeSamples(std::vector < short *>&bufs, int len, bool * underrun, | ||||
| 			 TIMESTAMP timestamp = 0xffffffff, bool isControl = | ||||
| 			 false); | ||||
|  | ||||
| 	/** Update the alignment between the read and write timestamps */ | ||||
| 	bool updateAlignment(TIMESTAMP timestamp); | ||||
|  | ||||
| 	/** Set the transmitter frequency */ | ||||
| 	bool setTxFreq(double wFreq, size_t chan = 0); | ||||
|  | ||||
| 	/** Set the receiver frequency */ | ||||
| 	bool setRxFreq(double wFreq, size_t chan = 0); | ||||
|  | ||||
| 	/** Returns the starting write Timestamp*/ | ||||
| 	TIMESTAMP initialWriteTimestamp(void) { | ||||
| 		return ts_initial; | ||||
| 	} | ||||
|  | ||||
| 	/** Returns the starting read Timestamp*/ | ||||
| 	TIMESTAMP initialReadTimestamp(void) { | ||||
| 		return ts_initial; | ||||
| 	} | ||||
|  | ||||
| 	/** returns the full-scale transmit amplitude **/ | ||||
| 	double fullScaleInputValue() { | ||||
| 		return(double) SHRT_MAX * LIMESDR_TX_AMPL; | ||||
| 	} | ||||
|  | ||||
| 	/** returns the full-scale receive amplitude **/ | ||||
| 	double fullScaleOutputValue() { | ||||
| 		return (double) SHRT_MAX; | ||||
| 	} | ||||
|  | ||||
| 	/** sets the receive chan gain, returns the gain setting **/ | ||||
| 	double setRxGain(double dB, size_t chan = 0); | ||||
|  | ||||
| 	/** get the current receive gain */ | ||||
| 	double getRxGain(size_t chan = 0) { | ||||
| 		return rxGain; | ||||
| 	} | ||||
|  | ||||
| 	/** return maximum Rx Gain **/ | ||||
| 	double maxRxGain(void); | ||||
|  | ||||
| 	/** return minimum Rx Gain **/ | ||||
| 	double minRxGain(void); | ||||
|  | ||||
| 	/** sets the transmit chan gain, returns the gain setting **/ | ||||
| 	double setTxGain(double dB, size_t chan = 0); | ||||
|  | ||||
| 	/** return maximum Tx Gain **/ | ||||
| 	double maxTxGain(void); | ||||
|  | ||||
| 	/** return minimum Rx Gain **/ | ||||
| 	double minTxGain(void); | ||||
|  | ||||
| 	/** sets the RX path to use, returns true if successful and false otherwise */ | ||||
| 	bool setRxAntenna(const std::string & ant, size_t chan = 0); | ||||
|  | ||||
| 	/* return the used RX path */ | ||||
| 	std::string getRxAntenna(size_t chan = 0); | ||||
|  | ||||
| 	/** sets the RX path to use, returns true if successful and false otherwise */ | ||||
| 	bool setTxAntenna(const std::string & ant, size_t chan = 0); | ||||
|  | ||||
| 	/* return the used RX path */ | ||||
| 	std::string getTxAntenna(size_t chan = 0); | ||||
|  | ||||
| 	/** return whether user drives synchronization of Tx/Rx of USRP */ | ||||
|         bool requiresRadioAlign(); | ||||
|  | ||||
|         /** return whether user drives synchronization of Tx/Rx of USRP */ | ||||
|         virtual GSM::Time minLatency(); | ||||
|  | ||||
| 	/** Return internal status values */ | ||||
| 	inline double getTxFreq(size_t chan = 0) { | ||||
| 		return 0; | ||||
| 	} | ||||
| 	inline double getRxFreq(size_t chan = 0) { | ||||
| 		return 0; | ||||
| 	} | ||||
| 	inline double getSampleRate() { | ||||
| 		return actualSampleRate; | ||||
| 	} | ||||
| 	inline double numberRead() { | ||||
| 		return samplesRead; | ||||
| 	} | ||||
| 	inline double numberWritten() { | ||||
| 		return samplesWritten; | ||||
| 	} | ||||
| }; | ||||
|  | ||||
| #endif // _LMS_DEVICE_H_ | ||||
							
								
								
									
										10
									
								
								Transceiver52M/device/lms/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								Transceiver52M/device/lms/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/.. | ||||
| AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(LMS_CFLAGS) | ||||
|  | ||||
| noinst_HEADERS = LMSDevice.h | ||||
|  | ||||
| noinst_LTLIBRARIES = libdevice.la | ||||
|  | ||||
| libdevice_la_SOURCES = LMSDevice.cpp | ||||
| @@ -18,6 +18,13 @@ | ||||
| #include <string> | ||||
| #include <vector> | ||||
| 
 | ||||
| #include "GSMCommon.h" | ||||
| #include "Logger.h" | ||||
| 
 | ||||
| extern "C" { | ||||
| #include "config_defs.h" | ||||
| } | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| #endif | ||||
| @@ -33,7 +40,7 @@ class RadioDevice { | ||||
| 
 | ||||
|   public: | ||||
|   /* Available transport bus types */ | ||||
|   enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED }; | ||||
|   enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED, TX_WINDOW_LMS1 }; | ||||
| 
 | ||||
|   /* Radio interface types */ | ||||
|   enum InterfaceType { | ||||
| @@ -43,12 +50,6 @@ class RadioDevice { | ||||
|     MULTI_ARFCN, | ||||
|   }; | ||||
| 
 | ||||
|   enum ReferenceType { | ||||
|     REF_INTERNAL, | ||||
|     REF_EXTERNAL, | ||||
|     REF_GPS, | ||||
|   }; | ||||
| 
 | ||||
|   static RadioDevice *make(size_t tx_sps, size_t rx_sps, InterfaceType type, | ||||
|                            size_t chans = 1, double offset = 0.0, | ||||
|                            const std::vector<std::string>& tx_paths = std::vector<std::string>(1, ""), | ||||
| @@ -150,6 +151,12 @@ class RadioDevice { | ||||
|   /** return the used RX path */ | ||||
|   virtual std::string getTxAntenna(size_t chan = 0) = 0; | ||||
| 
 | ||||
|   /** return whether user drives synchronization of Tx/Rx of USRP */ | ||||
|   virtual bool requiresRadioAlign() = 0; | ||||
| 
 | ||||
|   /** Minimum latency that the device can achieve */ | ||||
|   virtual GSM::Time minLatency() = 0; | ||||
| 
 | ||||
|   /** Return internal status values */ | ||||
|   virtual double getTxFreq(size_t chan = 0) = 0; | ||||
|   virtual double getRxFreq(size_t chan = 0) = 0; | ||||
| @@ -157,6 +164,47 @@ class RadioDevice { | ||||
|   virtual double numberRead()=0; | ||||
|   virtual double numberWritten()=0; | ||||
| 
 | ||||
|   protected: | ||||
|   size_t tx_sps, rx_sps; | ||||
|   InterfaceType iface; | ||||
|   size_t chans; | ||||
|   double lo_offset; | ||||
|   std::vector<std::string> tx_paths, rx_paths; | ||||
| 
 | ||||
|   RadioDevice(size_t tx_sps, size_t rx_sps, InterfaceType type, size_t chans, double offset, | ||||
|               const std::vector<std::string>& tx_paths, | ||||
|               const std::vector<std::string>& rx_paths): | ||||
| 		tx_sps(tx_sps), rx_sps(rx_sps), iface(type), chans(chans), lo_offset(offset), | ||||
| 		tx_paths(tx_paths), rx_paths(rx_paths) | ||||
| 	{ } | ||||
| 
 | ||||
|   bool set_antennas() { | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	for (i = 0; i < tx_paths.size(); i++) { | ||||
| 		if (tx_paths[i] == "") | ||||
| 			continue; | ||||
| 		LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i]; | ||||
| 		if (!setTxAntenna(tx_paths[i], i)) { | ||||
| 			LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i]; | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < rx_paths.size(); i++) { | ||||
| 		if (rx_paths[i] == "") | ||||
| 			continue; | ||||
| 		LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i]; | ||||
| 		if (!setRxAntenna(rx_paths[i], i)) { | ||||
| 			LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i]; | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 	LOG(INFO) << "Antennas configured successfully"; | ||||
| 	return true; | ||||
|   } | ||||
| 
 | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
							
								
								
									
										8
									
								
								Transceiver52M/device/uhd/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								Transceiver52M/device/uhd/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/.. | ||||
| AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(UHD_CFLAGS) | ||||
|  | ||||
| noinst_LTLIBRARIES = libdevice.la | ||||
|  | ||||
| libdevice_la_SOURCES = UHDDevice.cpp | ||||
| @@ -28,7 +28,6 @@ | ||||
| #include <uhd/version.hpp> | ||||
| #include <uhd/property_tree.hpp> | ||||
| #include <uhd/usrp/multi_usrp.hpp> | ||||
| #include <uhd/utils/thread_priority.hpp> | ||||
| 
 | ||||
| #ifdef HAVE_CONFIG_H | ||||
| #include "config.h" | ||||
| @@ -36,6 +35,9 @@ | ||||
| 
 | ||||
| #ifndef USE_UHD_3_11 | ||||
| #include <uhd/utils/msg.hpp> | ||||
| #include <uhd/utils/thread_priority.hpp> | ||||
| #else | ||||
| #include <uhd/utils/thread.hpp> | ||||
| #endif | ||||
| 
 | ||||
| #define USRP_TX_AMPL     0.3 | ||||
| @@ -255,6 +257,10 @@ public: | ||||
| 	bool setTxAntenna(const std::string &ant, size_t chan); | ||||
| 	std::string getTxAntenna(size_t chan); | ||||
| 
 | ||||
| 	bool requiresRadioAlign(); | ||||
| 
 | ||||
| 	GSM::Time minLatency(); | ||||
| 
 | ||||
| 	inline double getSampleRate() { return tx_rate; } | ||||
| 	inline double numberRead() { return rx_pkt_cnt; } | ||||
| 	inline double numberWritten() { return 0; } | ||||
| @@ -278,16 +284,13 @@ private: | ||||
| 	enum TxWindowType tx_window; | ||||
| 	enum uhd_dev_type dev_type; | ||||
| 
 | ||||
| 	size_t tx_sps, rx_sps, chans; | ||||
| 	double tx_rate, rx_rate; | ||||
| 
 | ||||
| 	double tx_gain_min, tx_gain_max; | ||||
| 	double rx_gain_min, rx_gain_max; | ||||
| 	double offset; | ||||
| 
 | ||||
| 	std::vector<double> tx_gains, rx_gains; | ||||
| 	std::vector<double> tx_freqs, rx_freqs; | ||||
| 	std::vector<std::string> tx_paths, rx_paths; | ||||
| 	size_t tx_spp, rx_spp; | ||||
| 
 | ||||
| 	bool started; | ||||
| @@ -303,7 +306,6 @@ private: | ||||
| 	void init_gains(); | ||||
| 	void set_channels(bool swap); | ||||
| 	void set_rates(); | ||||
| 	bool set_antennas(); | ||||
| 	bool parse_dev_type(); | ||||
| 	bool flush_recv(size_t num_pkts); | ||||
| 	int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls); | ||||
| @@ -315,12 +317,12 @@ private: | ||||
| 	bool set_freq(double freq, size_t chan, bool tx); | ||||
| 
 | ||||
| 	Thread *async_event_thrd; | ||||
| 	InterfaceType iface; | ||||
| 	Mutex tune_lock; | ||||
| }; | ||||
| 
 | ||||
| void *async_event_loop(uhd_device *dev) | ||||
| { | ||||
| 	set_selfthread_name("UHDAsyncEvent"); | ||||
| 	dev->setPriority(0.43); | ||||
| 
 | ||||
| 	while (1) { | ||||
| @@ -341,13 +343,13 @@ void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg) | ||||
| { | ||||
| 	switch (type) { | ||||
| 	case uhd::msg::status: | ||||
| 		LOG(INFO) << msg; | ||||
| 		LOGC(DDEV, INFO) << msg; | ||||
| 		break; | ||||
| 	case uhd::msg::warning: | ||||
| 		LOG(WARNING) << msg; | ||||
| 		LOGC(DDEV, WARNING) << msg; | ||||
| 		break; | ||||
| 	case uhd::msg::error: | ||||
| 		LOG(ERR) << msg; | ||||
| 		LOGC(DDEV, ERR) << msg; | ||||
| 		break; | ||||
| 	case uhd::msg::fastpath: | ||||
| 		break; | ||||
| @@ -362,22 +364,16 @@ static void thread_enable_cancel(bool cancel) | ||||
| } | ||||
| 
 | ||||
| uhd_device::uhd_device(size_t tx_sps, size_t rx_sps, | ||||
| 		       InterfaceType iface, size_t chans, double offset, | ||||
| 		       InterfaceType iface, size_t chans, double lo_offset, | ||||
| 		       const std::vector<std::string>& tx_paths, | ||||
| 		       const std::vector<std::string>& rx_paths) | ||||
| 	: tx_gain_min(0.0), tx_gain_max(0.0), | ||||
| 	: RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths), | ||||
| 	  tx_gain_min(0.0), tx_gain_max(0.0), | ||||
| 	  rx_gain_min(0.0), rx_gain_max(0.0), | ||||
| 	  tx_spp(0), rx_spp(0), | ||||
| 	  started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0), | ||||
| 	  prev_ts(0,0), ts_initial(0), ts_offset(0), async_event_thrd(NULL) | ||||
| { | ||||
| 	this->tx_sps = tx_sps; | ||||
| 	this->rx_sps = rx_sps; | ||||
| 	this->chans = chans; | ||||
| 	this->offset = offset; | ||||
| 	this->iface = iface; | ||||
| 	this->tx_paths = tx_paths; | ||||
| 	this->rx_paths = rx_paths; | ||||
| } | ||||
| 
 | ||||
| uhd_device::~uhd_device() | ||||
| @@ -395,7 +391,7 @@ void uhd_device::init_gains() | ||||
| 	if (dev_type == UMTRX) { | ||||
| 		std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0); | ||||
| 		if (gain_stages[0] == "VGA") { | ||||
| 			LOG(WARNING) << "Update your UHD version for a proper Tx gain support"; | ||||
| 			LOGC(DDEV, WARNING) << "Update your UHD version for a proper Tx gain support"; | ||||
| 		} | ||||
| 		if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") { | ||||
| 			range = usrp_dev->get_tx_gain_range(); | ||||
| @@ -411,23 +407,23 @@ void uhd_device::init_gains() | ||||
| 		tx_gain_min = range.start(); | ||||
| 		tx_gain_max = range.stop(); | ||||
| 	} | ||||
| 	LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]"; | ||||
| 	LOGC(DDEV, INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]"; | ||||
| 
 | ||||
| 	range = usrp_dev->get_rx_gain_range(); | ||||
| 	rx_gain_min = range.start(); | ||||
| 	rx_gain_max = range.stop(); | ||||
| 	LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]"; | ||||
| 	LOGC(DDEV, INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]"; | ||||
| 
 | ||||
| 	for (size_t i = 0; i < tx_gains.size(); i++) { | ||||
| 		double gain = (tx_gain_min + tx_gain_max) / 2; | ||||
| 		LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain; | ||||
| 		LOGC(DDEV, INFO) << "Default setting Tx gain for channel " << i << " to " << gain; | ||||
| 		usrp_dev->set_tx_gain(gain, i); | ||||
| 		tx_gains[i] = usrp_dev->get_tx_gain(i); | ||||
| 	} | ||||
| 
 | ||||
| 	for (size_t i = 0; i < rx_gains.size(); i++) { | ||||
| 		double gain = (rx_gain_min + rx_gain_max) / 2; | ||||
| 		LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain; | ||||
| 		LOGC(DDEV, INFO) << "Default setting Rx gain for channel " << i << " to " << gain; | ||||
| 		usrp_dev->set_rx_gain(gain, i); | ||||
| 		rx_gains[i] = usrp_dev->get_rx_gain(i); | ||||
| 	} | ||||
| @@ -451,34 +447,7 @@ void uhd_device::set_rates() | ||||
| 	rx_rate = usrp_dev->get_rx_rate(); | ||||
| 
 | ||||
| 	ts_offset = static_cast<TIMESTAMP>(desc.offset * rx_rate); | ||||
| 	LOG(INFO) << "Rates configured for " << desc.str; | ||||
| } | ||||
| 
 | ||||
| bool uhd_device::set_antennas() | ||||
| { | ||||
| 	unsigned int i; | ||||
| 
 | ||||
| 	for (i = 0; i < tx_paths.size(); i++) { | ||||
| 		if (tx_paths[i] == "") | ||||
| 			continue; | ||||
| 		LOG(DEBUG) << "Configuring channel " << i << " with antenna " << tx_paths[i]; | ||||
| 		if (!setTxAntenna(tx_paths[i], i)) { | ||||
| 			LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << tx_paths[i]; | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| 	for (i = 0; i < rx_paths.size(); i++) { | ||||
| 		if (rx_paths[i] == "") | ||||
| 			continue; | ||||
| 		LOG(DEBUG) << "Configuring channel " << i << " with antenna " << rx_paths[i]; | ||||
| 		if (!setRxAntenna(rx_paths[i], i)) { | ||||
| 			LOG(ALERT) << "Failed configuring channel " << i << " with antenna " << rx_paths[i]; | ||||
| 			return false; | ||||
| 		} | ||||
| 	} | ||||
| 	LOG(INFO) << "Antennas configured successfully"; | ||||
| 	return true; | ||||
| 	LOGC(DDEV, INFO) << "Rates configured for " << desc.str; | ||||
| } | ||||
| 
 | ||||
| double uhd_device::setTxGain(double db, size_t chan) | ||||
| @@ -487,7 +456,7 @@ double uhd_device::setTxGain(double db, size_t chan) | ||||
| 		chan = 0; | ||||
| 
 | ||||
| 	if (chan >= tx_gains.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel" << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel" << chan; | ||||
| 		return 0.0f; | ||||
| 	} | ||||
| 
 | ||||
| @@ -510,22 +479,25 @@ double uhd_device::setTxGain(double db, size_t chan) | ||||
| 
 | ||||
| 	tx_gains[chan] = usrp_dev->get_tx_gain(chan); | ||||
| 
 | ||||
| 	LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)"; | ||||
| 	LOGC(DDEV, INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)"; | ||||
| 
 | ||||
| 	return tx_gains[chan]; | ||||
| } | ||||
| 
 | ||||
| double uhd_device::setRxGain(double db, size_t chan) | ||||
| { | ||||
| 	if (iface == MULTI_ARFCN) | ||||
| 		chan = 0; | ||||
| 
 | ||||
| 	if (chan >= rx_gains.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return 0.0f; | ||||
| 	} | ||||
| 
 | ||||
| 	usrp_dev->set_rx_gain(db, chan); | ||||
| 	rx_gains[chan] = usrp_dev->get_rx_gain(chan); | ||||
| 
 | ||||
| 	LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)"; | ||||
| 	LOGC(DDEV, INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)"; | ||||
| 
 | ||||
| 	return rx_gains[chan]; | ||||
| } | ||||
| @@ -536,7 +508,7 @@ double uhd_device::getRxGain(size_t chan) | ||||
| 		chan = 0; | ||||
| 
 | ||||
| 	if (chan >= rx_gains.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return 0.0f; | ||||
| 	} | ||||
| 
 | ||||
| @@ -584,7 +556,7 @@ bool uhd_device::parse_dev_type() | ||||
| 		mapIter++; | ||||
| 	} | ||||
| 
 | ||||
| 	LOG(ALERT) << "Unsupported device " << devString; | ||||
| 	LOGC(DDEV, ALERT) << "Unsupported device " << devString; | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| @@ -655,16 +627,16 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels) | ||||
| 	uhd::device_addr_t addr(args); | ||||
| 	uhd::device_addrs_t dev_addrs = uhd::device::find(addr); | ||||
| 	if (dev_addrs.size() == 0) { | ||||
| 		LOG(ALERT) << "No UHD devices found with address '" << args << "'"; | ||||
| 		LOGC(DDEV, ALERT) << "No UHD devices found with address '" << args << "'"; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	// Use the first found device
 | ||||
| 	LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string(); | ||||
| 	LOGC(DDEV, INFO) << "Using discovered UHD device " << dev_addrs[0].to_string(); | ||||
| 	try { | ||||
| 		usrp_dev = uhd::usrp::multi_usrp::make(addr); | ||||
| 	} catch(...) { | ||||
| 		LOG(ALERT) << "UHD make failed, device " << args; | ||||
| 	} catch(uhd::key_error::exception &e) { | ||||
| 		LOGC(DDEV, ALERT) << "UHD make failed, device " << args << ", exception:\n" << e.what(); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| @@ -673,19 +645,19 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels) | ||||
| 		return -1; | ||||
| 
 | ||||
| 	if ((dev_type == E3XX) && !uhd_e3xx_version_chk()) { | ||||
| 		LOG(ALERT) << "E3XX requires UHD 003.009.000 or greater"; | ||||
| 		LOGC(DDEV, ALERT) << "E3XX requires UHD 003.009.000 or greater"; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	try { | ||||
| 		set_channels(swap_channels); | ||||
|         } catch (const std::exception &e) { | ||||
| 		LOG(ALERT) << "Channel setting failed - " << e.what(); | ||||
| 		LOGC(DDEV, ALERT) << "Channel setting failed - " << e.what(); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| 	if (!set_antennas()) { | ||||
| 		LOG(ALERT) << "UHD antenna setting failed"; | ||||
| 		LOGC(DDEV, ALERT) << "UHD antenna setting failed"; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| @@ -706,7 +678,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels) | ||||
| 		refstr = "gpsdo"; | ||||
| 		break; | ||||
| 	default: | ||||
| 		LOG(ALERT) << "Invalid reference type"; | ||||
| 		LOGC(DDEV, ALERT) << "Invalid reference type"; | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| @@ -715,7 +687,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels) | ||||
| 	try { | ||||
| 		set_rates(); | ||||
|         } catch (const std::exception &e) { | ||||
| 		LOG(ALERT) << "UHD rate setting failed - " << e.what(); | ||||
| 		LOGC(DDEV, ALERT) << "UHD rate setting failed - " << e.what(); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| @@ -728,8 +700,8 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels) | ||||
| 		} | ||||
| 	} else if (dev_type == LIMESDR) { | ||||
| 		for (size_t i = 0; i < chans; i++) { | ||||
| 			usrp_dev->set_tx_bandwidth(5e6, i); | ||||
| 			usrp_dev->set_rx_bandwidth(5e6, i); | ||||
| 			usrp_dev->set_tx_bandwidth(5.2e6, i); | ||||
| 			usrp_dev->set_rx_bandwidth(1.4001e6, i); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| @@ -754,7 +726,7 @@ int uhd_device::open(const std::string &args, int ref, bool swap_channels) | ||||
| 	init_gains(); | ||||
| 
 | ||||
| 	// Print configuration
 | ||||
| 	LOG(INFO) << "\n" << usrp_dev->get_pp_string(); | ||||
| 	LOGC(DDEV, INFO) << "\n" << usrp_dev->get_pp_string(); | ||||
| 
 | ||||
| 	if (iface == MULTI_ARFCN) | ||||
| 		return MULTI_ARFCN; | ||||
| @@ -797,7 +769,7 @@ bool uhd_device::flush_recv(size_t num_pkts) | ||||
| 		if (!num_smpls) { | ||||
| 			switch (md.error_code) { | ||||
| 			case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: | ||||
| 				LOG(ALERT) << "Device timed out"; | ||||
| 				LOGC(DDEV, ALERT) << "Device timed out"; | ||||
| 				return false; | ||||
| 			default: | ||||
| 				continue; | ||||
| @@ -807,7 +779,7 @@ bool uhd_device::flush_recv(size_t num_pkts) | ||||
| 		ts_initial = md.time_spec.to_ticks(rx_rate); | ||||
| 	} | ||||
| 
 | ||||
| 	LOG(INFO) << "Initial timestamp " << ts_initial << std::endl; | ||||
| 	LOGC(DDEV, INFO) << "Initial timestamp " << ts_initial << std::endl; | ||||
| 
 | ||||
| 	return true; | ||||
| } | ||||
| @@ -832,10 +804,10 @@ bool uhd_device::restart() | ||||
| 
 | ||||
| bool uhd_device::start() | ||||
| { | ||||
| 	LOG(INFO) << "Starting USRP..."; | ||||
| 	LOGC(DDEV, INFO) << "Starting USRP..."; | ||||
| 
 | ||||
| 	if (started) { | ||||
| 		LOG(ERR) << "Device already started"; | ||||
| 		LOGC(DDEV, ERR) << "Device already started"; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| @@ -853,7 +825,7 @@ bool uhd_device::start() | ||||
| 
 | ||||
| 	// Display usrp time
 | ||||
| 	double time_now = usrp_dev->get_time_now().get_real_secs(); | ||||
| 	LOG(INFO) << "The current time is " << time_now << " seconds"; | ||||
| 	LOGC(DDEV, INFO) << "The current time is " << time_now << " seconds"; | ||||
| 
 | ||||
| 	started = true; | ||||
| 	return true; | ||||
| @@ -886,11 +858,11 @@ void uhd_device::setPriority(float prio) | ||||
| int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls) | ||||
| { | ||||
| 	if (!num_smpls) { | ||||
| 		LOG(ERR) << str_code(md); | ||||
| 		LOGC(DDEV, ERR) << str_code(md); | ||||
| 
 | ||||
| 		switch (md.error_code) { | ||||
| 		case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT: | ||||
| 			LOG(ALERT) << "UHD: Receive timed out"; | ||||
| 			LOGC(DDEV, ALERT) << "UHD: Receive timed out"; | ||||
| 			return ERROR_TIMEOUT; | ||||
| 		case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW: | ||||
| 		case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND: | ||||
| @@ -903,14 +875,14 @@ int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls) | ||||
| 
 | ||||
| 	// Missing timestamp
 | ||||
| 	if (!md.has_time_spec) { | ||||
| 		LOG(ALERT) << "UHD: Received packet missing timestamp"; | ||||
| 		LOGC(DDEV, ALERT) << "UHD: Received packet missing timestamp"; | ||||
| 		return ERROR_UNRECOVERABLE; | ||||
| 	} | ||||
| 
 | ||||
| 	// Monotonicity check
 | ||||
| 	if (md.time_spec < prev_ts) { | ||||
| 		LOG(ALERT) << "UHD: Loss of monotonic time"; | ||||
| 		LOG(ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", " | ||||
| 		LOGC(DDEV, ALERT) << "UHD: Loss of monotonic time"; | ||||
| 		LOGC(DDEV, ALERT) << "Current time: " << md.time_spec.get_real_secs() << ", " | ||||
| 			   << "Previous time: " << prev_ts.get_real_secs(); | ||||
| 		return ERROR_TIMING; | ||||
| 	} | ||||
| @@ -933,7 +905,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
| 	uhd::rx_metadata_t metadata; | ||||
| 
 | ||||
| 	if (bufs.size() != chans) { | ||||
| 		LOG(ALERT) << "Invalid channel combination " << bufs.size(); | ||||
| 		LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size(); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| @@ -944,13 +916,13 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
| 	timestamp += ts_offset; | ||||
| 
 | ||||
| 	ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate); | ||||
| 	LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs(); | ||||
| 	LOGC(DDEV, DEBUG) << "Requested timestamp = " << ts.get_real_secs(); | ||||
| 
 | ||||
| 	// Check that timestamp is valid
 | ||||
| 	rc = rx_buffers[0]->avail_smpls(timestamp); | ||||
| 	if (rc < 0) { | ||||
| 		LOG(ERR) << rx_buffers[0]->str_code(rc); | ||||
| 		LOG(ERR) << rx_buffers[0]->str_status(timestamp); | ||||
| 		LOGC(DDEV, ERR) << rx_buffers[0]->str_code(rc); | ||||
| 		LOGC(DDEV, ERR) << rx_buffers[0]->str_status(timestamp); | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| @@ -975,8 +947,8 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
| 		rc = check_rx_md_err(metadata, num_smpls); | ||||
| 		switch (rc) { | ||||
| 		case ERROR_UNRECOVERABLE: | ||||
| 			LOG(ALERT) << "UHD: Version " << uhd::get_version_string(); | ||||
| 			LOG(ALERT) << "UHD: Unrecoverable error, exiting..."; | ||||
| 			LOGC(DDEV, ALERT) << "UHD: Version " << uhd::get_version_string(); | ||||
| 			LOGC(DDEV, ALERT) << "UHD: Unrecoverable error, exiting..."; | ||||
| 			exit(-1); | ||||
| 		case ERROR_TIMEOUT: | ||||
| 			// Assume stopping condition
 | ||||
| @@ -988,7 +960,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
| 		} | ||||
| 
 | ||||
| 		ts = metadata.time_spec; | ||||
| 		LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs(); | ||||
| 		LOGC(DDEV, DEBUG) << "Received timestamp = " << ts.get_real_secs(); | ||||
| 
 | ||||
| 		for (size_t i = 0; i < rx_buffers.size(); i++) { | ||||
| 			rc = rx_buffers[i]->write((short *) &pkt_bufs[i].front(), | ||||
| @@ -997,8 +969,8 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
| 
 | ||||
| 			// Continue on local overrun, exit on other errors
 | ||||
| 			if ((rc < 0)) { | ||||
| 				LOG(ERR) << rx_buffers[i]->str_code(rc); | ||||
| 				LOG(ERR) << rx_buffers[i]->str_status(timestamp); | ||||
| 				LOGC(DDEV, ERR) << rx_buffers[i]->str_code(rc); | ||||
| 				LOGC(DDEV, ERR) << rx_buffers[i]->str_status(timestamp); | ||||
| 				if (rc != smpl_buf::ERROR_OVERFLOW) | ||||
| 					return 0; | ||||
| 			} | ||||
| @@ -1009,8 +981,8 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
| 	for (size_t i = 0; i < rx_buffers.size(); i++) { | ||||
| 		rc = rx_buffers[i]->read(bufs[i], len, timestamp); | ||||
| 		if ((rc < 0) || (rc != len)) { | ||||
| 			LOG(ERR) << rx_buffers[i]->str_code(rc); | ||||
| 			LOG(ERR) << rx_buffers[i]->str_status(timestamp); | ||||
| 			LOGC(DDEV, ERR) << rx_buffers[i]->str_code(rc); | ||||
| 			LOGC(DDEV, ERR) << rx_buffers[i]->str_status(timestamp); | ||||
| 			return 0; | ||||
| 		} | ||||
| 	} | ||||
| @@ -1031,12 +1003,12 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun | ||||
| 
 | ||||
| 	// No control packets
 | ||||
| 	if (isControl) { | ||||
| 		LOG(ERR) << "Control packets not supported"; | ||||
| 		LOGC(DDEV, ERR) << "Control packets not supported"; | ||||
| 		return 0; | ||||
| 	} | ||||
| 
 | ||||
| 	if (bufs.size() != chans) { | ||||
| 		LOG(ALERT) << "Invalid channel combination " << bufs.size(); | ||||
| 		LOGC(DDEV, ALERT) << "Invalid channel combination " << bufs.size(); | ||||
| 		return -1; | ||||
| 	} | ||||
| 
 | ||||
| @@ -1045,14 +1017,14 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun | ||||
| 		drop_cnt++; | ||||
| 
 | ||||
| 		if (drop_cnt == 1) { | ||||
| 			LOG(DEBUG) << "Aligning transmitter: stop burst"; | ||||
| 			LOGC(DDEV, DEBUG) << "Aligning transmitter: stop burst"; | ||||
| 			*underrun = true; | ||||
| 			metadata.end_of_burst = true; | ||||
| 		} else if (drop_cnt < 30) { | ||||
| 			LOG(DEBUG) << "Aligning transmitter: packet advance"; | ||||
| 			LOGC(DDEV, DEBUG) << "Aligning transmitter: packet advance"; | ||||
| 			return len; | ||||
| 		} else { | ||||
| 			LOG(DEBUG) << "Aligning transmitter: start burst"; | ||||
| 			LOGC(DDEV, DEBUG) << "Aligning transmitter: start burst"; | ||||
| 			metadata.start_of_burst = true; | ||||
| 			aligned = true; | ||||
| 			drop_cnt = 0; | ||||
| @@ -1064,7 +1036,7 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun | ||||
| 	thread_enable_cancel(true); | ||||
| 
 | ||||
| 	if (num_smpls != (unsigned) len) { | ||||
| 		LOG(ALERT) << "UHD: Device send timed out"; | ||||
| 		LOGC(DDEV, ALERT) << "UHD: Device send timed out"; | ||||
| 	} | ||||
| 
 | ||||
| 	return num_smpls; | ||||
| @@ -1082,8 +1054,8 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx) | ||||
| 	uhd::tune_request_t treq(freq); | ||||
| 
 | ||||
| 	if (dev_type == UMTRX) { | ||||
| 		if (offset != 0.0) | ||||
| 			return uhd::tune_request_t(freq, offset); | ||||
| 		if (lo_offset != 0.0) | ||||
| 			return uhd::tune_request_t(freq, lo_offset); | ||||
| 
 | ||||
| 		// Don't use DSP tuning, because LMS6002D PLL steps are small enough.
 | ||||
| 		// We end up with DSP tuning just for 2-3Hz, which is meaningless and
 | ||||
| @@ -1095,12 +1067,12 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx) | ||||
| 		treq.dsp_freq = 0.0; | ||||
| 		return treq; | ||||
| 	} else if (chans == 1) { | ||||
| 		if (offset == 0.0) | ||||
| 		if (lo_offset == 0.0) | ||||
| 			return treq; | ||||
| 
 | ||||
| 		return uhd::tune_request_t(freq, offset); | ||||
| 		return uhd::tune_request_t(freq, lo_offset); | ||||
| 	} else if ((dev_type != B210) || (chans > 2) || (chan > 1)) { | ||||
| 		LOG(ALERT) << chans << " channels unsupported"; | ||||
| 		LOGC(DDEV, ALERT) << chans << " channels unsupported"; | ||||
| 		return treq; | ||||
| 	} | ||||
| 
 | ||||
| @@ -1116,7 +1088,7 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx) | ||||
| 	/* Find center frequency between channels */ | ||||
| 	rf_spread = fabs(freqs[!chan] - freq); | ||||
| 	if (rf_spread > dev_param_map.at(dev_key(B210, tx_sps, rx_sps)).mcr) { | ||||
| 		LOG(ALERT) << rf_spread << "Hz tuning spread not supported\n"; | ||||
| 		LOGC(DDEV, ALERT) << rf_spread << "Hz tuning spread not supported\n"; | ||||
| 		return treq; | ||||
| 	} | ||||
| 
 | ||||
| @@ -1142,7 +1114,7 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx) | ||||
| 		tres = usrp_dev->set_rx_freq(treq, chan); | ||||
| 		rx_freqs[chan] = usrp_dev->get_rx_freq(chan); | ||||
| 	} | ||||
| 	LOG(INFO) << "\n" << tres.to_pp_string() << std::endl; | ||||
| 	LOGC(DDEV, INFO) << "\n" << tres.to_pp_string() << std::endl; | ||||
| 
 | ||||
| 	if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) | ||||
| 		return true; | ||||
| @@ -1162,7 +1134,7 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx) | ||||
| 			rx_freqs[!chan] = usrp_dev->get_rx_freq(!chan); | ||||
| 
 | ||||
| 		} | ||||
| 		LOG(INFO) << "\n" << tres.to_pp_string() << std::endl; | ||||
| 		LOGC(DDEV, INFO) << "\n" << tres.to_pp_string() << std::endl; | ||||
| 	} | ||||
| 
 | ||||
| 	return true; | ||||
| @@ -1171,7 +1143,7 @@ bool uhd_device::set_freq(double freq, size_t chan, bool tx) | ||||
| bool uhd_device::setTxFreq(double wFreq, size_t chan) | ||||
| { | ||||
| 	if (chan >= tx_freqs.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
| 	ScopedLock lock(tune_lock); | ||||
| @@ -1182,7 +1154,7 @@ bool uhd_device::setTxFreq(double wFreq, size_t chan) | ||||
| bool uhd_device::setRxFreq(double wFreq, size_t chan) | ||||
| { | ||||
| 	if (chan >= rx_freqs.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
| 	ScopedLock lock(tune_lock); | ||||
| @@ -1193,7 +1165,7 @@ bool uhd_device::setRxFreq(double wFreq, size_t chan) | ||||
| double uhd_device::getTxFreq(size_t chan) | ||||
| { | ||||
| 	if (chan >= tx_freqs.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return 0.0; | ||||
| 	} | ||||
| 
 | ||||
| @@ -1203,7 +1175,7 @@ double uhd_device::getTxFreq(size_t chan) | ||||
| double uhd_device::getRxFreq(size_t chan) | ||||
| { | ||||
| 	if (chan >= rx_freqs.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return 0.0; | ||||
| 	} | ||||
| 
 | ||||
| @@ -1214,23 +1186,23 @@ bool uhd_device::setRxAntenna(const std::string &ant, size_t chan) | ||||
| { | ||||
| 	std::vector<std::string> avail; | ||||
| 	if (chan >= rx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	avail = usrp_dev->get_rx_antennas(chan); | ||||
| 	if (std::find(avail.begin(), avail.end(), ant) == avail.end()) { | ||||
| 		LOG(ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan; | ||||
| 		LOG(INFO) << "Available Rx antennas: "; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent Rx antenna " << ant << " on channel " << chan; | ||||
| 		LOGC(DDEV, INFO) << "Available Rx antennas: "; | ||||
| 		for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i) | ||||
| 			LOG(INFO) << "- '" << *i << "'"; | ||||
| 			LOGC(DDEV, INFO) << "- '" << *i << "'"; | ||||
| 		return false; | ||||
| 	} | ||||
| 	usrp_dev->set_rx_antenna(ant, chan); | ||||
| 	rx_paths[chan] = usrp_dev->get_rx_antenna(chan); | ||||
| 
 | ||||
| 	if (ant != rx_paths[chan]) { | ||||
| 		LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan]; | ||||
| 		LOGC(DDEV, ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << rx_paths[chan]; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| @@ -1240,7 +1212,7 @@ bool uhd_device::setRxAntenna(const std::string &ant, size_t chan) | ||||
| std::string uhd_device::getRxAntenna(size_t chan) | ||||
| { | ||||
| 	if (chan >= rx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return ""; | ||||
| 	} | ||||
| 	return usrp_dev->get_rx_antenna(chan); | ||||
| @@ -1250,23 +1222,23 @@ bool uhd_device::setTxAntenna(const std::string &ant, size_t chan) | ||||
| { | ||||
| 	std::vector<std::string> avail; | ||||
| 	if (chan >= tx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| 	avail = usrp_dev->get_tx_antennas(chan); | ||||
| 	if (std::find(avail.begin(), avail.end(), ant) == avail.end()) { | ||||
| 		LOG(ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan; | ||||
| 		LOG(INFO) << "Available Tx antennas: "; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent Tx antenna " << ant << " on channel " << chan; | ||||
| 		LOGC(DDEV, INFO) << "Available Tx antennas: "; | ||||
| 		for (std::vector<std::string>::const_iterator i = avail.begin(); i != avail.end(); ++i) | ||||
| 			LOG(INFO) << "- '" << *i << "'"; | ||||
| 			LOGC(DDEV, INFO) << "- '" << *i << "'"; | ||||
| 		return false; | ||||
| 	} | ||||
| 	usrp_dev->set_tx_antenna(ant, chan); | ||||
| 	tx_paths[chan] = usrp_dev->get_tx_antenna(chan); | ||||
| 
 | ||||
| 	if (ant != tx_paths[chan]) { | ||||
| 		LOG(ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan]; | ||||
| 		LOGC(DDEV, ALERT) << "Failed setting antenna " << ant << " on channel " << chan << ", got instead " << tx_paths[chan]; | ||||
| 		return false; | ||||
| 	} | ||||
| 
 | ||||
| @@ -1276,12 +1248,25 @@ bool uhd_device::setTxAntenna(const std::string &ant, size_t chan) | ||||
| std::string uhd_device::getTxAntenna(size_t chan) | ||||
| { | ||||
| 	if (chan >= tx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return ""; | ||||
| 	} | ||||
| 	return usrp_dev->get_tx_antenna(chan); | ||||
| } | ||||
| 
 | ||||
| bool uhd_device::requiresRadioAlign() | ||||
| { | ||||
| 	return false; | ||||
| } | ||||
| 
 | ||||
| GSM::Time uhd_device::minLatency() { | ||||
| 	/* Empirical data from a handful of
 | ||||
| 	relatively recent machines shows that the B100 will underrun when | ||||
| 	the transmit threshold is reduced to a time of 6 and a half frames, | ||||
| 	so we set a minimum 7 frame threshold. */ | ||||
| 	return GSM::Time(6,7); | ||||
| } | ||||
| 
 | ||||
| /*
 | ||||
|  * Only allow sampling the Rx path lower than Tx and not vice-versa. | ||||
|  * Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed | ||||
| @@ -1331,7 +1316,7 @@ bool uhd_device::recv_async_msg() | ||||
| 
 | ||||
| 		if ((md.event_code != uhd::async_metadata_t::EVENT_CODE_UNDERFLOW) && | ||||
| 		    (md.event_code != uhd::async_metadata_t::EVENT_CODE_TIME_ERROR)) { | ||||
| 			LOG(ERR) << str_code(md); | ||||
| 			LOGC(DDEV, ERR) << str_code(md); | ||||
| 		} | ||||
| 	} | ||||
| 
 | ||||
| @@ -1491,15 +1476,15 @@ ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp) | ||||
| 		return ERROR_TIMESTAMP; | ||||
| 
 | ||||
| 	if (timestamp < time_end) { | ||||
| 		LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end; | ||||
| 		LOGC(DDEV, ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end; | ||||
| 		uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt); | ||||
| 		LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt; | ||||
| 		LOGC(DDEV, DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt; | ||||
| 		// Do not return error here, because it's a rounding error and is not fatal
 | ||||
| 	} | ||||
| 	if (timestamp > time_end && time_end != 0) { | ||||
| 		LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end; | ||||
| 		LOGC(DDEV, ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end; | ||||
| 		uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt); | ||||
| 		LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt; | ||||
| 		LOGC(DDEV, DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt; | ||||
| 		// Do not return error here, because it's a rounding error and is not fatal
 | ||||
| 	} | ||||
| 
 | ||||
| @@ -1568,9 +1553,9 @@ std::string smpl_buf::str_code(ssize_t code) | ||||
| } | ||||
| 
 | ||||
| RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, | ||||
| 			       InterfaceType iface, size_t chans, double offset, | ||||
| 			       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, offset, tx_paths, rx_paths); | ||||
| 	return new uhd_device(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths); | ||||
| } | ||||
							
								
								
									
										10
									
								
								Transceiver52M/device/usrp1/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								Transceiver52M/device/usrp1/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| include $(top_srcdir)/Makefile.common | ||||
|  | ||||
| AM_CPPFLAGS = -Wall $(STD_DEFINES_AND_INCLUDES) -I${srcdir}/.. | ||||
| AM_CXXFLAGS = -lpthread $(LIBOSMOCORE_CFLAGS) $(LIBOSMOCTRL_CFLAGS) $(LIBOSMOVTY_CFLAGS) $(USRP_CFLAGS) | ||||
|  | ||||
| noinst_HEADERS = USRPDevice.h | ||||
|  | ||||
| noinst_LTLIBRARIES = libdevice.la | ||||
|  | ||||
| libdevice_la_SOURCES = USRPDevice.cpp | ||||
| @@ -58,12 +58,15 @@ const dboardConfigType dboardConfig = TXA_RXB; | ||||
| 
 | ||||
| const double USRPDevice::masterClockRate = 52.0e6; | ||||
| 
 | ||||
| USRPDevice::USRPDevice(size_t sps) | ||||
| USRPDevice::USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, | ||||
| 		       size_t chans, double lo_offset, | ||||
| 		       const std::vector<std::string>& tx_paths, | ||||
| 		       const std::vector<std::string>& rx_paths): | ||||
| 		RadioDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths) | ||||
| { | ||||
|   LOG(INFO) << "creating USRP device..."; | ||||
|   LOGC(DDEV, INFO) << "creating USRP device..."; | ||||
| 
 | ||||
|   this->sps = sps; | ||||
|   decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps)); | ||||
|   decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) tx_sps)); | ||||
|   actualSampleRate = masterClockRate/decimRate; | ||||
|   rxGain = 0; | ||||
| 
 | ||||
| @@ -73,9 +76,9 @@ USRPDevice::USRPDevice(size_t sps) | ||||
|    * split sample rate Tx/Rx - 4/1 sps we need to need to | ||||
|    * compensate for advance rather than delay. | ||||
|    */ | ||||
|   if (sps == 1) | ||||
|   if (tx_sps == 1) | ||||
|     pingOffset = 272; | ||||
|   else if (sps == 4) | ||||
|   else if (tx_sps == 4) | ||||
|     pingOffset = 269 - 7500; | ||||
|   else | ||||
|     pingOffset = 0; | ||||
| @@ -92,34 +95,32 @@ int USRPDevice::open(const std::string &, int, bool) | ||||
| { | ||||
|   writeLock.unlock(); | ||||
| 
 | ||||
|   LOG(INFO) << "opening USRP device.."; | ||||
|   LOGC(DDEV, INFO) << "opening USRP device.."; | ||||
| #ifndef SWLOOPBACK | ||||
|   string rbf = "std_inband.rbf"; | ||||
|   //string rbf = "inband_1rxhb_1tx.rbf";
 | ||||
|   m_uRx.reset(); | ||||
|   if (!skipRx) { | ||||
|   try { | ||||
|     m_uRx = usrp_standard_rx_sptr(usrp_standard_rx::make( | ||||
|                                         0, decimRate * sps, 1, -1, | ||||
|                                         0, decimRate * tx_sps, 1, -1, | ||||
|                                         usrp_standard_rx::FPGA_MODE_NORMAL, | ||||
|                                         1024, 16 * 8, rbf)); | ||||
|     m_uRx->set_fpga_master_clock_freq(masterClockRate); | ||||
|   } | ||||
| 
 | ||||
|   catch(...) { | ||||
|     LOG(ALERT) << "make failed on Rx"; | ||||
|     LOGC(DDEV, ALERT) << "make failed on Rx"; | ||||
|     m_uRx.reset(); | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   if (m_uRx->fpga_master_clock_freq() != masterClockRate) | ||||
|   { | ||||
|     LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq() | ||||
|     LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq() | ||||
|                << ", desired clock freq = " << masterClockRate; | ||||
|     m_uRx.reset(); | ||||
|     return -1; | ||||
|   } | ||||
|   } | ||||
| 
 | ||||
|   try { | ||||
|     m_uTx = usrp_standard_tx_sptr(usrp_standard_tx::make( | ||||
| @@ -129,20 +130,20 @@ int USRPDevice::open(const std::string &, int, bool) | ||||
|   } | ||||
| 
 | ||||
|   catch(...) { | ||||
|     LOG(ALERT) << "make failed on Tx"; | ||||
|     LOGC(DDEV, ALERT) << "make failed on Tx"; | ||||
|     m_uTx.reset(); | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   if (m_uTx->fpga_master_clock_freq() != masterClockRate) | ||||
|   { | ||||
|     LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq() | ||||
|     LOGC(DDEV, ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq() | ||||
|                << ", desired clock freq = " << masterClockRate; | ||||
|     m_uTx.reset(); | ||||
|     return -1; | ||||
|   } | ||||
| 
 | ||||
|   if (!skipRx) m_uRx->stop(); | ||||
|   m_uRx->stop(); | ||||
|   m_uTx->stop(); | ||||
| 
 | ||||
| #endif | ||||
| @@ -183,12 +184,12 @@ int USRPDevice::open(const std::string &, int, bool) | ||||
| 
 | ||||
| bool USRPDevice::start() | ||||
| { | ||||
|   LOG(INFO) << "starting USRP..."; | ||||
|   LOGC(DDEV, INFO) << "starting USRP..."; | ||||
| #ifndef SWLOOPBACK | ||||
|   if (!m_uRx && !skipRx) return false; | ||||
|   if (!m_uRx) return false; | ||||
|   if (!m_uTx) return false; | ||||
| 
 | ||||
|   if (!skipRx) m_uRx->stop(); | ||||
|   m_uRx->stop(); | ||||
|   m_uTx->stop(); | ||||
| 
 | ||||
|   writeLock.lock(); | ||||
| @@ -218,10 +219,7 @@ bool USRPDevice::start() | ||||
|   isAligned = false; | ||||
| 
 | ||||
| 
 | ||||
|   if (!skipRx) | ||||
|   started = (m_uRx->start() && m_uTx->start()); | ||||
|   else | ||||
|   started = m_uTx->start(); | ||||
|   return started; | ||||
| #else | ||||
|   gettimeofday(&lastReadTime,NULL); | ||||
| @@ -267,7 +265,7 @@ double USRPDevice::minRxGain() | ||||
| double USRPDevice::setTxGain(double dB, size_t chan) | ||||
| { | ||||
|   if (chan) { | ||||
|     LOG(ALERT) << "Invalid channel " << chan; | ||||
|     LOGC(DDEV, ALERT) << "Invalid channel " << chan; | ||||
|     return 0.0; | ||||
|   } | ||||
| 
 | ||||
| @@ -277,10 +275,10 @@ double USRPDevice::setTxGain(double dB, size_t chan) | ||||
|   if (dB < minTxGain()) | ||||
|     dB = minTxGain(); | ||||
| 
 | ||||
|   LOG(NOTICE) << "Setting TX gain to " << dB << " dB."; | ||||
|   LOGC(DDEV, NOTICE) << "Setting TX gain to " << dB << " dB."; | ||||
| 
 | ||||
|   if (!m_dbTx->set_gain(dB)) | ||||
|     LOG(ERR) << "Error setting TX gain"; | ||||
|     LOGC(DDEV, ERR) << "Error setting TX gain"; | ||||
| 
 | ||||
|   writeLock.unlock(); | ||||
| 
 | ||||
| @@ -291,7 +289,7 @@ double USRPDevice::setTxGain(double dB, size_t chan) | ||||
| double USRPDevice::setRxGain(double dB, size_t chan) | ||||
| { | ||||
|   if (chan) { | ||||
|     LOG(ALERT) << "Invalid channel " << chan; | ||||
|     LOGC(DDEV, ALERT) << "Invalid channel " << chan; | ||||
|     return 0.0; | ||||
|   } | ||||
| 
 | ||||
| @@ -303,10 +301,10 @@ double USRPDevice::setRxGain(double dB, size_t chan) | ||||
|   if (dB < minRxGain()) | ||||
|     dB = minRxGain(); | ||||
| 
 | ||||
|   LOG(NOTICE) << "Setting RX gain to " << dB << " dB."; | ||||
|   LOGC(DDEV, NOTICE) << "Setting RX gain to " << dB << " dB."; | ||||
| 
 | ||||
|   if (!m_dbRx->set_gain(dB)) | ||||
|     LOG(ERR) << "Error setting RX gain"; | ||||
|     LOGC(DDEV, ERR) << "Error setting RX gain"; | ||||
| 
 | ||||
|   writeLock.unlock(); | ||||
| 
 | ||||
| @@ -316,43 +314,51 @@ double USRPDevice::setRxGain(double dB, size_t chan) | ||||
| bool USRPDevice::setRxAntenna(const std::string &ant, size_t chan) | ||||
| { | ||||
| 	if (chan >= rx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
| 	LOG(ALERT) << "Not implemented"; | ||||
| 	LOGC(DDEV, ALERT) << "Not implemented"; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| std::string USRPDevice::getRxAntenna(size_t chan) | ||||
| { | ||||
| 	if (chan >= rx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return ""; | ||||
| 	} | ||||
| 	LOG(ALERT) << "Not implemented"; | ||||
| 	LOGC(DDEV, ALERT) << "Not implemented"; | ||||
| 	return ""; | ||||
| } | ||||
| 
 | ||||
| bool USRPDevice::setTxAntenna(const std::string &ant, size_t chan) | ||||
| { | ||||
| 	if (chan >= tx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return false; | ||||
| 	} | ||||
| 	LOG(ALERT) << "Not implemented"; | ||||
| 	LOGC(DDEV, ALERT) << "Not implemented"; | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| std::string USRPDevice::getTxAntenna(size_t chan) | ||||
| { | ||||
| 	if (chan >= tx_paths.size()) { | ||||
| 		LOG(ALERT) << "Requested non-existent channel " << chan; | ||||
| 		LOGC(DDEV, ALERT) << "Requested non-existent channel " << chan; | ||||
| 		return ""; | ||||
| 	} | ||||
| 	LOG(ALERT) << "Not implemented"; | ||||
| 	LOGC(DDEV, ALERT) << "Not implemented"; | ||||
| 	return ""; | ||||
| } | ||||
| 
 | ||||
| bool USRPDevice::requiresRadioAlign() | ||||
| { | ||||
| 	return true; | ||||
| } | ||||
| 
 | ||||
| GSM::Time USRPDevice::minLatency() { | ||||
| 	return GSM::Time(1,1); | ||||
| } | ||||
| 
 | ||||
| // NOTE: Assumes sequential reads
 | ||||
| int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
| @@ -387,18 +393,18 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
| 
 | ||||
|     // read USRP packets, parse and save A/D data as needed
 | ||||
|     readLen = m_uRx->read((void *)readBuf,readLen,overrun); | ||||
|     for(int pktNum = 0; pktNum < (readLen/512); pktNum++) { | ||||
|     for (int pktNum = 0; pktNum < (readLen/512); pktNum++) { | ||||
|       // tmpBuf points to start of a USB packet
 | ||||
|       uint32_t* tmpBuf = (uint32_t *) (readBuf+pktNum*512/4); | ||||
|       TIMESTAMP pktTimestamp = usrp_to_host_u32(tmpBuf[1]); | ||||
|       uint32_t word0 = usrp_to_host_u32(tmpBuf[0]); | ||||
|       uint32_t chan = (word0 >> 16) & 0x1f; | ||||
|       unsigned payloadSz = word0 & 0x1ff; | ||||
|       LOG(DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp; | ||||
|       LOGC(DDEV, DEBUG) << "first two bytes: " << hex << word0 << " " << dec << pktTimestamp; | ||||
| 
 | ||||
|       bool incrementHi32 = ((lastPktTimestamp & 0x0ffffffffll) > pktTimestamp); | ||||
|       if (incrementHi32 && (timeStart!=0)) { | ||||
|            LOG(DEBUG) << "high 32 increment!!!"; | ||||
|            LOGC(DDEV, DEBUG) << "high 32 increment!!!"; | ||||
|            hi32Timestamp++; | ||||
|       } | ||||
|       pktTimestamp = (((TIMESTAMP) hi32Timestamp) << 32) | pktTimestamp; | ||||
| @@ -410,19 +416,19 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
| 	if ((word2 >> 16) == ((0x01 << 8) | 0x02)) { | ||||
|           timestamp -= timestampOffset; | ||||
| 	  timestampOffset = pktTimestamp - pingTimestamp + pingOffset; | ||||
| 	  LOG(DEBUG) << "updating timestamp offset to: " << timestampOffset; | ||||
| 	  LOGC(DDEV, DEBUG) << "updating timestamp offset to: " << timestampOffset; | ||||
|           timestamp += timestampOffset; | ||||
| 	  isAligned = true; | ||||
| 	} | ||||
| 	continue; | ||||
|       } | ||||
|       if (chan != 0) { | ||||
| 	LOG(DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz; | ||||
| 	LOGC(DDEV, DEBUG) << "chan: " << chan << ", timestamp: " << pktTimestamp << ", sz:" << payloadSz; | ||||
| 	continue; | ||||
|       } | ||||
|       if ((word0 >> 28) & 0x04) { | ||||
| 	if (underrun) *underrun = true; | ||||
| 	LOG(DEBUG) << "UNDERRUN in TRX->USRP interface"; | ||||
| 	LOGC(DDEV, DEBUG) << "UNDERRUN in TRX->USRP interface"; | ||||
|       } | ||||
|       if (RSSI) *RSSI = (word0 >> 21) & 0x3f; | ||||
| 
 | ||||
| @@ -443,7 +449,7 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
|       if (pktTimestamp + payloadSz/2/sizeof(short) > timeEnd) | ||||
| 	timeEnd = pktTimestamp+payloadSz/2/sizeof(short); | ||||
| 
 | ||||
|       LOG(DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp; | ||||
|       LOGC(DDEV, DEBUG) << "timeStart: " << timeStart << ", timeEnd: " << timeEnd << ", pktTimestamp: " << pktTimestamp; | ||||
| 
 | ||||
|     } | ||||
|   } | ||||
| @@ -451,14 +457,14 @@ int USRPDevice::readSamples(std::vector<short *> &bufs, int len, bool *overrun, | ||||
|   // copy desired data to buf
 | ||||
|   unsigned bufStart = dataStart+(timestamp-timeStart); | ||||
|   if (bufStart + len < currDataSize/2) { | ||||
|     LOG(DEBUG) << "bufStart: " << bufStart; | ||||
|     LOGC(DDEV, DEBUG) << "bufStart: " << bufStart; | ||||
|     memcpy(buf,data+bufStart*2,len*2*sizeof(short)); | ||||
|     memset(data+bufStart*2,0,len*2*sizeof(short)); | ||||
|   } | ||||
|   else { | ||||
|     LOG(DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart; | ||||
|     LOGC(DDEV, DEBUG) << "len: " << len << ", currDataSize/2: " << currDataSize/2 << ", bufStart: " << bufStart; | ||||
|     unsigned firstLength = (currDataSize/2-bufStart); | ||||
|     LOG(DEBUG) << "firstLength: " << firstLength; | ||||
|     LOGC(DDEV, DEBUG) << "firstLength: " << firstLength; | ||||
|     memcpy(buf,data+bufStart*2,firstLength*2*sizeof(short)); | ||||
|     memset(data+bufStart*2,0,firstLength*2*sizeof(short)); | ||||
|     memcpy(buf+firstLength*2,data,(len-firstLength)*2*sizeof(short)); | ||||
| @@ -588,19 +594,19 @@ bool USRPDevice::setTxFreq(double wFreq, size_t chan) | ||||
|   usrp_tune_result result; | ||||
| 
 | ||||
|   if (chan) { | ||||
|     LOG(ALERT) << "Invalid channel " << chan; | ||||
|     LOGC(DDEV, ALERT) << "Invalid channel " << chan; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   if (m_uTx->tune(txSubdevSpec.side, m_dbTx, wFreq, &result)) { | ||||
|     LOG(INFO) << "set TX: " << wFreq << std::endl | ||||
|     LOGC(DDEV, INFO) << "set TX: " << wFreq << std::endl | ||||
|               << "    baseband freq: " << result.baseband_freq << std::endl | ||||
|               << "    DDC freq:      " << result.dxc_freq << std::endl | ||||
|               << "    residual freq: " << result.residual_freq; | ||||
|     return true; | ||||
|   } | ||||
|   else { | ||||
|     LOG(ALERT) << "set TX: " << wFreq << "failed" << std::endl | ||||
|     LOGC(DDEV, ALERT) << "set TX: " << wFreq << " failed" << std::endl | ||||
|                << "    baseband freq: " << result.baseband_freq << std::endl | ||||
|                << "    DDC freq:      " << result.dxc_freq << std::endl | ||||
|                << "    residual freq: " << result.residual_freq; | ||||
| @@ -613,19 +619,19 @@ bool USRPDevice::setRxFreq(double wFreq, size_t chan) | ||||
|   usrp_tune_result result; | ||||
| 
 | ||||
|   if (chan) { | ||||
|     LOG(ALERT) << "Invalid channel " << chan; | ||||
|     LOGC(DDEV, ALERT) << "Invalid channel " << chan; | ||||
|     return false; | ||||
|   } | ||||
| 
 | ||||
|   if (m_uRx->tune(0, m_dbRx, wFreq, &result)) { | ||||
|     LOG(INFO) << "set RX: " << wFreq << std::endl | ||||
|     LOGC(DDEV, INFO) << "set RX: " << wFreq << std::endl | ||||
|               << "    baseband freq: " << result.baseband_freq << std::endl | ||||
|               << "    DDC freq:      " << result.dxc_freq << std::endl | ||||
|               << "    residual freq: " << result.residual_freq; | ||||
|     return true; | ||||
|   } | ||||
|   else { | ||||
|     LOG(ALERT) << "set RX: " << wFreq << "failed" << std::endl | ||||
|     LOGC(DDEV, ALERT) << "set RX: " << wFreq << " failed" << std::endl | ||||
|                << "    baseband freq: " << result.baseband_freq << std::endl | ||||
|                << "    DDC freq:      " << result.dxc_freq << std::endl | ||||
|                << "    residual freq: " << result.residual_freq; | ||||
| @@ -640,9 +646,21 @@ bool USRPDevice::setRxFreq(double wFreq) { return true;}; | ||||
| #endif | ||||
| 
 | ||||
| RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps, | ||||
| 			       InterfaceType iface, size_t chans, double offset, | ||||
| 			       InterfaceType iface, size_t chans, double lo_offset, | ||||
| 			       const std::vector<std::string>& tx_paths, | ||||
| 			       const std::vector<std::string>& rx_paths) | ||||
| { | ||||
| 	return new USRPDevice(tx_sps); | ||||
| 	if (tx_sps != rx_sps) { | ||||
| 		LOGC(DDEV, ERROR) << "USRP1 requires tx_sps == rx_sps"; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (chans != 1) { | ||||
| 		LOGC(DDEV, ERROR) << "USRP1 supports only 1 channel"; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	if (lo_offset != 0.0) { | ||||
| 		LOGC(DDEV, ERROR) << "USRP1 doesn't support lo_offset"; | ||||
| 		return NULL; | ||||
| 	} | ||||
| 	return new USRPDevice(tx_sps, rx_sps, iface, chans, lo_offset, tx_paths, rx_paths); | ||||
| } | ||||
| @@ -48,7 +48,6 @@ private: | ||||
|   usrp_subdev_spec rxSubdevSpec; | ||||
|   usrp_subdev_spec txSubdevSpec; | ||||
| 
 | ||||
|   int sps; | ||||
|   double actualSampleRate;	///< the actual USRP sampling rate
 | ||||
|   unsigned int decimRate;	///< the USRP decimation rate
 | ||||
| 
 | ||||
| @@ -56,7 +55,6 @@ private: | ||||
|   unsigned long long samplesWritten;	///< number of samples sent to USRP
 | ||||
| 
 | ||||
|   bool started;			///< flag indicates USRP has started
 | ||||
|   bool skipRx;			///< set if USRP is transmit-only.
 | ||||
| 
 | ||||
|   static const unsigned int currDataSize_log2 = 21; | ||||
|   static const unsigned long currDataSize = (1 << currDataSize_log2); | ||||
| @@ -96,7 +94,9 @@ private: | ||||
|  public: | ||||
| 
 | ||||
|   /** Object constructor */ | ||||
|   USRPDevice(size_t sps); | ||||
|   USRPDevice(size_t tx_sps, size_t rx_sps, InterfaceType iface, size_t chans, double lo_offset, | ||||
| 		const std::vector<std::string>& tx_paths, | ||||
| 		const std::vector<std::string>& rx_paths); | ||||
| 
 | ||||
|   /** Instantiate the USRP */ | ||||
|   int open(const std::string &, int, bool); | ||||
| @@ -191,13 +191,18 @@ private: | ||||
|   /* return the used RX path */ | ||||
|   std::string getTxAntenna(size_t chan = 0); | ||||
| 
 | ||||
|   /** return whether user drives synchronization of Tx/Rx of USRP */ | ||||
|   bool requiresRadioAlign(); | ||||
| 
 | ||||
|   /** return whether user drives synchronization of Tx/Rx of USRP */ | ||||
|   virtual GSM::Time minLatency(); | ||||
| 
 | ||||
|   /** Return internal status values */ | ||||
|   inline double getTxFreq(size_t chan = 0) { return 0; } | ||||
|   inline double getRxFreq(size_t chan = 0) { return 0; } | ||||
|   inline double getSampleRate() { return actualSampleRate; } | ||||
|   inline double numberRead() { return samplesRead; } | ||||
|   inline double numberWritten() { return samplesWritten; } | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| #endif // _USRP_DEVICE_H_
 | ||||
| @@ -22,11 +22,13 @@ | ||||
|  | ||||
| #include "Transceiver.h" | ||||
| #include "radioDevice.h" | ||||
| #include "Utils.h" | ||||
|  | ||||
| #include <time.h> | ||||
| #include <signal.h> | ||||
| #include <stdlib.h> | ||||
| #include <unistd.h> | ||||
| #include <getopt.h> | ||||
| #include <sched.h> | ||||
| #include <vector> | ||||
| #include <string> | ||||
| @@ -37,132 +39,42 @@ | ||||
| #include <Logger.h> | ||||
|  | ||||
| extern "C" { | ||||
| #include <osmocom/core/talloc.h> | ||||
| #include <osmocom/core/application.h> | ||||
| #include <osmocom/core/msgb.h> | ||||
| #include <osmocom/core/stats.h> | ||||
| #include <osmocom/vty/logging.h> | ||||
| #include <osmocom/vty/ports.h> | ||||
| #include <osmocom/vty/misc.h> | ||||
| #include <osmocom/vty/telnet_interface.h> | ||||
| #include <osmocom/ctrl/control_vty.h> | ||||
| #include <osmocom/ctrl/ports.h> | ||||
| #include <osmocom/ctrl/control_if.h> | ||||
| #include <osmocom/vty/stats.h> | ||||
| #include <osmocom/vty/command.h> | ||||
|  | ||||
| #include "convolve.h" | ||||
| #include "convert.h" | ||||
| #include "trx_vty.h" | ||||
| #include "debug.h" | ||||
| #include "osmo_signal.h" | ||||
| } | ||||
|  | ||||
| /* Samples-per-symbol for downlink path | ||||
|  *     4 - Uses precision modulator (more computation, less distortion) | ||||
|  *     1 - Uses minimized modulator (less computation, more distortion) | ||||
|  * | ||||
|  *     Other values are invalid. Receive path (uplink) is always | ||||
|  *     downsampled to 1 sps. Default to 4 sps for all cases. | ||||
|  */ | ||||
| #define DEFAULT_TX_SPS		4 | ||||
| #define DEFAULT_CONFIG_FILE	"osmo-trx.cfg" | ||||
|  | ||||
| /* | ||||
|  * Samples-per-symbol for uplink (receiver) path | ||||
|  *     Do not modify this value. EDGE configures 4 sps automatically on | ||||
|  *     B200/B210 devices only. Use of 4 sps on the receive path for other | ||||
|  *     configurations is not supported. | ||||
|  */ | ||||
| #define DEFAULT_RX_SPS		1 | ||||
| #define charp2str(a) ((a) ? std::string(a) : std::string("")) | ||||
|  | ||||
| /* Default configuration parameters */ | ||||
| #define DEFAULT_TRX_PORT	5700 | ||||
| #define DEFAULT_TRX_IP		"127.0.0.1" | ||||
| #define DEFAULT_CHANS		1 | ||||
|  | ||||
| struct trx_config { | ||||
| 	std::string log_level; | ||||
| 	std::string local_addr; | ||||
| 	std::string remote_addr; | ||||
| 	std::string dev_args; | ||||
| 	unsigned port; | ||||
| 	unsigned tx_sps; | ||||
| 	unsigned rx_sps; | ||||
| 	unsigned chans; | ||||
| 	unsigned rtsc; | ||||
| 	unsigned rach_delay; | ||||
| 	bool extref; | ||||
| 	bool gpsref; | ||||
| 	Transceiver::FillerType filler; | ||||
| 	bool mcbts; | ||||
| 	double offset; | ||||
| 	double rssi_offset; | ||||
| 	bool swap_channels; | ||||
| 	bool edge; | ||||
| 	int sched_rr; | ||||
| 	std::vector<std::string> rx_paths; | ||||
| 	std::vector<std::string> tx_paths; | ||||
| }; | ||||
| static char* config_file = (char*)DEFAULT_CONFIG_FILE; | ||||
|  | ||||
| volatile bool gshutdown = false; | ||||
|  | ||||
| /* Setup configuration values | ||||
|  *     Don't query the existence of the Log.Level because it's a | ||||
|  *     mandatory value. That is, if it doesn't exist, the configuration | ||||
|  *     table will crash or will have already crashed. Everything else we | ||||
|  *     can survive without and use default values if the database entries | ||||
|  *     are empty. | ||||
|  */ | ||||
| bool trx_setup_config(struct trx_config *config) | ||||
| { | ||||
| 	std::string refstr, fillstr, divstr, mcstr, edgestr; | ||||
| 	std::vector<std::string>::const_iterator si; | ||||
| static void *tall_trx_ctx; | ||||
| static struct trx_ctx *g_trx_ctx; | ||||
| static struct ctrl_handle *g_ctrlh; | ||||
|  | ||||
| 	if (config->mcbts && config->chans > 5) { | ||||
| 		std::cout << "Unsupported number of channels" << std::endl; | ||||
| 		return false; | ||||
| 	} | ||||
|  | ||||
| 	edgestr = config->edge ? "Enabled" : "Disabled"; | ||||
| 	mcstr = config->mcbts ? "Enabled" : "Disabled"; | ||||
|  | ||||
| 	if (config->extref) | ||||
| 		refstr = "External"; | ||||
| 	else if (config->gpsref) | ||||
| 		refstr = "GPS"; | ||||
| 	else | ||||
| 		refstr = "Internal"; | ||||
|  | ||||
| 	switch (config->filler) { | ||||
| 	case Transceiver::FILLER_DUMMY: | ||||
| 		fillstr = "Dummy bursts"; | ||||
| 		break; | ||||
| 	case Transceiver::FILLER_ZERO: | ||||
| 		fillstr = "Disabled"; | ||||
| 		break; | ||||
| 	case Transceiver::FILLER_NORM_RAND: | ||||
| 		fillstr = "Normal busrts with random payload"; | ||||
| 		break; | ||||
| 	case Transceiver::FILLER_EDGE_RAND: | ||||
| 		fillstr = "EDGE busrts with random payload"; | ||||
| 		break; | ||||
| 	case Transceiver::FILLER_ACCESS_RAND: | ||||
| 		fillstr = "Access busrts with random payload"; | ||||
| 		break; | ||||
| 	} | ||||
|  | ||||
| 	std::ostringstream ost(""); | ||||
| 	ost << "Config Settings" << std::endl; | ||||
| 	ost << "   Log Level............... " << config->log_level << std::endl; | ||||
| 	ost << "   Device args............. " << config->dev_args << std::endl; | ||||
| 	ost << "   TRX Base Port........... " << config->port << std::endl; | ||||
| 	ost << "   TRX Address............. " << config->local_addr << std::endl; | ||||
| 	ost << "   GSM Core Address........." << config->remote_addr << std::endl; | ||||
| 	ost << "   Channels................ " << config->chans << std::endl; | ||||
| 	ost << "   Tx Samples-per-Symbol... " << config->tx_sps << std::endl; | ||||
| 	ost << "   Rx Samples-per-Symbol... " << config->rx_sps << std::endl; | ||||
| 	ost << "   EDGE support............ " << edgestr << std::endl; | ||||
| 	ost << "   Reference............... " << refstr << std::endl; | ||||
| 	ost << "   C0 Filler Table......... " << fillstr << std::endl; | ||||
| 	ost << "   Multi-Carrier........... " << mcstr << std::endl; | ||||
| 	ost << "   Tuning offset........... " << config->offset << std::endl; | ||||
| 	ost << "   RSSI to dBm offset...... " << config->rssi_offset << std::endl; | ||||
| 	ost << "   Swap channels........... " << config->swap_channels << std::endl; | ||||
| 	ost << "   Tx Antennas............."; | ||||
| 		for (si = config->tx_paths.begin(); si != config->tx_paths.end(); ++si) | ||||
| 			ost << " '" << ((*si != "") ? *si : "<default>") <<  "'"; | ||||
| 		ost << std::endl; | ||||
| 	ost << "   Rx Antennas............."; | ||||
| 		for (si = config->rx_paths.begin(); si != config->rx_paths.end(); ++si) | ||||
| 			ost << " '" << ((*si != "") ? *si : "<default>") <<  "'"; | ||||
| 		ost << std::endl; | ||||
|  | ||||
| 	std::cout << ost << std::endl; | ||||
| 	return true; | ||||
| } | ||||
| static RadioDevice *usrp; | ||||
| static RadioInterface *radio; | ||||
| static Transceiver *transceiver; | ||||
|  | ||||
| /* Create radio interface | ||||
|  *     The interface consists of sample rate changes, frequency shifts, | ||||
| @@ -171,24 +83,24 @@ bool trx_setup_config(struct trx_config *config) | ||||
|  *     The radio interface connects the main transceiver with the device | ||||
|  *     object, which may be operating some other rate. | ||||
|  */ | ||||
| RadioInterface *makeRadioInterface(struct trx_config *config, | ||||
| RadioInterface *makeRadioInterface(struct trx_ctx *trx, | ||||
|                                    RadioDevice *usrp, int type) | ||||
| { | ||||
| 	RadioInterface *radio = NULL; | ||||
|  | ||||
| 	switch (type) { | ||||
| 	case RadioDevice::NORMAL: | ||||
| 		radio = new RadioInterface(usrp, config->tx_sps, | ||||
| 					   config->rx_sps, config->chans); | ||||
| 		radio = new RadioInterface(usrp, trx->cfg.tx_sps, | ||||
| 					   trx->cfg.rx_sps, trx->cfg.num_chans); | ||||
| 		break; | ||||
| 	case RadioDevice::RESAMP_64M: | ||||
| 	case RadioDevice::RESAMP_100M: | ||||
| 		radio = new RadioInterfaceResamp(usrp, config->tx_sps, | ||||
| 						 config->rx_sps); | ||||
| 		radio = new RadioInterfaceResamp(usrp, trx->cfg.tx_sps, | ||||
| 						 trx->cfg.rx_sps); | ||||
| 		break; | ||||
| 	case RadioDevice::MULTI_ARFCN: | ||||
| 		radio = new RadioInterfaceMulti(usrp, config->tx_sps, | ||||
| 						config->rx_sps, config->chans); | ||||
| 		radio = new RadioInterfaceMulti(usrp, trx->cfg.tx_sps, | ||||
| 						trx->cfg.rx_sps, trx->cfg.num_chans); | ||||
| 		break; | ||||
| 	default: | ||||
| 		LOG(ALERT) << "Unsupported radio interface configuration"; | ||||
| @@ -203,248 +115,243 @@ RadioInterface *makeRadioInterface(struct trx_config *config, | ||||
| 	return radio; | ||||
| } | ||||
|  | ||||
| /* Callback function to be called every time we receive a signal from TRANSC */ | ||||
| static int transc_sig_cb(unsigned int subsys, unsigned int signal, | ||||
| 		     void *handler_data, void *signal_data) | ||||
| { | ||||
| 	switch (signal) { | ||||
| 	case S_TRANSC_STOP_REQUIRED: | ||||
| 		gshutdown = true; | ||||
|                 break; | ||||
| 	default: | ||||
|                 break; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Create transceiver core | ||||
|  *     The multi-threaded modem core operates at multiples of the GSM rate of | ||||
|  *     270.8333 ksps and consists of GSM specific modulation, demodulation, | ||||
|  *     and decoding schemes. Also included are the socket interfaces for | ||||
|  *     connecting to the upper layer stack. | ||||
|  */ | ||||
| Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio) | ||||
| int makeTransceiver(struct trx_ctx *trx, RadioInterface *radio) | ||||
| { | ||||
| 	Transceiver *trx; | ||||
| 	VectorFIFO *fifo; | ||||
|  | ||||
| 	trx = new Transceiver(config->port, config->local_addr.c_str(), | ||||
| 			      config->remote_addr.c_str(), config->tx_sps, | ||||
| 			      config->rx_sps, config->chans, GSM::Time(3,0), | ||||
| 			      radio, config->rssi_offset); | ||||
| 	if (!trx->init(config->filler, config->rtsc, | ||||
| 		       config->rach_delay, config->edge)) { | ||||
| 	transceiver = new Transceiver(trx->cfg.base_port, trx->cfg.bind_addr, | ||||
| 			      trx->cfg.remote_addr, trx->cfg.tx_sps, | ||||
| 			      trx->cfg.rx_sps, trx->cfg.num_chans, GSM::Time(3,0), | ||||
| 			      radio, trx->cfg.rssi_offset); | ||||
| 	if (!transceiver->init(trx->cfg.filler, trx->cfg.rtsc, | ||||
| 		       trx->cfg.rach_delay, trx->cfg.egprs)) { | ||||
| 		LOG(ALERT) << "Failed to initialize transceiver"; | ||||
| 		delete trx; | ||||
| 		return NULL; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	for (size_t i = 0; i < config->chans; i++) { | ||||
|         transceiver->setSignalHandler(transc_sig_cb); | ||||
|  | ||||
| 	for (size_t i = 0; i < trx->cfg.num_chans; i++) { | ||||
| 		fifo = radio->receiveFIFO(i); | ||||
| 		if (fifo && trx->receiveFIFO(fifo, i)) | ||||
| 		if (fifo && transceiver->receiveFIFO(fifo, i)) | ||||
| 			continue; | ||||
|  | ||||
| 		LOG(ALERT) << "Could not attach FIFO to channel " << i; | ||||
| 		delete trx; | ||||
| 		return NULL; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return trx; | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void sig_handler(int signo) | ||||
| { | ||||
| 	fprintf(stdout, "Received shutdown signal"); | ||||
| 	gshutdown = true; | ||||
| 	fprintf(stdout, "signal %d received\n", signo); | ||||
| 	switch (signo) { | ||||
| 	case SIGINT: | ||||
| 	case SIGTERM: | ||||
| 		fprintf(stdout, "shutting down\n"); | ||||
| 		gshutdown = true; | ||||
| 		break; | ||||
| 	case SIGABRT: | ||||
| 	case SIGUSR1: | ||||
| 		talloc_report(tall_trx_ctx, stderr); | ||||
| 		talloc_report_full(tall_trx_ctx, stderr); | ||||
| 		break; | ||||
| 	case SIGUSR2: | ||||
| 		talloc_report_full(tall_trx_ctx, stderr); | ||||
| 		break; | ||||
| 	default: | ||||
| 		break; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| static void setup_signal_handlers() | ||||
| { | ||||
| 	if (signal(SIGINT, sig_handler) == SIG_ERR) { | ||||
| 		fprintf(stderr, "Failed to install SIGINT signal handler\n"); | ||||
| 		exit(EXIT_FAILURE); | ||||
| 	} | ||||
| 	if (signal(SIGTERM, sig_handler) == SIG_ERR) { | ||||
| 		fprintf(stderr, "Couldn't install SIGTERM signal handler\n"); | ||||
| 		exit( EXIT_FAILURE); | ||||
| 	} | ||||
| } | ||||
|  | ||||
|  | ||||
| static std::vector<std::string> comma_delimited_to_vector(char* opt) { | ||||
| 	std::string str = std::string(opt); | ||||
| 	std::vector<std::string> result; | ||||
| 	std::stringstream ss(str); | ||||
|  | ||||
| 	while( ss.good() ) | ||||
| 	{ | ||||
| 	    std::string substr; | ||||
| 	    getline(ss, substr, ','); | ||||
| 	    result.push_back(substr); | ||||
| 	} | ||||
| 	return result; | ||||
| 	/* Handle keyboard interrupt SIGINT */ | ||||
| 	signal(SIGINT, &sig_handler); | ||||
| 	signal(SIGTERM, &sig_handler); | ||||
| 	signal(SIGABRT, &sig_handler); | ||||
| 	signal(SIGUSR1, &sig_handler); | ||||
| 	signal(SIGUSR2, &sig_handler); | ||||
| 	osmo_init_ignore_signals(); | ||||
| } | ||||
|  | ||||
| static void print_help() | ||||
| { | ||||
| 	fprintf(stdout, "Options:\n" | ||||
| 		"  -h    This text\n" | ||||
| 		"  -a    UHD device args\n" | ||||
| 		"  -l    Logging level (%s)\n" | ||||
| 		"  -i    IP address of GSM core\n" | ||||
| 		"  -j    IP address of osmo-trx\n" | ||||
| 		"  -p    Base port number\n" | ||||
| 		"  -e    Enable EDGE receiver\n" | ||||
| 		"  -m    Enable multi-ARFCN transceiver (default=disabled)\n" | ||||
| 		"  -x    Enable external 10 MHz reference\n" | ||||
| 		"  -g    Enable GPSDO reference\n" | ||||
| 		"  -s    Tx samples-per-symbol (1 or 4)\n" | ||||
| 		"  -b    Rx samples-per-symbol (1 or 4)\n" | ||||
| 		"  -c    Number of ARFCN channels (default=1)\n" | ||||
| 		"  -f    Enable C0 filler table\n" | ||||
| 		"  -o    Set baseband frequency offset (default=auto)\n" | ||||
| 		"  -r    Random Normal Burst test mode with TSC\n" | ||||
| 		"  -A    Random Access Burst test mode with delay\n" | ||||
| 		"  -R    RSSI to dBm offset in dB (default=0)\n" | ||||
| 		"  -S    Swap channels (UmTRX only)\n" | ||||
| 		"  -t    SCHED_RR real-time priority (1..32)\n" | ||||
| 		"  -y    comma-delimited list of Tx paths (num elements matches -c)\n" | ||||
| 		"  -z    comma-delimited list of Rx paths (num elements matches -c)\n", | ||||
| 		"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG"); | ||||
| 		"  -h, --help      This text\n" | ||||
| 		"  -C, --config    Filename The config file to use\n" | ||||
| 		"  -V, --version   Print the version of OsmoTRX\n" | ||||
| 		); | ||||
| } | ||||
|  | ||||
| static void handle_options(int argc, char **argv, struct trx_config *config) | ||||
| static void print_deprecated(char opt) | ||||
| { | ||||
| 	LOG(WARNING) << "Cmd line option '" << opt << "' is deprecated and will be soon removed." | ||||
| 		<< " Please use VTY cfg option instead." | ||||
| 		<< " All cmd line options are already being overriden by VTY options if set."; | ||||
| } | ||||
|  | ||||
| static void handle_options(int argc, char **argv, struct trx_ctx* trx) | ||||
| { | ||||
| 	int option; | ||||
| 	bool tx_path_set = false, rx_path_set = false; | ||||
| 	unsigned int i; | ||||
| 	std::vector<std::string> rx_paths, tx_paths; | ||||
| 	bool rx_paths_set = false, tx_paths_set = false; | ||||
| 	static struct option long_options[] = { | ||||
| 		{"help", 0, 0, 'h'}, | ||||
| 		{"config", 1, 0, 'C'}, | ||||
| 		{"version", 0, 0, 'V'}, | ||||
| 		{NULL, 0, 0, 0} | ||||
| 	}; | ||||
|  | ||||
| 	config->log_level = "NOTICE"; | ||||
| 	config->local_addr = DEFAULT_TRX_IP; | ||||
| 	config->remote_addr = DEFAULT_TRX_IP; | ||||
| 	config->port = DEFAULT_TRX_PORT; | ||||
| 	config->tx_sps = DEFAULT_TX_SPS; | ||||
| 	config->rx_sps = DEFAULT_RX_SPS; | ||||
| 	config->chans = DEFAULT_CHANS; | ||||
| 	config->rtsc = 0; | ||||
| 	config->rach_delay = 0; | ||||
| 	config->extref = false; | ||||
| 	config->gpsref = false; | ||||
| 	config->filler = Transceiver::FILLER_ZERO; | ||||
| 	config->mcbts = false; | ||||
| 	config->offset = 0.0; | ||||
| 	config->rssi_offset = 0.0; | ||||
| 	config->swap_channels = false; | ||||
| 	config->edge = false; | ||||
| 	config->sched_rr = -1; | ||||
| 	config->tx_paths = std::vector<std::string>(DEFAULT_CHANS, ""); | ||||
| 	config->rx_paths = std::vector<std::string>(DEFAULT_CHANS, ""); | ||||
|  | ||||
| 	while ((option = getopt(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:")) != -1) { | ||||
| 	while ((option = getopt_long(argc, argv, "ha:l:i:j:p:c:dmxgfo:s:b:r:A:R:Set:y:z:C:V", long_options, | ||||
| 		NULL)) != -1) { | ||||
| 		switch (option) { | ||||
| 		case 'h': | ||||
| 			print_help(); | ||||
| 			exit(0); | ||||
| 			break; | ||||
| 		case 'a': | ||||
| 			config->dev_args = optarg; | ||||
| 			print_deprecated(option); | ||||
| 			osmo_talloc_replace_string(trx, &trx->cfg.dev_args, optarg); | ||||
| 			break; | ||||
| 		case 'l': | ||||
| 			config->log_level = optarg; | ||||
| 			print_deprecated(option); | ||||
| 			log_set_log_level(osmo_stderr_target, atoi(optarg)); | ||||
| 			break; | ||||
| 		case 'i': | ||||
| 			config->remote_addr = optarg; | ||||
| 			print_deprecated(option); | ||||
| 			osmo_talloc_replace_string(trx, &trx->cfg.remote_addr, optarg); | ||||
| 			break; | ||||
| 		case 'j': | ||||
| 			config->local_addr = optarg; | ||||
| 			print_deprecated(option); | ||||
| 			osmo_talloc_replace_string(trx, &trx->cfg.bind_addr, optarg); | ||||
| 			break; | ||||
| 		case 'p': | ||||
| 			config->port = atoi(optarg); | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.base_port = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'c': | ||||
| 			config->chans = atoi(optarg); | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.num_chans = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'm': | ||||
| 			config->mcbts = true; | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.multi_arfcn = true; | ||||
| 			break; | ||||
| 		case 'x': | ||||
| 			config->extref = true; | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.clock_ref = REF_EXTERNAL; | ||||
| 			break; | ||||
| 		case 'g': | ||||
| 			config->gpsref = true; | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.clock_ref = REF_GPS; | ||||
| 			break; | ||||
| 		case 'f': | ||||
| 			config->filler = Transceiver::FILLER_DUMMY; | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.filler = FILLER_DUMMY; | ||||
| 			break; | ||||
| 		case 'o': | ||||
| 			config->offset = atof(optarg); | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.offset = atof(optarg); | ||||
| 			break; | ||||
| 		case 's': | ||||
| 			config->tx_sps = atoi(optarg); | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.tx_sps = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'b': | ||||
| 			config->rx_sps = atoi(optarg); | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.rx_sps = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'r': | ||||
| 			config->rtsc = atoi(optarg); | ||||
| 			config->filler = Transceiver::FILLER_NORM_RAND; | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.rtsc_set = true; | ||||
| 			trx->cfg.rtsc = atoi(optarg); | ||||
| 			if (!trx->cfg.egprs) /* Don't override egprs which sets different filler */ | ||||
| 				trx->cfg.filler = FILLER_NORM_RAND; | ||||
| 			break; | ||||
| 		case 'A': | ||||
| 			config->rach_delay = atoi(optarg); | ||||
| 			config->filler = Transceiver::FILLER_ACCESS_RAND; | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.rach_delay_set = true; | ||||
| 			trx->cfg.rach_delay = atoi(optarg); | ||||
| 			trx->cfg.filler = FILLER_ACCESS_RAND; | ||||
| 			break; | ||||
| 		case 'R': | ||||
| 			config->rssi_offset = atof(optarg); | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.rssi_offset = atof(optarg); | ||||
| 			break; | ||||
| 		case 'S': | ||||
| 			config->swap_channels = true; | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.swap_channels = true; | ||||
| 			break; | ||||
| 		case 'e': | ||||
| 			config->edge = true; | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.egprs = true; | ||||
| 			break; | ||||
| 		case 't': | ||||
| 			config->sched_rr = atoi(optarg); | ||||
| 			print_deprecated(option); | ||||
| 			trx->cfg.sched_rr = atoi(optarg); | ||||
| 			break; | ||||
| 		case 'y': | ||||
| 			config->tx_paths = comma_delimited_to_vector(optarg); | ||||
| 			tx_path_set = true; | ||||
| 			print_deprecated(option); | ||||
| 			tx_paths = comma_delimited_to_vector(optarg); | ||||
| 			tx_paths_set = true; | ||||
| 			break; | ||||
| 		case 'z': | ||||
| 			config->rx_paths = comma_delimited_to_vector(optarg); | ||||
| 			rx_path_set = true; | ||||
| 			print_deprecated(option); | ||||
| 			rx_paths = comma_delimited_to_vector(optarg); | ||||
| 			rx_paths_set = true; | ||||
| 			break; | ||||
| 		case 'C': | ||||
| 			config_file = optarg; | ||||
| 			break; | ||||
| 		case 'V': | ||||
| 			print_version(1); | ||||
| 			exit(0); | ||||
| 			break; | ||||
| 		default: | ||||
| 			print_help(); | ||||
| 			exit(0); | ||||
| 			goto bad_config; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Force 4 SPS for EDGE or multi-ARFCN configurations */ | ||||
| 	if ((config->edge) || (config->mcbts)) { | ||||
| 		config->tx_sps = 4; | ||||
| 		config->rx_sps = 4; | ||||
| 	} | ||||
| 	/* Cmd line option specific validation & setup */ | ||||
|  | ||||
| 	if (config->gpsref && config->extref) { | ||||
| 		printf("External and GPSDO references unavailable at the same time\n\n"); | ||||
| 	if (trx->cfg.num_chans > TRX_CHAN_MAX) { | ||||
| 		LOG(ERROR) << "Too many channels requested, maximum is " <<  TRX_CHAN_MAX; | ||||
| 		goto bad_config; | ||||
| 	} | ||||
|  | ||||
| 	if (config->edge && (config->filler == Transceiver::FILLER_NORM_RAND)) | ||||
| 		config->filler = Transceiver::FILLER_EDGE_RAND; | ||||
|  | ||||
| 	if ((config->tx_sps != 1) && (config->tx_sps != 4) && | ||||
| 	    (config->rx_sps != 1) && (config->rx_sps != 4)) { | ||||
| 		printf("Unsupported samples-per-symbol %i\n\n", config->tx_sps); | ||||
| 	if ((tx_paths_set && tx_paths.size() != trx->cfg.num_chans) || | ||||
| 	    (rx_paths_set && rx_paths.size() != trx->cfg.num_chans)) { | ||||
| 		LOG(ERROR) << "Num of channels and num of Rx/Tx Antennas doesn't match"; | ||||
| 		goto bad_config; | ||||
| 	} | ||||
|  | ||||
| 	if (config->rtsc > 7) { | ||||
| 		printf("Invalid training sequence %i\n\n", config->rtsc); | ||||
| 		goto bad_config; | ||||
| 	} | ||||
|  | ||||
| 	if (config->rach_delay > 68) { | ||||
| 		printf("RACH delay is too big %i\n\n", config->rach_delay); | ||||
| 		goto bad_config; | ||||
| 	} | ||||
|  | ||||
| 	if (!tx_path_set) { | ||||
| 		config->tx_paths = std::vector<std::string>(config->chans, ""); | ||||
| 	} else if (config->tx_paths.size() != config->chans) { | ||||
| 		printf("Num of channels and num of Tx Antennas doesn't match\n\n"); | ||||
| 		goto bad_config; | ||||
| 	} | ||||
| 	if (!rx_path_set) { | ||||
| 		config->rx_paths = std::vector<std::string>(config->chans, ""); | ||||
| 	} else if (config->rx_paths.size() != config->chans) { | ||||
| 		printf("Num of channels and num of Rx Antennas doesn't match\n\n"); | ||||
| 		goto bad_config; | ||||
| 	for (i = 0; i < trx->cfg.num_chans; i++) { | ||||
| 		trx->cfg.chans[i].trx = trx; | ||||
| 		trx->cfg.chans[i].idx = i; | ||||
| 		if (tx_paths_set) | ||||
| 			osmo_talloc_replace_string(trx, &trx->cfg.chans[i].tx_path, tx_paths[i].c_str()); | ||||
| 		if (rx_paths_set) | ||||
| 			osmo_talloc_replace_string(trx, &trx->cfg.chans[i].rx_path, rx_paths[i].c_str()); | ||||
| 	} | ||||
|  | ||||
| 	return; | ||||
| @@ -454,29 +361,141 @@ bad_config: | ||||
| 	exit(0); | ||||
| } | ||||
|  | ||||
| static int set_sched_rr(int prio) | ||||
| int trx_validate_config(struct trx_ctx *trx) | ||||
| { | ||||
| 	if (trx->cfg.multi_arfcn && trx->cfg.num_chans > 5) { | ||||
| 		LOG(ERROR) << "Unsupported number of channels"; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	/* Force 4 SPS for EDGE or multi-ARFCN configurations */ | ||||
| 	if ((trx->cfg.egprs || trx->cfg.multi_arfcn) && | ||||
| 	    (trx->cfg.tx_sps!=4 || trx->cfg.rx_sps!=4)) { | ||||
| 		LOG(ERROR) << "EDGE and Multi-Carrier options require 4 tx and rx sps. Check you config."; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static int set_sched_rr(unsigned int prio) | ||||
| { | ||||
| 	struct sched_param param; | ||||
| 	int rc; | ||||
| 	memset(¶m, 0, sizeof(param)); | ||||
| 	param.sched_priority = prio; | ||||
| 	printf("Setting SCHED_RR priority(%d)\n", param.sched_priority); | ||||
| 	LOG(INFO) << "Setting SCHED_RR priority " << param.sched_priority; | ||||
| 	rc = sched_setscheduler(getpid(), SCHED_RR, ¶m); | ||||
| 	if (rc != 0) { | ||||
| 		std::cerr << "Config: Setting SCHED_RR failed" << std::endl; | ||||
| 		LOG(ERROR) << "Config: Setting SCHED_RR failed"; | ||||
| 		return -1; | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| static void print_config(struct trx_ctx *trx) | ||||
| { | ||||
| 	unsigned int i; | ||||
| 	std::ostringstream ost(""); | ||||
|  | ||||
| 	ost << "Config Settings" << std::endl; | ||||
| 	ost << "   Log Level............... " << (unsigned int) osmo_stderr_target->loglevel << std::endl; | ||||
| 	ost << "   Device args............. " << charp2str(trx->cfg.dev_args) << std::endl; | ||||
| 	ost << "   TRX Base Port........... " << trx->cfg.base_port << std::endl; | ||||
| 	ost << "   TRX Address............. " << charp2str(trx->cfg.bind_addr) << std::endl; | ||||
| 	ost << "   GSM BTS Address......... " << charp2str(trx->cfg.remote_addr) << std::endl; | ||||
| 	ost << "   Channels................ " << trx->cfg.num_chans << std::endl; | ||||
| 	ost << "   Tx Samples-per-Symbol... " << trx->cfg.tx_sps << std::endl; | ||||
| 	ost << "   Rx Samples-per-Symbol... " << trx->cfg.rx_sps << std::endl; | ||||
| 	ost << "   EDGE support............ " << trx->cfg.egprs << std::endl; | ||||
| 	ost << "   Reference............... " << trx->cfg.clock_ref << std::endl; | ||||
| 	ost << "   C0 Filler Table......... " << trx->cfg.filler << std::endl; | ||||
| 	ost << "   Multi-Carrier........... " << trx->cfg.multi_arfcn << std::endl; | ||||
| 	ost << "   Tuning offset........... " << trx->cfg.offset << std::endl; | ||||
| 	ost << "   RSSI to dBm offset...... " << trx->cfg.rssi_offset << std::endl; | ||||
| 	ost << "   Swap channels........... " << trx->cfg.swap_channels << std::endl; | ||||
| 	ost << "   Tx Antennas............."; | ||||
| 	for (i = 0; i < trx->cfg.num_chans; i++) { | ||||
| 		std::string p = charp2str(trx->cfg.chans[i].tx_path); | ||||
| 		ost << " '" << ((p != "") ? p : "<default>") <<  "'"; | ||||
| 	} | ||||
| 	ost << std::endl; | ||||
| 	ost << "   Rx Antennas............."; | ||||
| 	for (i = 0; i < trx->cfg.num_chans; i++) { | ||||
| 		std::string p = charp2str(trx->cfg.chans[i].rx_path); | ||||
| 		ost << " '" << ((p != "") ? p : "<default>") <<  "'"; | ||||
| 	} | ||||
| 	ost << std::endl; | ||||
|  | ||||
| 	LOG(INFO) << ost << std::endl; | ||||
| } | ||||
|  | ||||
| static void trx_stop() | ||||
| { | ||||
| 	LOG(NOTICE) << "Shutting down transceiver..." << std::endl; | ||||
|  | ||||
| 	delete transceiver; | ||||
| 	delete radio; | ||||
| 	delete usrp; | ||||
| } | ||||
|  | ||||
| static int trx_start(struct trx_ctx *trx) | ||||
| { | ||||
| 	int type, chans; | ||||
| 	unsigned int i; | ||||
| 	std::vector<std::string> rx_paths, tx_paths; | ||||
| 	RadioDevice::InterfaceType iface = RadioDevice::NORMAL; | ||||
|  | ||||
| 	/* Create the low level device object */ | ||||
| 	if (trx->cfg.multi_arfcn) | ||||
| 		iface = RadioDevice::MULTI_ARFCN; | ||||
|  | ||||
| 	/* Generate vector of rx/tx_path: */ | ||||
| 	for (i = 0; i < trx->cfg.num_chans; i++) { | ||||
| 		rx_paths.push_back(charp2str(trx->cfg.chans[i].rx_path)); | ||||
| 		tx_paths.push_back(charp2str(trx->cfg.chans[i].tx_path)); | ||||
| 	} | ||||
|  | ||||
| 	usrp = RadioDevice::make(trx->cfg.tx_sps, trx->cfg.rx_sps, iface, | ||||
| 				 trx->cfg.num_chans, trx->cfg.offset, | ||||
| 				 tx_paths, rx_paths); | ||||
| 	type = usrp->open(charp2str(trx->cfg.dev_args), trx->cfg.clock_ref, trx->cfg.swap_channels); | ||||
| 	if (type < 0) { | ||||
| 		LOG(ALERT) << "Failed to create radio device" << std::endl; | ||||
| 		goto shutdown; | ||||
| 	} | ||||
|  | ||||
| 	/* Setup the appropriate device interface */ | ||||
| 	radio = makeRadioInterface(trx, usrp, type); | ||||
| 	if (!radio) | ||||
| 		goto shutdown; | ||||
|  | ||||
| 	/* Create the transceiver core */ | ||||
| 	if (makeTransceiver(trx, radio) < 0) | ||||
| 		goto shutdown; | ||||
|  | ||||
| 	chans = transceiver->numChans(); | ||||
| 	LOG(NOTICE) << "-- Transceiver active with " | ||||
| 		  << chans << " channel(s)" << std::endl; | ||||
|  | ||||
| 	return 0; | ||||
|  | ||||
| shutdown: | ||||
| 	trx_stop(); | ||||
| 	return -1; | ||||
| } | ||||
|  | ||||
| int main(int argc, char *argv[]) | ||||
| { | ||||
| 	int type, chans, ref; | ||||
| 	RadioDevice *usrp; | ||||
| 	RadioInterface *radio = NULL; | ||||
| 	Transceiver *trx = NULL; | ||||
| 	RadioDevice::InterfaceType iface = RadioDevice::NORMAL; | ||||
| 	struct trx_config config; | ||||
| 	int rc; | ||||
|  | ||||
| 	tall_trx_ctx = talloc_named_const(NULL, 0, "OsmoTRX"); | ||||
| 	msgb_talloc_ctx_init(tall_trx_ctx, 0); | ||||
| 	g_vty_info.tall_ctx = tall_trx_ctx; | ||||
|  | ||||
| 	setup_signal_handlers(); | ||||
|  | ||||
| 	g_trx_ctx = vty_trx_ctx_alloc(tall_trx_ctx); | ||||
|  | ||||
| #ifdef HAVE_SSE3 | ||||
| 	printf("Info: SSE3 support compiled in"); | ||||
| @@ -505,67 +524,68 @@ int main(int argc, char *argv[]) | ||||
| 	convolve_init(); | ||||
| 	convert_init(); | ||||
|  | ||||
| 	handle_options(argc, argv, &config); | ||||
| 	osmo_init_logging2(tall_trx_ctx, &log_info); | ||||
| 	osmo_stats_init(tall_trx_ctx); | ||||
| 	vty_init(&g_vty_info); | ||||
| 	ctrl_vty_init(tall_trx_ctx); | ||||
| 	trx_vty_init(g_trx_ctx); | ||||
|  | ||||
| 	if (config.sched_rr != -1) { | ||||
| 		if (set_sched_rr(config.sched_rr) < 0) | ||||
| 			return EXIT_FAILURE; | ||||
| 	logging_vty_add_cmds(); | ||||
| 	osmo_talloc_vty_add_cmds(); | ||||
| 	osmo_stats_vty_add_cmds(); | ||||
|  | ||||
| 	handle_options(argc, argv, g_trx_ctx); | ||||
|  | ||||
| 	rate_ctr_init(tall_trx_ctx); | ||||
|  | ||||
| 	rc = vty_read_config_file(config_file, NULL); | ||||
| 	if (rc < 0) { | ||||
| 		fprintf(stderr, "Failed to open config file: '%s'\n", config_file); | ||||
| 		exit(2); | ||||
| 	} | ||||
|  | ||||
| 	setup_signal_handlers(); | ||||
| 	rc = telnet_init_dynif(tall_trx_ctx, NULL, vty_get_bind_addr(), OSMO_VTY_PORT_TRX); | ||||
| 	if (rc < 0) | ||||
| 		exit(1); | ||||
|  | ||||
| 	/* Check database sanity */ | ||||
| 	if (!trx_setup_config(&config)) { | ||||
| 		std::cerr << "Config: Database failure - exiting" << std::endl; | ||||
| 	g_ctrlh = ctrl_interface_setup(NULL, OSMO_CTRL_PORT_TRX, NULL); | ||||
| 	if (!g_ctrlh) { | ||||
| 		LOG(ERROR) << "Failed to create CTRL interface.\n"; | ||||
| 		exit(1); | ||||
| 	} | ||||
|  | ||||
| 	/* Backward compatibility: Hack to have 1 channel allocated by default. | ||||
| 	 * Can be Dropped once we * get rid of "-c" cmdline param */ | ||||
| 	if (g_trx_ctx->cfg.num_chans == 0) { | ||||
| 		g_trx_ctx->cfg.num_chans = 1; | ||||
| 		g_trx_ctx->cfg.chans[0].trx = g_trx_ctx; | ||||
| 		g_trx_ctx->cfg.chans[0].idx = 0; | ||||
| 		LOG(ERROR) << "No explicit channel config found. Make sure you" \ | ||||
| 			" configure channels in VTY config. Using 1 channel as default," \ | ||||
| 			" but expect your config to break in the future."; | ||||
| 	} | ||||
|  | ||||
| 	print_config(g_trx_ctx); | ||||
|  | ||||
| 	if (trx_validate_config(g_trx_ctx) < 0) { | ||||
| 		LOG(ERROR) << "Config failure - exiting"; | ||||
| 		return EXIT_FAILURE; | ||||
| 	} | ||||
|  | ||||
| 	gLogInit(config.log_level.c_str()); | ||||
| 	if (g_trx_ctx->cfg.sched_rr) { | ||||
| 		if (set_sched_rr(g_trx_ctx->cfg.sched_rr) < 0) | ||||
| 			return EXIT_FAILURE; | ||||
| 	} | ||||
|  | ||||
| 	srandom(time(NULL)); | ||||
|  | ||||
| 	/* Create the low level device object */ | ||||
| 	if (config.mcbts) | ||||
| 		iface = RadioDevice::MULTI_ARFCN; | ||||
|  | ||||
| 	if (config.extref) | ||||
| 		ref = RadioDevice::REF_EXTERNAL; | ||||
| 	else if (config.gpsref) | ||||
| 		ref = RadioDevice::REF_GPS; | ||||
| 	else | ||||
| 		ref = RadioDevice::REF_INTERNAL; | ||||
|  | ||||
| 	usrp = RadioDevice::make(config.tx_sps, config.rx_sps, iface, | ||||
| 				 config.chans, config.offset, config.tx_paths, config.rx_paths); | ||||
| 	type = usrp->open(config.dev_args, ref, config.swap_channels); | ||||
| 	if (type < 0) { | ||||
| 		LOG(ALERT) << "Failed to create radio device" << std::endl; | ||||
| 		goto shutdown; | ||||
| 	} | ||||
|  | ||||
| 	/* Setup the appropriate device interface */ | ||||
| 	radio = makeRadioInterface(&config, usrp, type); | ||||
| 	if (!radio) | ||||
| 		goto shutdown; | ||||
|  | ||||
| 	/* Create the transceiver core */ | ||||
| 	trx = makeTransceiver(&config, radio); | ||||
| 	if (!trx) | ||||
| 		goto shutdown; | ||||
|  | ||||
| 	chans = trx->numChans(); | ||||
| 	std::cout << "-- Transceiver active with " | ||||
| 		  << chans << " channel(s)" << std::endl; | ||||
| 	if(trx_start(g_trx_ctx) < 0) | ||||
| 		return EXIT_FAILURE; | ||||
|  | ||||
| 	while (!gshutdown) | ||||
| 		sleep(1); | ||||
| 		osmo_select_main(0); | ||||
|  | ||||
| shutdown: | ||||
| 	std::cout << "Shutting down transceiver..." << std::endl; | ||||
|  | ||||
| 	delete trx; | ||||
| 	delete radio; | ||||
| 	delete usrp; | ||||
| 	trx_stop(); | ||||
|  | ||||
| 	return 0; | ||||
| } | ||||
|   | ||||
| @@ -75,6 +75,14 @@ bool RadioInterface::init(int type) | ||||
|  | ||||
| void RadioInterface::close() | ||||
| { | ||||
|   for (std::vector<RadioBuffer*>::iterator it = sendBuffer.begin(); it != sendBuffer.end(); ++it) | ||||
|           delete *it; | ||||
|   for (std::vector<RadioBuffer*>::iterator it = recvBuffer.begin(); it != recvBuffer.end(); ++it) | ||||
|           delete *it; | ||||
|   for (std::vector<short*>::iterator it = convertSendBuffer.begin(); it != convertSendBuffer.end(); ++it) | ||||
|           delete[] *it; | ||||
|   for (std::vector<short*>::iterator it = convertRecvBuffer.begin(); it != convertRecvBuffer.end(); ++it) | ||||
|           delete[] *it; | ||||
|   sendBuffer.resize(0); | ||||
|   recvBuffer.resize(0); | ||||
|   convertSendBuffer.resize(0); | ||||
| @@ -145,16 +153,32 @@ bool RadioInterface::tuneRx(double freq, size_t chan) | ||||
|   return mRadio->setRxFreq(freq, chan); | ||||
| } | ||||
|  | ||||
| /** synchronization thread loop */ | ||||
| void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface) | ||||
| { | ||||
|   set_selfthread_name("AlignRadio"); | ||||
|   while (1) { | ||||
|     sleep(60); | ||||
|     radioInterface->alignRadio(); | ||||
|     pthread_testcancel(); | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| void RadioInterface::alignRadio() { | ||||
|   mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000); | ||||
| } | ||||
|  | ||||
| bool RadioInterface::start() | ||||
| { | ||||
|   if (mOn) | ||||
|     return true; | ||||
|  | ||||
|   LOG(INFO) << "Starting radio device"; | ||||
| #ifdef USRP1 | ||||
|   mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter, | ||||
|                                      (void*)this); | ||||
| #endif | ||||
|   if (mRadio->requiresRadioAlign()) | ||||
|         mAlignRadioServiceLoopThread.start( | ||||
|                                 (void * (*)(void*))AlignRadioServiceLoopAdapter, | ||||
|                                 (void*)this); | ||||
|  | ||||
|   if (!mRadio->start()) | ||||
|     return false; | ||||
| @@ -191,22 +215,6 @@ bool RadioInterface::stop() | ||||
|   return true; | ||||
| } | ||||
|  | ||||
| #ifdef USRP1 | ||||
| void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface) | ||||
| { | ||||
|   while (1) { | ||||
|     radioInterface->alignRadio(); | ||||
|     pthread_testcancel(); | ||||
|   } | ||||
|   return NULL; | ||||
| } | ||||
|  | ||||
| void RadioInterface::alignRadio() { | ||||
|   sleep(60); | ||||
|   mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000); | ||||
| } | ||||
| #endif | ||||
|  | ||||
| void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts, | ||||
|                                         std::vector<bool> &zeros) | ||||
| { | ||||
| @@ -219,14 +227,15 @@ void RadioInterface::driveTransmitRadio(std::vector<signalVector *> &bursts, | ||||
|   while (pushBuffer()); | ||||
| } | ||||
|  | ||||
| bool RadioInterface::driveReceiveRadio() | ||||
| int RadioInterface::driveReceiveRadio() | ||||
| { | ||||
|   radioVector *burst = NULL; | ||||
|  | ||||
|   if (!mOn) | ||||
|     return false; | ||||
|     return 0; | ||||
|  | ||||
|   pullBuffer(); | ||||
|   if (pullBuffer() < 0) | ||||
|     return  -1; | ||||
|  | ||||
|   GSM::Time rcvClock = mClock.get(); | ||||
|   rcvClock.decTN(receiveOffset); | ||||
| @@ -240,11 +249,11 @@ bool RadioInterface::driveReceiveRadio() | ||||
|   else | ||||
|     burstSize = symbolsPerSlot + (tN % 4 == 0); | ||||
|  | ||||
|   /*  | ||||
|   /* | ||||
|    * Pre-allocate head room for the largest correlation size | ||||
|    * so we can later avoid a re-allocation and copy | ||||
|    * */ | ||||
|   size_t head = GSM::gRACHSynchSequence.size(); | ||||
|   size_t head = GSM::gRACHSynchSequenceTS0.size(); | ||||
|  | ||||
|   /* | ||||
|    * Form receive bursts and pass up to transceiver. Use repeating | ||||
| @@ -271,7 +280,7 @@ bool RadioInterface::driveReceiveRadio() | ||||
|       burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx; | ||||
|   } | ||||
|  | ||||
|   return true; | ||||
|   return 1; | ||||
| } | ||||
|  | ||||
| bool RadioInterface::isUnderrun() | ||||
| @@ -301,13 +310,14 @@ double RadioInterface::getRxGain(size_t chan) | ||||
| } | ||||
|  | ||||
| /* Receive a timestamped chunk from the device */ | ||||
| void RadioInterface::pullBuffer() | ||||
| int RadioInterface::pullBuffer() | ||||
| { | ||||
|   bool local_underrun; | ||||
|   size_t numRecv, segmentLen = recvBuffer[0]->getSegmentLen(); | ||||
|   int numRecv; | ||||
|   size_t segmentLen = recvBuffer[0]->getSegmentLen(); | ||||
|  | ||||
|   if (recvBuffer[0]->getFreeSegments() <= 0) | ||||
|     return; | ||||
|     return -1; | ||||
|  | ||||
|   /* Outer buffer access size is fixed */ | ||||
|   numRecv = mRadio->readSamples(convertRecvBuffer, | ||||
| @@ -316,9 +326,9 @@ void RadioInterface::pullBuffer() | ||||
|                                 readTimestamp, | ||||
|                                 &local_underrun); | ||||
|  | ||||
|   if (numRecv != segmentLen) { | ||||
|   if ((size_t) numRecv != segmentLen) { | ||||
|           LOG(ALERT) << "Receive error " << numRecv; | ||||
|           return; | ||||
|           return -1; | ||||
|   } | ||||
|  | ||||
|   for (size_t i = 0; i < mChans; i++) { | ||||
| @@ -329,6 +339,7 @@ void RadioInterface::pullBuffer() | ||||
|  | ||||
|   underrun |= local_underrun; | ||||
|   readTimestamp += numRecv; | ||||
|   return 0; | ||||
| } | ||||
|  | ||||
| /* Send timestamped chunk to the device with arbitrary size */ | ||||
|   | ||||
| @@ -14,7 +14,7 @@ | ||||
|  | ||||
|  | ||||
|  | ||||
| #include "sigProcLib.h"   | ||||
| #include "sigProcLib.h" | ||||
| #include "GSMCommon.h" | ||||
| #include "LinkedLists.h" | ||||
| #include "radioDevice.h" | ||||
| @@ -71,7 +71,7 @@ private: | ||||
|   virtual bool pushBuffer(void); | ||||
|  | ||||
|   /** pull GSM bursts from the receive buffer */ | ||||
|   virtual void pullBuffer(void); | ||||
|   virtual int pullBuffer(void); | ||||
|  | ||||
| public: | ||||
|  | ||||
| @@ -116,8 +116,8 @@ public: | ||||
|   void driveTransmitRadio(std::vector<signalVector *> &bursts, | ||||
|                           std::vector<bool> &zeros); | ||||
|  | ||||
|   /** drive reception of GSM bursts */ | ||||
|   bool driveReceiveRadio(); | ||||
|   /** drive reception of GSM bursts. -1: Error. 0: Radio off. 1: Received something. */ | ||||
|   int driveReceiveRadio(); | ||||
|  | ||||
|   int setPowerAttenuation(int atten, size_t chan = 0); | ||||
|  | ||||
| @@ -130,31 +130,26 @@ public: | ||||
|   /** set thread priority on current thread */ | ||||
|   void setPriority(float prio = 0.5) { mRadio->setPriority(prio); } | ||||
|  | ||||
|   /** get transport window type of attached device */  | ||||
|   /** get transport window type of attached device */ | ||||
|   enum RadioDevice::TxWindowType getWindowType() { return mRadio->getWindowType(); } | ||||
|  | ||||
| #if USRP1 | ||||
| protected: | ||||
|   /** Minimum latency that the device can achieve */ | ||||
|   GSM::Time minLatency()  { return mRadio->minLatency(); } | ||||
|  | ||||
| protected: | ||||
|   /** drive synchronization of Tx/Rx of USRP */ | ||||
|   void alignRadio(); | ||||
|  | ||||
|   friend void *AlignRadioServiceLoopAdapter(RadioInterface*); | ||||
| #endif | ||||
| }; | ||||
|  | ||||
| #if USRP1 | ||||
| /** synchronization thread loop */ | ||||
| void *AlignRadioServiceLoopAdapter(RadioInterface*); | ||||
| #endif | ||||
|  | ||||
| class RadioInterfaceResamp : public RadioInterface { | ||||
| private: | ||||
|   signalVector *outerSendBuffer; | ||||
|   signalVector *outerRecvBuffer; | ||||
|  | ||||
|   bool pushBuffer(); | ||||
|   void pullBuffer(); | ||||
|   int pullBuffer(); | ||||
|  | ||||
| public: | ||||
|   RadioInterfaceResamp(RadioDevice* wRadio, size_t tx_sps, size_t rx_sps); | ||||
| @@ -167,7 +162,7 @@ public: | ||||
| class RadioInterfaceMulti : public RadioInterface { | ||||
| private: | ||||
|   bool pushBuffer(); | ||||
|   void pullBuffer(); | ||||
|   int pullBuffer(); | ||||
|  | ||||
|   signalVector *outerSendBuffer; | ||||
|   signalVector *outerRecvBuffer; | ||||
|   | ||||
| @@ -1,7 +1,7 @@ | ||||
| /* | ||||
|  * Multi-carrier radio interface | ||||
|  * | ||||
|  * Copyright (C) 2016 Ettus Research LLC  | ||||
|  * Copyright (C) 2016 Ettus Research LLC | ||||
|  * | ||||
|  * Author: Tom Tsou <tom.tsou@ettus.com> | ||||
|  * | ||||
| @@ -225,14 +225,15 @@ bool RadioInterfaceMulti::init(int type) | ||||
| } | ||||
|  | ||||
| /* Receive a timestamped chunk from the device */ | ||||
| void RadioInterfaceMulti::pullBuffer() | ||||
| int RadioInterfaceMulti::pullBuffer() | ||||
| { | ||||
| 	bool local_underrun; | ||||
| 	size_t num; | ||||
| 	float *buf; | ||||
| 	unsigned int i; | ||||
|  | ||||
| 	if (recvBuffer[0]->getFreeSegments() <= 0) | ||||
| 		return; | ||||
| 		return -1; | ||||
|  | ||||
| 	/* Outer buffer access size is fixed */ | ||||
| 	num = mRadio->readSamples(convertRecvBuffer, | ||||
| @@ -242,7 +243,7 @@ void RadioInterfaceMulti::pullBuffer() | ||||
| 				  &local_underrun); | ||||
| 	if (num != channelizer->inputLen()) { | ||||
| 		LOG(ALERT) << "Receive error " << num << ", " << channelizer->inputLen(); | ||||
| 		return; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	convert_short_float((float *) outerRecvBuffer->begin(), | ||||
| @@ -273,10 +274,22 @@ void RadioInterfaceMulti::pullBuffer() | ||||
| 		buf = channelizer->outputBuffer(pchan); | ||||
| 		size_t cLen = channelizer->outputLen(); | ||||
| 		size_t hLen = dnsampler->len(); | ||||
| 		size_t hSize = 2 * hLen * sizeof(float); | ||||
|  | ||||
| 		memcpy(&buf[2 * -hLen], history[lchan]->begin(), hSize); | ||||
| 		memcpy(history[lchan]->begin(), &buf[2 * (cLen - hLen)], hSize); | ||||
| 		float *fdst = &buf[2 * -hLen]; | ||||
| 		complex *src = history[lchan]->begin(); | ||||
| 		for (i = 0; i < hLen; i++) { | ||||
| 			fdst[0] = src->real(); | ||||
| 			fdst[1] = src->imag(); | ||||
| 			src++; | ||||
| 			fdst += 2; | ||||
| 		} | ||||
| 		complex *dst = history[lchan]->begin(); | ||||
| 		float *fsrc = &buf[2 * (cLen - hLen)]; | ||||
| 		for (i = 0; i < hLen; i++) { | ||||
| 			*dst = complex(fdst[0], fdst[1]); | ||||
| 			fsrc += 2; | ||||
| 			dst++; | ||||
| 		} | ||||
|  | ||||
| 		float *wr_segment = recvBuffer[lchan]->getWriteSegment(); | ||||
|  | ||||
| @@ -288,6 +301,7 @@ void RadioInterfaceMulti::pullBuffer() | ||||
| 			LOG(ALERT) << "Sample rate upsampling error"; | ||||
| 		} | ||||
| 	} | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Send a timestamped chunk to the device */ | ||||
|   | ||||
| @@ -160,13 +160,13 @@ bool RadioInterfaceResamp::init(int type) | ||||
| } | ||||
|  | ||||
| /* Receive a timestamped chunk from the device */ | ||||
| void RadioInterfaceResamp::pullBuffer() | ||||
| int RadioInterfaceResamp::pullBuffer() | ||||
| { | ||||
| 	bool local_underrun; | ||||
| 	int rc, num_recv; | ||||
|  | ||||
| 	if (recvBuffer[0]->getFreeSegments() <= 0) | ||||
| 		return; | ||||
| 		return -1; | ||||
|  | ||||
| 	/* Outer buffer access size is fixed */ | ||||
| 	num_recv = mRadio->readSamples(convertRecvBuffer, | ||||
| @@ -176,7 +176,7 @@ void RadioInterfaceResamp::pullBuffer() | ||||
| 				       &local_underrun); | ||||
| 	if (num_recv != (int) resamp_outchunk) { | ||||
| 		LOG(ALERT) << "Receive error " << num_recv; | ||||
| 		return; | ||||
| 		return -1; | ||||
| 	} | ||||
|  | ||||
| 	convert_short_float((float *) outerRecvBuffer->begin(), | ||||
| @@ -196,6 +196,7 @@ void RadioInterfaceResamp::pullBuffer() | ||||
|  | ||||
| 	/* Set history for the next chunk */ | ||||
| 	outerRecvBuffer->updateHistory(); | ||||
| 	return 0; | ||||
| } | ||||
|  | ||||
| /* Send a timestamped chunk to the device */ | ||||
|   | ||||
| @@ -84,14 +84,13 @@ static Resampler *dnsampler = NULL; | ||||
|  * perform 16-byte memory alignment required by many SSE instructions. | ||||
|  */ | ||||
| struct CorrelationSequence { | ||||
|   CorrelationSequence() : sequence(NULL), buffer(NULL) | ||||
|   CorrelationSequence() : sequence(NULL) | ||||
|   { | ||||
|   } | ||||
|  | ||||
|   ~CorrelationSequence() | ||||
|   { | ||||
|     delete sequence; | ||||
|     free(buffer); | ||||
|   } | ||||
|  | ||||
|   signalVector *sequence; | ||||
| @@ -106,8 +105,7 @@ struct CorrelationSequence { | ||||
|  * for SSE instructions. | ||||
|  */ | ||||
| struct PulseSequence { | ||||
|   PulseSequence() : c0(NULL), c1(NULL), c0_inv(NULL), empty(NULL), | ||||
| 		    c0_buffer(NULL), c1_buffer(NULL), c0_inv_buffer(NULL) | ||||
|   PulseSequence() : c0(NULL), c1(NULL), c0_inv(NULL), empty(NULL) | ||||
|   { | ||||
|   } | ||||
|  | ||||
| @@ -117,22 +115,17 @@ struct PulseSequence { | ||||
|     delete c1; | ||||
|     delete c0_inv; | ||||
|     delete empty; | ||||
|     free(c0_buffer); | ||||
|     free(c1_buffer); | ||||
|   } | ||||
|  | ||||
|   signalVector *c0; | ||||
|   signalVector *c1; | ||||
|   signalVector *c0_inv; | ||||
|   signalVector *empty; | ||||
|   void *c0_buffer; | ||||
|   void *c1_buffer; | ||||
|   void *c0_inv_buffer; | ||||
| }; | ||||
|  | ||||
| static CorrelationSequence *gMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; | ||||
| static CorrelationSequence *gEdgeMidambles[] = {NULL,NULL,NULL,NULL,NULL,NULL,NULL,NULL}; | ||||
| static CorrelationSequence *gRACHSequence = NULL; | ||||
| static CorrelationSequence *gRACHSequences[] = {NULL,NULL,NULL}; | ||||
| static PulseSequence *GSMPulse1 = NULL; | ||||
| static PulseSequence *GSMPulse4 = NULL; | ||||
|  | ||||
| @@ -150,11 +143,15 @@ void sigProcLibDestroy() | ||||
|     delayFilters[i] = NULL; | ||||
|   } | ||||
|  | ||||
|   for (int i = 0; i < 3; i++) { | ||||
|     delete gRACHSequences[i]; | ||||
|     gRACHSequences[i] = NULL; | ||||
|   } | ||||
|  | ||||
|   delete GMSKRotation1; | ||||
|   delete GMSKReverseRotation1; | ||||
|   delete GMSKRotation4; | ||||
|   delete GMSKReverseRotation4; | ||||
|   delete gRACHSequence; | ||||
|   delete GSMPulse1; | ||||
|   delete GSMPulse4; | ||||
|   delete dnsampler; | ||||
| @@ -163,7 +160,6 @@ void sigProcLibDestroy() | ||||
|   GMSKRotation4 = NULL; | ||||
|   GMSKReverseRotation4 = NULL; | ||||
|   GMSKReverseRotation1 = NULL; | ||||
|   gRACHSequence = NULL; | ||||
|   GSMPulse1 = NULL; | ||||
|   GSMPulse4 = NULL; | ||||
| } | ||||
| @@ -337,7 +333,7 @@ static signalVector *convolve(const signalVector *x, const signalVector *h, | ||||
|   if (y && (len > y->size())) | ||||
|     return NULL; | ||||
|   if (!y) { | ||||
|     y = new signalVector(len); | ||||
|     y = new signalVector(len, convolve_h_alloc, free); | ||||
|     alloc = true; | ||||
|   } | ||||
|  | ||||
| @@ -400,8 +396,7 @@ static bool generateInvertC0Pulse(PulseSequence *pulse) | ||||
|   if (!pulse) | ||||
|     return false; | ||||
|  | ||||
|   pulse->c0_inv_buffer = convolve_h_alloc(5); | ||||
|   pulse->c0_inv = new signalVector((complex *) pulse->c0_inv_buffer, 0, 5); | ||||
|   pulse->c0_inv = new signalVector((complex *) convolve_h_alloc(5), 0, 5, convolve_h_alloc, free); | ||||
|   pulse->c0_inv->isReal(true); | ||||
|   pulse->c0_inv->setAligned(false); | ||||
|  | ||||
| @@ -430,9 +425,7 @@ static bool generateC1Pulse(int sps, PulseSequence *pulse) | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   pulse->c1_buffer = convolve_h_alloc(len); | ||||
|   pulse->c1 = new signalVector((complex *) | ||||
|                                   pulse->c1_buffer, 0, len); | ||||
|   pulse->c1 = new signalVector((complex *) convolve_h_alloc(len), 0, len, convolve_h_alloc, free); | ||||
|   pulse->c1->isReal(true); | ||||
|  | ||||
|   /* Enable alignment for SSE usage */ | ||||
| @@ -486,8 +479,7 @@ static PulseSequence *generateGSMPulse(int sps) | ||||
|     len = 4; | ||||
|   } | ||||
|  | ||||
|   pulse->c0_buffer = convolve_h_alloc(len); | ||||
|   pulse->c0 = new signalVector((complex *) pulse->c0_buffer, 0, len); | ||||
|   pulse->c0 = new signalVector((complex *) convolve_h_alloc(len), 0, len, convolve_h_alloc, free); | ||||
|   pulse->c0->isReal(true); | ||||
|  | ||||
|   /* Enable alingnment for SSE usage */ | ||||
| @@ -1016,7 +1008,7 @@ static void generateDelayFilters() | ||||
|  | ||||
|   for (int i = 0; i < DELAYFILTS; i++) { | ||||
|     data = (complex *) convolve_h_alloc(h_len); | ||||
|     h = new signalVector(data, 0, h_len); | ||||
|     h = new signalVector(data, 0, h_len, convolve_h_alloc, free); | ||||
|     h->setAligned(true); | ||||
|     h->isReal(true); | ||||
|  | ||||
| @@ -1102,17 +1094,17 @@ static complex interpolatePoint(const signalVector &inSig, float ix) | ||||
|   if (start < 0) start = 0; | ||||
|   int end = (int) (floor(ix) + 11); | ||||
|   if ((unsigned) end > inSig.size()-1) end = inSig.size()-1; | ||||
|    | ||||
|  | ||||
|   complex pVal = 0.0; | ||||
|   if (!inSig.isReal()) { | ||||
|     for (int i = start; i < end; i++)  | ||||
|     for (int i = start; i < end; i++) | ||||
|       pVal += inSig[i] * sinc(M_PI_F*(i-ix)); | ||||
|   } | ||||
|   else { | ||||
|     for (int i = start; i < end; i++)  | ||||
|     for (int i = start; i < end; i++) | ||||
|       pVal += inSig[i].real() * sinc(M_PI_F*(i-ix)); | ||||
|   } | ||||
|     | ||||
|  | ||||
|   return pVal; | ||||
| } | ||||
|  | ||||
| @@ -1157,12 +1149,12 @@ static complex peakDetect(const signalVector &rxBurst, | ||||
|   // to save computation, we'll use early-late balancing | ||||
|   float earlyIndex = maxIndex-1; | ||||
|   float lateIndex = maxIndex+1; | ||||
|    | ||||
|  | ||||
|   float incr = 0.5; | ||||
|   while (incr > 1.0/1024.0) { | ||||
|     complex earlyP = interpolatePoint(rxBurst,earlyIndex); | ||||
|     complex lateP =  interpolatePoint(rxBurst,lateIndex); | ||||
|     if (earlyP < lateP)  | ||||
|     if (earlyP < lateP) | ||||
|       earlyIndex += incr; | ||||
|     else if (earlyP > lateP) | ||||
|       earlyIndex -= incr; | ||||
| @@ -1250,7 +1242,7 @@ static bool generateMidamble(int sps, int tsc) | ||||
|  | ||||
|   // NOTE: Because ideal TSC 16-bit midamble is 66 symbols into burst, | ||||
|   //       the ideal TSC has an + 180 degree phase shift, | ||||
|   //       due to the pi/2 frequency shift, that  | ||||
|   //       due to the pi/2 frequency shift, that | ||||
|   //       needs to be accounted for. | ||||
|   //       26-midamble is 61 symbols into burst, has +90 degree phase shift. | ||||
|   scaleVector(*midMidamble, complex(-1.0, 0.0)); | ||||
| @@ -1260,10 +1252,9 @@ static bool generateMidamble(int sps, int tsc) | ||||
|  | ||||
|   /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */ | ||||
|   data = (complex *) convolve_h_alloc(midMidamble->size()); | ||||
|   _midMidamble = new signalVector(data, 0, midMidamble->size()); | ||||
|   _midMidamble = new signalVector(data, 0, midMidamble->size(), convolve_h_alloc, free); | ||||
|   _midMidamble->setAligned(true); | ||||
|   memcpy(_midMidamble->begin(), midMidamble->begin(), | ||||
| 	 midMidamble->size() * sizeof(complex)); | ||||
|   midMidamble->copyTo(*_midMidamble); | ||||
|  | ||||
|   autocorr = convolve(midamble, _midMidamble, NULL, NO_DELAY); | ||||
|   if (!autocorr) { | ||||
| @@ -1272,7 +1263,6 @@ static bool generateMidamble(int sps, int tsc) | ||||
|   } | ||||
|  | ||||
|   gMidambles[tsc] = new CorrelationSequence; | ||||
|   gMidambles[tsc]->buffer = data; | ||||
|   gMidambles[tsc]->sequence = _midMidamble; | ||||
|   gMidambles[tsc]->gain = peakDetect(*autocorr, &toa, NULL); | ||||
|  | ||||
| @@ -1317,14 +1307,12 @@ static CorrelationSequence *generateEdgeMidamble(int tsc) | ||||
|   conjugateVector(*midamble); | ||||
|  | ||||
|   data = (complex *) convolve_h_alloc(midamble->size()); | ||||
|   _midamble = new signalVector(data, 0, midamble->size()); | ||||
|   _midamble = new signalVector(data, 0, midamble->size(), convolve_h_alloc, free); | ||||
|   _midamble->setAligned(true); | ||||
|   memcpy(_midamble->begin(), midamble->begin(), | ||||
| 	 midamble->size() * sizeof(complex)); | ||||
|   midamble->copyTo(*_midamble); | ||||
|  | ||||
|   /* Channel gain is an empirically measured value */ | ||||
|   seq = new CorrelationSequence; | ||||
|   seq->buffer = data; | ||||
|   seq->sequence = _midamble; | ||||
|   seq->gain = Complex<float>(-19.6432, 19.5006) / 1.18; | ||||
|   seq->toa = 0; | ||||
| @@ -1334,7 +1322,7 @@ static CorrelationSequence *generateEdgeMidamble(int tsc) | ||||
|   return seq; | ||||
| } | ||||
|  | ||||
| static bool generateRACHSequence(int sps) | ||||
| static bool generateRACHSequence(CorrelationSequence **seq, const BitVector &bv, int sps) | ||||
| { | ||||
|   bool status = true; | ||||
|   float toa; | ||||
| @@ -1342,13 +1330,14 @@ static bool generateRACHSequence(int sps) | ||||
|   signalVector *autocorr = NULL; | ||||
|   signalVector *seq0 = NULL, *seq1 = NULL, *_seq1 = NULL; | ||||
|  | ||||
|   delete gRACHSequence; | ||||
|   if (*seq != NULL) | ||||
|     delete *seq; | ||||
|  | ||||
|   seq0 = modulateBurst(gRACHSynchSequence, 0, sps, false); | ||||
|   seq0 = modulateBurst(bv, 0, sps, false); | ||||
|   if (!seq0) | ||||
|     return false; | ||||
|  | ||||
|   seq1 = modulateBurst(gRACHSynchSequence.segment(0, 40), 0, sps, true); | ||||
|   seq1 = modulateBurst(bv.segment(0, 40), 0, sps, true); | ||||
|   if (!seq1) { | ||||
|     status = false; | ||||
|     goto release; | ||||
| @@ -1358,9 +1347,9 @@ static bool generateRACHSequence(int sps) | ||||
|  | ||||
|   /* For SSE alignment, reallocate the midamble sequence on 16-byte boundary */ | ||||
|   data = (complex *) convolve_h_alloc(seq1->size()); | ||||
|   _seq1 = new signalVector(data, 0, seq1->size()); | ||||
|   _seq1 = new signalVector(data, 0, seq1->size(), convolve_h_alloc, free); | ||||
|   _seq1->setAligned(true); | ||||
|   memcpy(_seq1->begin(), seq1->begin(), seq1->size() * sizeof(complex)); | ||||
|   seq1->copyTo(*_seq1); | ||||
|  | ||||
|   autocorr = convolve(seq0, _seq1, autocorr, NO_DELAY); | ||||
|   if (!autocorr) { | ||||
| @@ -1368,19 +1357,18 @@ static bool generateRACHSequence(int sps) | ||||
|     goto release; | ||||
|   } | ||||
|  | ||||
|   gRACHSequence = new CorrelationSequence; | ||||
|   gRACHSequence->sequence = _seq1; | ||||
|   gRACHSequence->buffer = data; | ||||
|   gRACHSequence->gain = peakDetect(*autocorr, &toa, NULL); | ||||
|   *seq = new CorrelationSequence; | ||||
|   (*seq)->sequence = _seq1; | ||||
|   (*seq)->gain = peakDetect(*autocorr, &toa, NULL); | ||||
|  | ||||
|   /* For 1 sps only | ||||
|    *     (Half of correlation length - 1) + midpoint of pulse shaping filer | ||||
|    *     20.5 = (40 / 2 - 1) + 1.5 | ||||
|    */ | ||||
|   if (sps == 1) | ||||
|     gRACHSequence->toa = toa - 20.5; | ||||
|     (*seq)->toa = toa - 20.5; | ||||
|   else | ||||
|     gRACHSequence->toa = 0.0; | ||||
|     (*seq)->toa = 0.0; | ||||
|  | ||||
| release: | ||||
|   delete autocorr; | ||||
| @@ -1390,7 +1378,7 @@ release: | ||||
|   if (!status) { | ||||
|     delete _seq1; | ||||
|     free(data); | ||||
|     gRACHSequence = NULL; | ||||
|     *seq = NULL; | ||||
|   } | ||||
|  | ||||
|   return status; | ||||
| @@ -1457,7 +1445,7 @@ static signalVector *downsampleBurst(const signalVector &burst) | ||||
| { | ||||
|   signalVector in(DOWNSAMPLE_IN_LEN, dnsampler->len()); | ||||
|   signalVector *out = new signalVector(DOWNSAMPLE_OUT_LEN); | ||||
|   memcpy(in.begin(), burst.begin(), DOWNSAMPLE_IN_LEN * 2 * sizeof(float)); | ||||
|   burst.copyToSegment(in, 0, DOWNSAMPLE_IN_LEN); | ||||
|  | ||||
|   if (dnsampler->rotate((float *) in.begin(), DOWNSAMPLE_IN_LEN, | ||||
|                         (float *) out->begin(), DOWNSAMPLE_OUT_LEN) < 0) { | ||||
| @@ -1591,7 +1579,7 @@ static int detectGeneralBurst(const signalVector &rxBurst, | ||||
| } | ||||
|  | ||||
|  | ||||
| /*  | ||||
| /* | ||||
|  * RACH burst detection | ||||
|  * | ||||
|  * Correlation window parameters: | ||||
| @@ -1600,23 +1588,27 @@ static int detectGeneralBurst(const signalVector &rxBurst, | ||||
|  *   tail: Search 8 symbols + maximum expected delay | ||||
|  */ | ||||
| static int detectRACHBurst(const signalVector &burst, float threshold, int sps, | ||||
|                            complex &litude, float &toa, unsigned max_toa) | ||||
|                            complex &litude, float &toa, unsigned max_toa, bool ext) | ||||
| { | ||||
|   int rc, target, head, tail; | ||||
|   CorrelationSequence *sync; | ||||
|   int i, num_seq; | ||||
|  | ||||
|   target = 8 + 40; | ||||
|   head = 8; | ||||
|   tail = 8 + max_toa; | ||||
|   sync = gRACHSequence; | ||||
|   num_seq = ext ? 3 : 1; | ||||
|  | ||||
|   rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa, | ||||
|                           target, head, tail, sync); | ||||
|   for (i = 0; i < num_seq; i++) { | ||||
|     rc = detectGeneralBurst(burst, threshold, sps, amplitude, toa, | ||||
|                             target, head, tail, gRACHSequences[i]); | ||||
|     if (rc > 0) | ||||
|       break; | ||||
|   } | ||||
|  | ||||
|   return rc; | ||||
| } | ||||
|  | ||||
| /*  | ||||
| /* | ||||
|  * Normal burst detection | ||||
|  * | ||||
|  * Correlation window parameters: | ||||
| @@ -1680,9 +1672,10 @@ int detectAnyBurst(const signalVector &burst, unsigned tsc, float threshold, | ||||
|     rc = analyzeTrafficBurst(burst, tsc, threshold, sps, | ||||
|                              amp, toa, max_toa); | ||||
|     break; | ||||
|   case EXT_RACH: | ||||
|   case RACH: | ||||
|     rc = detectRACHBurst(burst, threshold, sps, amp, toa, | ||||
|                          max_toa); | ||||
|                          max_toa, type == EXT_RACH); | ||||
|     break; | ||||
|   default: | ||||
|     LOG(ERR) << "Invalid correlation type"; | ||||
| @@ -1860,7 +1853,10 @@ bool sigProcLibSetup() | ||||
|   GSMPulse1 = generateGSMPulse(1); | ||||
|   GSMPulse4 = generateGSMPulse(4); | ||||
|  | ||||
|   generateRACHSequence(1); | ||||
|   generateRACHSequence(&gRACHSequences[0], gRACHSynchSequenceTS0, 1); | ||||
|   generateRACHSequence(&gRACHSequences[1], gRACHSynchSequenceTS1, 1); | ||||
|   generateRACHSequence(&gRACHSequences[2], gRACHSynchSequenceTS2, 1); | ||||
|  | ||||
|   for (int tsc = 0; tsc < 8; tsc++) { | ||||
|     generateMidamble(1, tsc); | ||||
|     gEdgeMidambles[tsc] = generateEdgeMidamble(tsc); | ||||
|   | ||||
| @@ -29,6 +29,7 @@ | ||||
| enum CorrType{ | ||||
|   OFF,         ///< timeslot is off | ||||
|   TSC,         ///< timeslot should contain a normal burst | ||||
|   EXT_RACH,    ///< timeslot should contain an extended access burst | ||||
|   RACH,        ///< timeslot should contain an access burst | ||||
|   EDGE,        ///< timeslot should contain an EDGE burst | ||||
|   IDLE         ///< timeslot is an idle (or dummy) burst | ||||
|   | ||||
| @@ -1,20 +1,20 @@ | ||||
| #include "signalVector.h" | ||||
|  | ||||
| signalVector::signalVector(size_t size) | ||||
| 	: Vector<complex>(size), | ||||
| signalVector::signalVector(size_t size, vector_alloc_func wAllocFunc, vector_free_func wFreeFunc) | ||||
| 	: Vector<complex>(size, wAllocFunc, wFreeFunc), | ||||
| 	  real(false), aligned(false), symmetry(NONE) | ||||
| { | ||||
| } | ||||
|  | ||||
| signalVector::signalVector(size_t size, size_t start) | ||||
| 	: Vector<complex>(size + start), | ||||
| signalVector::signalVector(size_t size, size_t start, vector_alloc_func wAllocFunc, vector_free_func wFreeFunc) | ||||
| 	: Vector<complex>(size + start, wAllocFunc, wFreeFunc), | ||||
| 	  real(false), aligned(false), symmetry(NONE) | ||||
| { | ||||
| 	mStart = mData + start; | ||||
| } | ||||
|  | ||||
| signalVector::signalVector(complex *data, size_t start, size_t span) | ||||
| 	: Vector<complex>(NULL, data + start, data + start + span), | ||||
| signalVector::signalVector(complex *data, size_t start, size_t span, vector_alloc_func wAllocFunc, vector_free_func wFreeFunc) | ||||
| 	: Vector<complex>(data, data + start, data + start + span, wAllocFunc, wFreeFunc), | ||||
| 	  real(false), aligned(false), symmetry(NONE) | ||||
| { | ||||
| } | ||||
| @@ -41,7 +41,14 @@ signalVector::signalVector(const signalVector &vector, | ||||
| void signalVector::operator=(const signalVector& vector) | ||||
| { | ||||
| 	resize(vector.size() + vector.getStart()); | ||||
| 	memcpy(mData, vector.mData, bytes()); | ||||
|  | ||||
| 	unsigned int i; | ||||
| 	complex *dst = mData; | ||||
| 	complex *src = vector.mData; | ||||
| 	for (i = 0; i < size(); i++, src++, dst++) | ||||
| 		*dst = *src; | ||||
| 	/* TODO: optimize for non non-trivially copyable types: */ | ||||
| 	/*memcpy(mData, vector.mData, bytes()); */ | ||||
| 	mStart = mData + vector.getStart(); | ||||
| } | ||||
|  | ||||
| @@ -58,8 +65,13 @@ size_t signalVector::getStart() const | ||||
| size_t signalVector::updateHistory() | ||||
| { | ||||
| 	size_t num = getStart(); | ||||
|  | ||||
| 	memmove(mData, mStart + this->size() - num, num * sizeof(complex)); | ||||
| 	unsigned int i; | ||||
| 	complex *dst = mData; | ||||
| 	complex *src = mStart + this->size() - num; | ||||
| 	for (i = 0; i < num; i++, src++, dst++) | ||||
| 		*dst = *src; | ||||
| 	/* TODO: optimize for non non-trivially copyable types: */ | ||||
| 	/*memmove(mData, mStart + this->size() - num, num * sizeof(complex)); */ | ||||
|  | ||||
| 	return num; | ||||
| } | ||||
|   | ||||
| @@ -13,13 +13,13 @@ enum Symmetry { | ||||
| class signalVector: public Vector<complex> { | ||||
| public: | ||||
| 	/** Default constructor */ | ||||
| 	signalVector(size_t size = 0); | ||||
| 	signalVector(size_t size = 0, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL); | ||||
|  | ||||
| 	/** Construct with head room */ | ||||
| 	signalVector(size_t size, size_t start); | ||||
| 	signalVector(size_t size, size_t start, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL); | ||||
|  | ||||
| 	/** Construct from existing buffer data (buffer not managed) */ | ||||
| 	signalVector(complex *data, size_t start, size_t span); | ||||
| 	signalVector(complex *data, size_t start, size_t span, vector_alloc_func wAllocFunc = NULL, vector_free_func wFreeFunc = NULL); | ||||
|  | ||||
| 	/** Construct by from existing vector */ | ||||
| 	signalVector(const signalVector &vector); | ||||
|   | ||||
							
								
								
									
										157
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										157
									
								
								configure.ac
									
									
									
									
									
								
							| @@ -19,7 +19,7 @@ dnl along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||
| dnl | ||||
|  | ||||
| AC_INIT([osmo-trx], | ||||
| 	m4_esyscmd([./git-version-gen .tarball-veresion]), | ||||
| 	m4_esyscmd([./git-version-gen .tarball-version]), | ||||
| 	[openbsc@lists.osmocom.org]) | ||||
| AC_PREREQ(2.57) | ||||
| AC_CONFIG_SRCDIR([Transceiver52M/Makefile.am]) | ||||
| @@ -42,12 +42,14 @@ RELMAKE='-include osmo-release.mk' | ||||
| AC_SUBST([RELMAKE]) | ||||
|  | ||||
| AM_PROG_AS | ||||
| AC_PROG_CC | ||||
| AC_PROG_CXX | ||||
| AX_CXX_COMPILE_STDCXX_11 | ||||
| AC_PROG_LN_S | ||||
| AC_PROG_MAKE_SET | ||||
| AC_PROG_INSTALL | ||||
| AC_PATH_PROG([RM_PROG], [rm]) | ||||
| AC_LANG([C++]) | ||||
|  | ||||
| dnl check for pkg-config (explained in detail in libosmocore/configure.ac) | ||||
| AC_PATH_PROG(PKG_CONFIG_INSTALLED, pkg-config, no) | ||||
| @@ -73,11 +75,56 @@ AC_TYPE_SIZE_T | ||||
| AC_HEADER_TIME | ||||
| AC_C_BIGENDIAN | ||||
|  | ||||
| PKG_CHECK_MODULES(LIBOSMOCORE, libosmocore >= 0.11.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOVTY, libosmovty >= 0.11.0) | ||||
| PKG_CHECK_MODULES(LIBOSMOCTRL, libosmoctrl >= 0.11.0) | ||||
|  | ||||
| AC_ARG_ENABLE(sanitize, | ||||
| 	[AS_HELP_STRING( | ||||
| 		[--enable-sanitize], | ||||
| 		[Compile with address sanitizer enabled], | ||||
| 	)], | ||||
| 	[sanitize=$enableval], [sanitize="no"]) | ||||
| if test x"$sanitize" = x"yes" | ||||
| then | ||||
| 	CPPFLAGS="$CPPFLAGS -fsanitize=address -fsanitize=undefined" | ||||
| 	LDFLAGS="$LDFLAGS -fsanitize=address -fsanitize=undefined" | ||||
| fi | ||||
|  | ||||
| AC_ARG_ENABLE(werror, | ||||
| 	[AS_HELP_STRING( | ||||
| 		[--enable-werror], | ||||
| 		[Turn all compiler warnings into errors, with exceptions: | ||||
| 		 a) deprecation (allow upstream to mark deprecation without breaking builds); | ||||
| 		 b) "#warning" pragmas (allow to remind ourselves of errors without breaking builds) | ||||
| 		] | ||||
| 	)], | ||||
| 	[werror=$enableval], [werror="no"]) | ||||
| if test x"$werror" = x"yes" | ||||
| then | ||||
| 	WERROR_FLAGS="-Werror" | ||||
| 	WERROR_FLAGS+=" -Wno-error=deprecated -Wno-error=deprecated-declarations" | ||||
| 	WERROR_FLAGS+=" -Wno-error=cpp" # "#warning" | ||||
| 	CFLAGS="$CFLAGS $WERROR_FLAGS" | ||||
| 	CPPFLAGS="$CPPFLAGS $WERROR_FLAGS" | ||||
| fi | ||||
|  | ||||
|  | ||||
| AC_ARG_WITH(uhd, [ | ||||
|     AS_HELP_STRING([--with-uhd], | ||||
|         [enable UHD based transceiver]) | ||||
| ]) | ||||
|  | ||||
| AC_ARG_WITH(usrp1, [ | ||||
|     AS_HELP_STRING([--with-usrp1], | ||||
|         [enable USRP1 gnuradio based transceiver]) | ||||
| ]) | ||||
|  | ||||
| AC_ARG_WITH(lms, [ | ||||
|     AS_HELP_STRING([--with-lms], | ||||
|         [enable LimeSuite based transceiver]) | ||||
| ]) | ||||
|  | ||||
| AC_ARG_WITH(singledb, [ | ||||
|     AS_HELP_STRING([--with-singledb], | ||||
|         [enable single daughterboard use on USRP1]) | ||||
| @@ -108,10 +155,30 @@ AS_IF([test "x$with_neon_vfpv4" = "xyes"], [ | ||||
| ]) | ||||
|  | ||||
| AS_IF([test "x$with_usrp1" = "xyes"], [ | ||||
|     AC_CHECK_HEADER([boost/config.hpp],[], | ||||
|         [AC_MSG_ERROR([boost/config.hpp not found, install e.g. libboost-dev])]) | ||||
|     PKG_CHECK_MODULES(USRP, usrp >= 3.3) | ||||
| ]) | ||||
|  | ||||
| AS_IF([test "x$with_usrp1" != "xyes"],[ | ||||
| AS_IF([test "x$with_lms" = "xyes"], [ | ||||
|     PKG_CHECK_MODULES(LMS, LimeSuite) | ||||
|  | ||||
|     # LimeSuite dc124e4e2ed9b549b142410af172f0592f9f0c23 > 18.10 broke API compatibility: | ||||
|     _cflags_save=$CFLAGS | ||||
|     CFLAGS="$CFLAGS $LMS_CFLAGS" | ||||
|     AC_COMPILE_IFELSE( | ||||
|         [AC_LANG_PROGRAM( | ||||
|             [[#include <lime/LimeSuite.h>]], | ||||
|             [[LMS_VCTCXOWrite(NULL, 0, false); LMS_VCTCXORead(NULL, 0, false);]] | ||||
|         )], | ||||
|         [AC_DEFINE([HAVE_LMS_VCTCXO_EEPROM_SAVING], [1], | ||||
|             [LMS_VCTCXO* requires memory parameter])], | ||||
|         [AC_DEFINE([HAVE_LMS_VCTCXO_EEPROM_SAVING], [0], | ||||
|             [LMS_VCTCXO* has no memory parameter])]) | ||||
|     CFLAGS=$_cflags_save | ||||
| ]) | ||||
|  | ||||
| AS_IF([test "x$with_uhd" != "xno"],[ | ||||
|     PKG_CHECK_MODULES(UHD, uhd >= 003.011, | ||||
|         [AC_DEFINE(USE_UHD_3_11, 1, UHD version 3.11.0 or higher)], | ||||
|         [PKG_CHECK_MODULES(UHD, uhd >= 003.009, | ||||
| @@ -119,7 +186,6 @@ AS_IF([test "x$with_usrp1" != "xyes"],[ | ||||
|             [PKG_CHECK_MODULES(UHD, uhd >= 003.005)] | ||||
|         )] | ||||
|     ) | ||||
|     AC_DEFINE(USE_UHD, 1, All UHD versions) | ||||
| ]) | ||||
|  | ||||
| AS_IF([test "x$with_singledb" = "xyes"], [ | ||||
| @@ -161,15 +227,77 @@ dnl Check if the compiler supports runtime SIMD detection | ||||
| CHECK_BUILTIN_SUPPORT([__builtin_cpu_supports], | ||||
|   [Runtime SIMD detection will be disabled]) | ||||
|  | ||||
| AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"]) | ||||
| AM_CONDITIONAL(DEVICE_UHD, [test "x$with_uhd" != "xno"]) | ||||
| AM_CONDITIONAL(DEVICE_USRP1, [test "x$with_usrp1" = "xyes"]) | ||||
| AM_CONDITIONAL(DEVICE_LMS, [test "x$with_lms" = "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"]) | ||||
|  | ||||
| PKG_CHECK_MODULES(LIBUSB, libusb-1.0) | ||||
| PKG_CHECK_MODULES(FFTWF, fftw3f) | ||||
|  | ||||
| AC_CHECK_HEADER([boost/config.hpp],[], | ||||
|     [AC_MSG_ERROR([boost/config.hpp not found, install e.g. libboost-dev])]) | ||||
| # Generate manuals | ||||
| AC_ARG_ENABLE(manuals, | ||||
| 	[AS_HELP_STRING( | ||||
| 		[--enable-manuals], | ||||
| 		[Generate manual PDFs [default=no]], | ||||
| 	)], | ||||
| 	[osmo_ac_build_manuals=$enableval], [osmo_ac_build_manuals="no"]) | ||||
| AM_CONDITIONAL([BUILD_MANUALS], [test x"$osmo_ac_build_manuals" = x"yes"]) | ||||
| AC_ARG_VAR(OSMO_GSM_MANUALS_DIR, [path to common osmo-gsm-manuals files, overriding pkg-config and "../osmo-gsm-manuals" | ||||
| 	fallback]) | ||||
| if test x"$osmo_ac_build_manuals" = x"yes" | ||||
| then | ||||
| 	# Find OSMO_GSM_MANUALS_DIR (env, pkg-conf, fallback) | ||||
| 	if test -n "$OSMO_GSM_MANUALS_DIR"; then | ||||
| 		echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from env)" | ||||
| 	else | ||||
| 		OSMO_GSM_MANUALS_DIR="$($PKG_CONFIG osmo-gsm-manuals --variable=osmogsmmanualsdir 2>/dev/null)" | ||||
| 		if test -n "$OSMO_GSM_MANUALS_DIR"; then | ||||
| 			echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (from pkg-conf)" | ||||
| 		else | ||||
| 			OSMO_GSM_MANUALS_DIR="../osmo-gsm-manuals" | ||||
| 			echo "checking for OSMO_GSM_MANUALS_DIR... $OSMO_GSM_MANUALS_DIR (fallback)" | ||||
| 		fi | ||||
| 	fi | ||||
| 	if ! test -d "$OSMO_GSM_MANUALS_DIR"; then | ||||
| 		AC_MSG_ERROR("OSMO_GSM_MANUALS_DIR does not exist! Install osmo-gsm-manuals or set OSMO_GSM_MANUALS_DIR.") | ||||
| 	fi | ||||
|  | ||||
| 	# Find and run check-depends | ||||
| 	CHECK_DEPENDS="$OSMO_GSM_MANUALS_DIR/check-depends.sh" | ||||
| 	if ! test -x "$CHECK_DEPENDS"; then | ||||
| 		CHECK_DEPENDS="osmo-gsm-manuals-check-depends" | ||||
| 	fi | ||||
| 	if ! $CHECK_DEPENDS; then | ||||
| 		AC_MSG_ERROR("missing dependencies for --enable-manuals") | ||||
| 	fi | ||||
|  | ||||
| 	# Put in Makefile with absolute path | ||||
| 	OSMO_GSM_MANUALS_DIR="$(realpath "$OSMO_GSM_MANUALS_DIR")" | ||||
| 	AC_SUBST([OSMO_GSM_MANUALS_DIR]) | ||||
| fi | ||||
|  | ||||
|     # https://www.freedesktop.org/software/systemd/man/daemon.html | ||||
|     AC_ARG_WITH([systemdsystemunitdir], | ||||
|          [AS_HELP_STRING([--with-systemdsystemunitdir=DIR], [Directory for systemd service files])],, | ||||
|          [with_systemdsystemunitdir=auto]) | ||||
|     AS_IF([test "x$with_systemdsystemunitdir" = "xyes" -o "x$with_systemdsystemunitdir" = "xauto"], [ | ||||
|          def_systemdsystemunitdir=$($PKG_CONFIG --variable=systemdsystemunitdir systemd) | ||||
|  | ||||
|          AS_IF([test "x$def_systemdsystemunitdir" = "x"], | ||||
|        [AS_IF([test "x$with_systemdsystemunitdir" = "xyes"], | ||||
|         [AC_MSG_ERROR([systemd support requested but pkg-config unable to query systemd package])]) | ||||
|         with_systemdsystemunitdir=no], | ||||
|        [with_systemdsystemunitdir="$def_systemdsystemunitdir"])]) | ||||
|     AS_IF([test "x$with_systemdsystemunitdir" != "xno"], | ||||
|           [AC_SUBST([systemdsystemunitdir], [$with_systemdsystemunitdir])]) | ||||
|     AM_CONDITIONAL([HAVE_SYSTEMD], [test "x$with_systemdsystemunitdir" != "xno"]) | ||||
|  | ||||
| AC_MSG_RESULT([CPPFLAGS="$CPPFLAGS"]) | ||||
| AC_MSG_RESULT([CFLAGS="$CFLAGS"]) | ||||
| AC_MSG_RESULT([CXXFLAGS="$CXXFLAGS"]) | ||||
| AC_MSG_RESULT([LDFLAGS="$LDFLAGS"]) | ||||
|  | ||||
| dnl Output files | ||||
| AC_CONFIG_FILES([\ | ||||
| @@ -177,11 +305,22 @@ AC_CONFIG_FILES([\ | ||||
|     CommonLibs/Makefile \ | ||||
|     GSM/Makefile \ | ||||
|     Transceiver52M/Makefile \ | ||||
|     Transceiver52M/arm/Makefile \ | ||||
|     Transceiver52M/x86/Makefile \ | ||||
|     Transceiver52M/arch/Makefile \ | ||||
|     Transceiver52M/arch/common/Makefile \ | ||||
|     Transceiver52M/arch/arm/Makefile \ | ||||
|     Transceiver52M/arch/x86/Makefile \ | ||||
|     Transceiver52M/device/Makefile \ | ||||
|     Transceiver52M/device/uhd/Makefile \ | ||||
|     Transceiver52M/device/usrp1/Makefile \ | ||||
|     Transceiver52M/device/lms/Makefile \ | ||||
|     tests/Makefile \ | ||||
|     tests/CommonLibs/Makefile \ | ||||
|     tests/Transceiver52M/Makefile \ | ||||
|     doc/Makefile \ | ||||
|     doc/examples/Makefile \ | ||||
|     contrib/Makefile \ | ||||
|     contrib/systemd/Makefile \ | ||||
| ]) | ||||
|  | ||||
| AC_OUTPUT | ||||
| AC_OUTPUT( | ||||
| 	doc/manuals/Makefile) | ||||
|   | ||||
							
								
								
									
										1
									
								
								contrib/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								contrib/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| SUBDIRS = systemd | ||||
| @@ -1,4 +1,12 @@ | ||||
| #!/bin/sh | ||||
| # jenkins build helper script for osmo-trx.  This is how we build on jenkins.osmocom.org | ||||
| # | ||||
| # environment variables: | ||||
| # * INSTR: configure the CPU instruction set ("--with-sse", "--with-neon" or "--with-neon-vfpv4") | ||||
| # * WITH_MANUALS: build manual PDFs if set to "1" | ||||
| # * PUBLISH: upload manuals after building if set to "1" (ignored without WITH_MANUALS = "1") | ||||
| # * INSIDE_CHROOT: (used internally) set to "1" when the script runs with QEMU in an ARM chroot | ||||
| # | ||||
| set -ex | ||||
|  | ||||
| substr() { [ -z "${2##*$1*}" ]; } | ||||
| @@ -15,6 +23,11 @@ mychroot() { | ||||
|         mychroot_nocwd -w / "$@" | ||||
| } | ||||
|  | ||||
| base="$PWD" | ||||
| deps="$base/deps" | ||||
| inst="$deps/install" | ||||
| export deps inst | ||||
|  | ||||
| if [ -z "${INSIDE_CHROOT}" ]; then | ||||
|  | ||||
|         osmo-clean-workspace.sh | ||||
| @@ -23,7 +36,7 @@ if [ -z "${INSIDE_CHROOT}" ]; then | ||||
|         if ! $(substr "arm" "$(uname -m)") && [ "x${INSTR}" = "x--with-neon" -o "x${INSTR}" = "x--with-neon-vfpv4" ]; then | ||||
|  | ||||
|                 OSMOTRX_DIR="$PWD" # we assume we are called as contrib/jenkins.sh | ||||
|                 ROOTFS_PREFIX="${ROOTFS_PREFIX:-/opt}" | ||||
|                 ROOTFS_PREFIX="${ROOTFS_PREFIX:-$HOME}" | ||||
|                 ROOTFS="${ROOTFS_PREFIX}/qemu-img" | ||||
|                 mkdir -p "${ROOTFS_PREFIX}" | ||||
|  | ||||
| @@ -36,27 +49,62 @@ if [ -z "${INSIDE_CHROOT}" ]; then | ||||
|                                 sed -i "s/setup_proc//g" "$ROOTFS/debootstrap/suite-script" | ||||
|                                 mychroot /debootstrap/debootstrap --second-stage --verbose http://ftp.de.debian.org/debian/ | ||||
|                         else | ||||
|                                 wget -nc -q "https://uk.images.linuxcontainers.org/images/debian/stretch/armhf/default/20180114_22:42/rootfs.tar.xz" | ||||
|                                 YESTERDAY=$(python -c 'import datetime ; print((datetime.datetime.now() - datetime.timedelta(days=1)).strftime("%Y%m%d"))') | ||||
|                                 wget -nc -q "https://uk.images.linuxcontainers.org/images/debian/stretch/armhf/default/${YESTERDAY}_22:42/rootfs.tar.xz" | ||||
|                                 tar -xf rootfs.tar.xz -C "$ROOTFS/" || true | ||||
|                                 echo "nameserver 8.8.8.8" > "$ROOTFS/etc/resolv.conf" | ||||
|                         fi | ||||
|                         mychroot -b /dev apt-get update | ||||
|                         mychroot apt-get -y install build-essential dh-autoreconf pkg-config libuhd-dev libusb-1.0-0-dev libusb-dev git | ||||
|                         mychroot apt-get -y install build-essential dh-autoreconf pkg-config libuhd-dev libusb-1.0-0-dev libusb-dev git libtalloc-dev libgnutls28-dev stow | ||||
|                 fi | ||||
|                 # Run jenkins.sh inside the chroot: | ||||
|                 INSIDE_CHROOT=1 mychroot_nocwd -w /osmo-trx -b "$OSMOTRX_DIR:/osmo-trx" -b "$(which osmo-clean-workspace.sh):/usr/bin/osmo-clean-workspace.sh" ./contrib/jenkins.sh | ||||
|                 INSIDE_CHROOT=1 mychroot_nocwd \ | ||||
|                  -w /osmo-trx \ | ||||
|                  -b "$OSMOTRX_DIR:/osmo-trx" \ | ||||
|                  -b "$(which osmo-clean-workspace.sh):/usr/bin/osmo-clean-workspace.sh" \ | ||||
|                  -b "$(which osmo-build-dep.sh):/usr/bin/osmo-build-dep.sh" \ | ||||
|                  -b "$(which osmo-deps.sh):/usr/bin/osmo-deps.sh" \ | ||||
|                   ./contrib/jenkins.sh | ||||
|                 exit 0 | ||||
|         fi | ||||
| fi | ||||
|  | ||||
| ### BUILD osmo-trx | ||||
| mkdir "$deps" || true | ||||
|  | ||||
| osmo-build-dep.sh libosmocore "" "--enable-sanitize --disable-doxygen --disable-pcsc" | ||||
| osmo-build-dep.sh libusrp | ||||
|  | ||||
| export PKG_CONFIG_PATH="$inst/lib/pkgconfig:$PKG_CONFIG_PATH" | ||||
| export LD_LIBRARY_PATH="$inst/lib" | ||||
| export PATH="$inst/bin:$PATH" | ||||
|  | ||||
| CONFIG="--enable-sanitize --enable-werror --with-uhd --with-usrp1 --with-lms $INSTR" | ||||
|  | ||||
| # Additional configure options and depends | ||||
| if [ "$WITH_MANUALS" = "1" ]; then | ||||
| 	osmo-build-dep.sh osmo-gsm-manuals | ||||
| 	CONFIG="$CONFIG --enable-manuals" | ||||
| fi | ||||
|  | ||||
| set +x | ||||
| echo | ||||
| echo | ||||
| echo | ||||
| echo " =============================== osmo-trx ===============================" | ||||
| echo | ||||
| set -x | ||||
|  | ||||
| cd "$base" | ||||
| autoreconf --install --force | ||||
| ./configure $INSTR | ||||
| ./configure $CONFIG | ||||
| $MAKE $PARALLEL_MAKE | ||||
| $MAKE check \ | ||||
|   || cat-testlogs.sh | ||||
| DISTCHECK_CONFIGURE_FLAGS="$CONFIG" $MAKE distcheck \ | ||||
|   || cat-testlogs.sh | ||||
|  | ||||
| if [ -z "x${INSIDE_CHROOT}" ]; then | ||||
|         osmo-clean-workspace.sh | ||||
| if [ "$WITH_MANUALS" = "1" ] && [ "$PUBLISH" = "1" ]; then | ||||
| 	make -C "$base/doc/manuals" publish | ||||
| fi | ||||
|  | ||||
| osmo-clean-workspace.sh | ||||
|   | ||||
							
								
								
									
										22
									
								
								contrib/systemd/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								contrib/systemd/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| EXTRA_DIST = \ | ||||
|   osmo-trx-lms.service \ | ||||
|   osmo-trx-uhd.service \ | ||||
|   osmo-trx-usrp1.service | ||||
|  | ||||
| if HAVE_SYSTEMD | ||||
| SYSTEMD_SERVICES = | ||||
|  | ||||
| if DEVICE_UHD | ||||
| SYSTEMD_SERVICES += osmo-trx-uhd.service | ||||
| endif | ||||
|  | ||||
| if DEVICE_USRP1 | ||||
| SYSTEMD_SERVICES += osmo-trx-usrp1.service | ||||
| endif | ||||
|  | ||||
| if DEVICE_LMS | ||||
| SYSTEMD_SERVICES += osmo-trx-lms.service | ||||
| endif | ||||
|  | ||||
| systemdsystemunit_DATA = $(SYSTEMD_SERVICES) | ||||
| endif # HAVE_SYSTEMD | ||||
							
								
								
									
										11
									
								
								contrib/systemd/osmo-trx-lms.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								contrib/systemd/osmo-trx-lms.service
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| [Unit] | ||||
| Description=Osmocom SDR BTS L1 Transceiver (LimeSuite backend) | ||||
|  | ||||
| [Service] | ||||
| Type=simple | ||||
| Restart=always | ||||
| ExecStart=/usr/bin/osmo-trx-lms -C /etc/osmocom/osmo-trx-lms.cfg | ||||
| RestartSec=2 | ||||
|  | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
							
								
								
									
										11
									
								
								contrib/systemd/osmo-trx-uhd.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								contrib/systemd/osmo-trx-uhd.service
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| [Unit] | ||||
| Description=Osmocom SDR BTS L1 Transceiver (UHD Backend) | ||||
|  | ||||
| [Service] | ||||
| Type=simple | ||||
| Restart=always | ||||
| ExecStart=/usr/bin/osmo-trx-uhd -C /etc/osmocom/osmo-trx-uhd.cfg | ||||
| RestartSec=2 | ||||
|  | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
							
								
								
									
										11
									
								
								contrib/systemd/osmo-trx-usrp1.service
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								contrib/systemd/osmo-trx-usrp1.service
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,11 @@ | ||||
| [Unit] | ||||
| Description=Osmocom SDR BTS L1 Transceiver (libusrp backend) | ||||
|  | ||||
| [Service] | ||||
| Type=simple | ||||
| Restart=always | ||||
| ExecStart=/usr/bin/osmo-trx-usrp1 -C /etc/osmocom/osmo-trx-usrp1.cfg | ||||
| RestartSec=2 | ||||
|  | ||||
| [Install] | ||||
| WantedBy=multi-user.target | ||||
							
								
								
									
										268
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										268
									
								
								debian/changelog
									
									
									
									
										vendored
									
									
								
							| @@ -1,3 +1,271 @@ | ||||
| osmo-trx (1.0.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * doc: examples: Add umtrx sample config | ||||
|   * UHDDevice: Fix setup failure with LimeSuite > 18.04.1 | ||||
|   * examples: Set rt-prio 18 and print file basename | ||||
|   * lms: Several improvements and compilation/runtime fixes | ||||
|   * build: Add support for LimeSuite device backend | ||||
|   * LMSDevice: Set correct values for Max{Tx,Rx}Gain | ||||
|   * LMSDevice: Fix setup failure with LimeSuite > 18.04.1 | ||||
|   * lms: Makefile.am: Reorder params to fix link issue | ||||
|   * lms: Check LPBFW to set is within supported range | ||||
|   * debian: Add package osmo-trx-lms | ||||
|   * contrib: Add systemd services for all backends | ||||
|   * debian: Add cfg file examples for osmo-trx-{lms,uhd} | ||||
|   * Add -V param to print version | ||||
|   * lms: Allow values diff than 34dB to be set by setRxGain() | ||||
|   * Use correct paths when installing example files | ||||
|   * debian: Enable build of osmo-trx-lms | ||||
|   * debian: Explicitly enable osmo-trx-uhd build | ||||
|   * configure.ac: Fix typo in with-lms help string | ||||
|   * vty: Fix typo in gpsdo clock reference type | ||||
|   * configure.ac: Add --enable-werror option | ||||
|   * Logger: Disable pthread cancel point inside Logger destructor | ||||
|   * cosmetic: Fix trailing whitespace | ||||
|   * radioInterface: forward errors from RadioDevice to Transceiver in recv path | ||||
|   * lms: Return error on device read timeout | ||||
|   * osmo-trx: Add osmo_signal to stop whole transceiver chain correctly on error | ||||
|   * radioInterface: Fix variable storing integer return value | ||||
|   * configure.ac: Specify default language as C++ | ||||
|   * UHHDDevice: Replace deprecated header uhd/utils/thread_priority.hpp | ||||
|   * SigProcLib: Use available copyTo Vector API instead of memcopy | ||||
|   * cosmetic: Fix trailing whitespace in several files | ||||
|   * radioInterfaceMulti:pullBuffer: Sanely convert float array to complex array | ||||
|   * Vector: Copy arrays in a sane way for non-trivially copyable types | ||||
|   * jenkins.sh: Add --enable-werror flag to osmo-trx configure step | ||||
|   * Install systemd services with autotools | ||||
|   * Install sample cfg file to /etc/osmocom | ||||
|   * cosmetic: Use proper whitespace in several for loops | ||||
|   * Use pthread_setname_np to name threads | ||||
|   * CommonLibs/Makefile.am: Specify libcommon_la_LIBADD | ||||
|   * Transciever: Log values causing Tx underrun | ||||
|   * examples: Use logging level 'set-all' instead of 'all' | ||||
|   * jenkins.sh: Enable build of osmo-trx-lms | ||||
|   * ChannelizerBase: Fix ASan alloc-dealloc-mismatch | ||||
|   * UHDDevice: setRxGain on chan 0 when using multi-arfcn | ||||
|   * lms: Use LimeSuite.h log level defines instead of hardcoded values | ||||
|   * lms: Apply LMS->OSMO log level conversion | ||||
|   * Introduce OsmoTRX manual | ||||
|   * Introduce chapter trx_if.adoc and add it to OsmoTRX and OsmoBTS | ||||
|   * trx: Add reference to project wiki page in overfiew section | ||||
|   * trx: Add Hardware architecture support section | ||||
|   * trx: Add Hardware device support section | ||||
|   * osmotrx: Split Device specific section from backend one | ||||
|   * osmotrx: Write initial documentation for several supported devices | ||||
|   * osmotrx: configuration: Add section to document multi-arfcn feature | ||||
|   * osmotrx: Create a common chapter for section documenting backends | ||||
|   * osmotrx: Introduce code architecture chapter | ||||
|   * lms: Fix start after stop of device | ||||
|   * lms: Destroy streams on device stop | ||||
|   * radioInterface: Fix memleak during close() | ||||
|   * PointerFIFO: Fix memleak of ListNode | ||||
|   * lms: Make sure LMS_Close is called when Device is torn down | ||||
|   * osmo-trx: Change some lines to use libosmocore logging instead of cout | ||||
|   * lms: Close device on LMS_Init failure | ||||
|   * SigProcLib: Improve Vector buffer allocation mess | ||||
|   * lms: Allow setting Tx/RxGain for chan!=0 | ||||
|   * lms: Allow setting Tx/RxFreq for lchan!=0 | ||||
|   * lms: Improve Set{Rx,Tx}{Gain,Freq} logging | ||||
|   * transceiver: log chan on CTRL command received | ||||
|   * Add TRXCTRL log category | ||||
|   * transceiver: Log TRXCTRL iface responses towards osmo-bts-trx | ||||
|   * lms: Move {under,over}run checks into separate method | ||||
|   * lms: Do {under,over}run checks even if LMS_RecvStream fails | ||||
|   * Timeval: passed() returns true if time is equal | ||||
|   * Timeval: Move implementation to use clock_gettime and timespec | ||||
|   * Timeval: Move to osmo_clock_gettime | ||||
|   * TimevalTest: Make test deterministic with fake time | ||||
|   * lms: Fix build against LimeSuite > 18.10 | ||||
|   * configure.ac: check boost only if USRP1 support is enabled | ||||
|  | ||||
|   [ Vadim Yanitskiy ] | ||||
|   * trx_vty.c: fix: use CONFIG_NODE as parent by default | ||||
|   * device/lms/LMSDevice.cpp: fix compilation warning | ||||
|   * sigProcLib: introduce both TS1 and TS2 RACH synch. sequences | ||||
|   * sigProcLib: add a CorrType for extended (11-bit) RACH | ||||
|  | ||||
|   [ Harald Welte ] | ||||
|   * Initial work towards direct LimeSuite support in OsmoTRX | ||||
|   * update .gitignore to include osmo-trx-lms | ||||
|   * LMSDevice: Call LMS_Init() before setting sample rate | ||||
|   * LMSDevice: Print sample rate range + actual sample rate after setting it | ||||
|   * LMSDevice: Typo fix: s/Internal/External | ||||
|   * LMSDevice: Set low-pass filters to smallest possible option | ||||
|   * LMSDevice: Fix initial timestamp offset of 2500 | ||||
|   * LMS_Device: Set ts_offset to 0. | ||||
|   * LMSDevice: Reduce Rx logging verbosity: Only log unexpected timestamps | ||||
|   * move set_antennas() from UHD to generic radioDevice base class | ||||
|   * lms: Fix support for rx_paths / tx_paths | ||||
|   * lms: Call set_antennas() during open() method | ||||
|   * radioDevice: Move tx_sps from derived into base class | ||||
|   * radioDevice: better encapsulation in base class | ||||
|   * lms: Fix coding style | ||||
|   * lms: Fail in case of unsupported configuration | ||||
|   * usrp1: Fail in case of unsupported configuration | ||||
|   * Fix config file saving of {tx,rx}-path VTY config strings | ||||
|   * logging: Introduce new "DDEV" category for device-specific code | ||||
|   * update git-version-gen to generate proper version numbers | ||||
|   * ensure well-formed example config files | ||||
|   * SocketsTest.testReaderIP(): Zero terminate received buffer | ||||
|   * trx_validate_config(): Fix validation of rx_sps | ||||
|   * vty-ref: Update URI of docbook 5.0 schema | ||||
|   * lms: User correct scale factor for transmit samples | ||||
|   * lms: Set Rx gain to midpoint, as comment suggests. | ||||
|   * usrp1: Remove uninitialized skipRx logic | ||||
|   * usrp1: Fix formatting of log message (missing space) | ||||
|   * cosmetic: Don't call the SDR "USRP" in error message | ||||
|  | ||||
|   [ Zydrunas Tamosevicius ] | ||||
|   * lms: Use same timestamp offset like when using LimeSDR via UHD | ||||
|   * lms: Reduce log level of "send buffer of len ..." | ||||
|   * lms: fix LMS_StartStream() handling for multiple channels | ||||
|   * lms: Reduce Rx gain from 47 to 34 dB | ||||
|  | ||||
|   [ Alexander Couzens ] | ||||
|   * debian: add patches for debian8 | ||||
|  | ||||
|   [ Oliver Smith ] | ||||
|   * Add long parameters (--help, --version, ...) | ||||
|   * build manuals moved here from osmo-gsm-manuals.git | ||||
|   * Fix DISTCHECK_CONFIGURE_FLAGS override | ||||
|   * contrib/jenkins.sh: build and publish manuals | ||||
|   * jenkins.sh: run "make distcheck" | ||||
|   * contrib: fix makedistcheck with disabled systemd | ||||
|   * osmo-trx.cpp: move comma_delimited_to_vector() to Utils.cpp | ||||
|   * LMSDevice: make use of dev-args in osmo-trx.cfg | ||||
|   * LMSDeviceTest: fix link errors on OBS | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * Importing history from osmo-gsm-manuals.git | ||||
|  | ||||
|   [ d0gtail ] | ||||
|   * UHDDevice: log exception information on device open failure | ||||
|  | ||||
|  -- Harald Welte <laforge@gnumonks.org>  Sun, 20 Jan 2019 19:35:04 +0100 | ||||
|  | ||||
| osmo-trx (0.4.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Neels Hofmeyr ] | ||||
|   * jenkins: use osmo-clean-workspace.sh before and after build | ||||
|  | ||||
|   [ Harald Welte ] | ||||
|   * SocketsTest: Fix printing of non-nul-terminated string | ||||
|   * Revert "debian: Remove osmo-trx-usrp1 until we can build libusrp1.deb" | ||||
|   * debian/control: Remove "Maintainer" from binary package section | ||||
|   * debian/rules: Make sure we always require libusrp | ||||
|   * debian: Ensure USRP1 firmware is part of osmo-trx-usrp1 | ||||
|   * debian/control: Add build dependency to libusrp-dev | ||||
|   * update .gitignore for new executable names | ||||
|   * osmo-trx: s/GSM Core Address/GSM BTS Address/ | ||||
|  | ||||
|   [ Piotr Krysik ] | ||||
|   * UHDDevice.cpp: add USRP B205mini support | ||||
|  | ||||
|   [ Max ] | ||||
|   * Mark release target as virtual | ||||
|   * Remove outdated references to OpenBTS | ||||
|   * Remove unused headers | ||||
|   * Update installation instructions | ||||
|   * Update legal disclaimer | ||||
|   * Update license notes | ||||
|   * tests: null-terminate buffer | ||||
|  | ||||
|   [ Pau Espin Pedrol ] | ||||
|   * cosmetic: Remove trailing whitespace | ||||
|   * Logger: Stop using Log.Alarms.Max from config | ||||
|   * Logger: Stop using Log.File and Log.Level from config | ||||
|   * Drop use of ConfigurationTable gConfig | ||||
|   * Remove Configuration module and libsqlite dependency | ||||
|   * cosmetic: AUTHORS: fix trailing whitespace | ||||
|   * Set up GNU Autotest infrastructure | ||||
|   * tests: InterThread: adapt to have reproducible output and enable autotest | ||||
|   * tests: Timeval: adapt to have reproducible output and enable autotest | ||||
|   * tests: Log: adapt to have reproducible output and enable autotest | ||||
|   * Sockets.cpp: Fix initialization of UDD socket | ||||
|   * tests: Sockets: adapt to have reproducible output and enable autotest | ||||
|   * utils/convolvtest: Remove uneeded libosmocore dependency | ||||
|   * Move ARCH_LA to Makefile.common | ||||
|   * tests: Migrate convtest util to autotest infrastructure | ||||
|   * arm/convert.c: Fix compilation error | ||||
|   * arm/convert.c: Add missing convert_init implementation | ||||
|   * .gitignore: Add missing test related files | ||||
|   * Remove UDDSocket class | ||||
|   * tests: SocketTests: Pick OS-assigned instead of setting one manually | ||||
|   * tests: SocketsTest: Avoid hang forever if test fails | ||||
|   * tests: SocketsTest: Fail test on write fail | ||||
|   * tests: TimevalTest: refactor and avoid double comparison | ||||
|   * contrib/jenkins.sh: Use qemu+proot+debootstrap to run tests with ARM instruction set | ||||
|   * tests: convolve: Disable due to difference in output in different archs | ||||
|   * Remove unneeded libdl dependency | ||||
|   * Fix whitespace | ||||
|   * Add support to set Rx/TxAntenna | ||||
|   * UHDDevice: Initialize async_event_thrd in constructor | ||||
|   * Logger: Drop unused gLogEarly | ||||
|   * Logger: Remove unused logging macros | ||||
|   * Logger: get rid of alarm APIs | ||||
|   * Logger: Drop syslog support | ||||
|   * Logger: Drop support to log into file | ||||
|   * Logger: Remove unused includes | ||||
|   * Logger: Remove gLogToConsole flag | ||||
|   * configure.ac: Check for pkg-config | ||||
|   * Depend on libosmocore | ||||
|   * osmo-trx: set up signals using libosmocore helpers | ||||
|   * osmo-trx: Set up talloc ctx | ||||
|   * debian: Depend on libtalloc and libosmocore | ||||
|   * Add initial support for logging, vty, ctrl | ||||
|   * Logger: Use libosmocore logging system | ||||
|   * osmo-trx.cpp: Move trx start and stop to helper functions | ||||
|   * Move enums required by VTY to a separate header | ||||
|   * vty: Implement VTY cfg parsing for current parameters | ||||
|   * doc: Add sample cfg file for LimeSDR | ||||
|   * osmo-trx: Use VTY cfg structures while still allowing cmd line options | ||||
|   * osmo-trx: Re-introduce -l cmd line parameter | ||||
|   * Makefile.am: Avoid using subdir if arch is not required | ||||
|   * Build Transceiver52M/common as an .la lib | ||||
|   * use osmo_init_logging2() | ||||
|   * tests: Makefile.am: Fix typo in include path | ||||
|   * configure.ac: Add --enable-sanitize option | ||||
|   * Move arch specific fiels to arch subdir | ||||
|   * Move device specific files to device subdir | ||||
|   * Change configure define USRP1 to DEVICE_USRP1 | ||||
|   * Move device specific code out of radioInterface | ||||
|   * Transceiver: Move device specific code to radioDevice class | ||||
|   * Build one osmo-trx binary for each device support enabled | ||||
|   * Logger: Print correct source file and line number | ||||
|   * Transceiver: log timing info of stale bursts | ||||
|   * build: Fix make distcheck | ||||
|   * build: More OBS build failure fixes | ||||
|   * jenkins.sh: Enable build of osmo-trx-usrp1 | ||||
|   * debian: Remove osmo-trx-usrp1 until we can build libusrp1.deb | ||||
|   * debian: Fix OBS build | ||||
|   * build: Fix OBS build for ARM | ||||
|   * git-version-gen: Take into account tags not in master | ||||
|  | ||||
|   [ Alexander Huemer ] | ||||
|   * Unbreak `./configure --with-usrp1` build | ||||
|   * Fix USRP1 build with support for setting Rx/TxAntenna | ||||
|  | ||||
|   [ Alexander Couzens ] | ||||
|   * jenkins.sh: fix the download url if the qemu image wasn't setup | ||||
|   * jenkins.sh: cleanup always the workspace | ||||
|   * jenkins.sh: change qemu-img default location to $HOME/qemu-img instead of /opt/qemu-img | ||||
|  | ||||
|   [ Vadim Yanitskiy ] | ||||
|   * Transceiver.cpp: use a define for the MAX_PACKET_LENGTH | ||||
|   * Transceiver.cpp: properly zero-terminate received commands | ||||
|   * Transceiver.cpp: use pointer arithmetics for CMD parsing | ||||
|   * Transceiver.cpp: fix incorrect format string for SETTSC | ||||
|   * Transceiver.cpp: prevent out-of-range array access | ||||
|  | ||||
|   [ Martin Hauke ] | ||||
|   * configure.ac: Fix typo | ||||
|  | ||||
|   [ Philipp Maier ] | ||||
|   * doc: add example config for usrp B200 series | ||||
|  | ||||
|  -- Pau Espin Pedrol <pespin@sysmocom.de>  Thu, 03 May 2018 16:23:29 +0200 | ||||
|  | ||||
| osmo-trx (0.2.0) unstable; urgency=medium | ||||
|  | ||||
|   [ Alexander Chemeris ] | ||||
|   | ||||
							
								
								
									
										63
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										63
									
								
								debian/control
									
									
									
									
										vendored
									
									
								
							| @@ -10,16 +10,33 @@ Build-Depends: debhelper (>= 9), | ||||
|                libuhd-dev, | ||||
|                libusb-1.0-0-dev, | ||||
|                libboost-all-dev, | ||||
|                libfftw3-dev | ||||
|                libfftw3-dev, | ||||
|                libtalloc-dev, | ||||
|                libusrp-dev, | ||||
|                liblimesuite-dev, | ||||
|                libosmocore-dev (>= 0.10.0) | ||||
| Standards-Version: 3.9.6 | ||||
| Vcs-Browser: http://cgit.osmocom.org/osmo-trx | ||||
| Vcs-Git: git://git.osmocom.org/osmo-trx | ||||
| Homepage: https://projects.osmocom.org/projects/osmotrx | ||||
|  | ||||
| Package: osmo-trx | ||||
| Depends: osmo-trx-uhd | ||||
| Architecture: all | ||||
| Description: Metapackage for osmo-trx-uhd | ||||
|  | ||||
| Package: osmo-trx-dbg | ||||
| Architecture: any | ||||
| Section: debug | ||||
| Priority: extra | ||||
| Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), osmo-trx-lms (= ${binary:Version}), ${misc:Depends} | ||||
| Description: Debug symbols for the osmo-trx-* | ||||
|  Make debugging possible | ||||
|  | ||||
| Package: osmo-trx-uhd | ||||
| Architecture: any | ||||
| Depends: ${shlibs:Depends}, ${misc:Depends} | ||||
| Description: SDR transceiver that implements Layer 1 of a GSM BTS | ||||
| Description: SDR transceiver that implements Layer 1 of a GSM BTS (UHD) | ||||
|  OsmoTRX is a software-defined radio transceiver that implements the Layer 1 | ||||
|  physical layer of a BTS comprising the following 3GPP specifications: | ||||
|  . | ||||
| @@ -35,10 +52,40 @@ Description: SDR transceiver that implements Layer 1 of a GSM BTS | ||||
|  between different telecommunication associations for developing new | ||||
|  generations of mobile phone networks. (post-2G/GSM) | ||||
|  | ||||
| Package: osmo-trx-dbg | ||||
| Package: osmo-trx-usrp1 | ||||
| Architecture: any | ||||
| Section: debug | ||||
| Priority: extra | ||||
| Depends: osmo-trx (= ${binary:Version}), ${misc:Depends} | ||||
| Description: Debug symbols for the osmo-trx | ||||
|  Make debugging possible | ||||
| Depends: ${shlibs:Depends}, ${misc:Depends} | ||||
| Description: SDR transceiver that implements Layer 1 of a GSM BTS (USRP1) | ||||
|  OsmoTRX is a software-defined radio transceiver that implements the Layer 1 | ||||
|  physical layer of a BTS comprising the following 3GPP specifications: | ||||
|  . | ||||
|  TS 05.01 "Physical layer on the radio path" | ||||
|  TS 05.02 "Multiplexing and Multiple Access on the Radio Path" | ||||
|  TS 05.04 "Modulation" | ||||
|  TS 05.10 "Radio subsystem synchronization" | ||||
|  . | ||||
|  In this context, BTS is "Base transceiver station". It's the stations that | ||||
|  connect mobile phones to the mobile network. | ||||
|  . | ||||
|  3GPP is the "3rd Generation Partnership Project" which is the collaboration | ||||
|  between different telecommunication associations for developing new | ||||
|  generations of mobile phone networks. (post-2G/GSM) | ||||
|  | ||||
| Package: osmo-trx-lms | ||||
| Architecture: any | ||||
| Depends: ${shlibs:Depends}, ${misc:Depends} | ||||
| Description: SDR transceiver that implements Layer 1 of a GSM BTS (LimeSuite) | ||||
|  OsmoTRX is a software-defined radio transceiver that implements the Layer 1 | ||||
|  physical layer of a BTS comprising the following 3GPP specifications: | ||||
|  . | ||||
|  TS 05.01 "Physical layer on the radio path" | ||||
|  TS 05.02 "Multiplexing and Multiple Access on the Radio Path" | ||||
|  TS 05.04 "Modulation" | ||||
|  TS 05.10 "Radio subsystem synchronization" | ||||
|  . | ||||
|  In this context, BTS is "Base transceiver station". It's the stations that | ||||
|  connect mobile phones to the mobile network. | ||||
|  . | ||||
|  3GPP is the "3rd Generation Partnership Project" which is the collaboration | ||||
|  between different telecommunication associations for developing new | ||||
|  generations of mobile phone networks. (post-2G/GSM) | ||||
|   | ||||
							
								
								
									
										4
									
								
								debian/osmo-trx-lms.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								debian/osmo-trx-lms.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| etc/osmocom/osmo-trx-lms.cfg | ||||
| lib/systemd/system/osmo-trx-lms.service | ||||
| /usr/bin/osmo-trx-lms | ||||
| /usr/share/doc/osmo-trx/examples/osmo-trx-lms/osmo-trx-limesdr.cfg /usr/share/doc/osmo-trx/examples/osmo-trx-lms/ | ||||
							
								
								
									
										6
									
								
								debian/osmo-trx-uhd.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										6
									
								
								debian/osmo-trx-uhd.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,6 @@ | ||||
| etc/osmocom/osmo-trx-uhd.cfg | ||||
| lib/systemd/system/osmo-trx-uhd.service | ||||
| /usr/bin/osmo-trx-uhd | ||||
| /usr/share/doc/osmo-trx/examples/osmo-trx-uhd/osmo-trx-usrp_b200.cfg /usr/share/doc/osmo-trx/examples/osmo-trx-uhd/ | ||||
| /usr/share/doc/osmo-trx/examples/osmo-trx-uhd/osmo-trx-limesdr.cfg /usr/share/doc/osmo-trx/examples/osmo-trx-uhd/ | ||||
| /usr/share/doc/osmo-trx/examples/osmo-trx-uhd/osmo-trx-umtrx.cfg /usr/share/doc/osmo-trx/examples/osmo-trx-uhd/ | ||||
							
								
								
									
										4
									
								
								debian/osmo-trx-usrp1.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								debian/osmo-trx-usrp1.install
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| lib/systemd/system/osmo-trx-usrp1.service | ||||
| /usr/bin/osmo-trx-usrp1 | ||||
| /usr/share/usrp/rev2/std_inband.rbf | ||||
| /usr/share/usrp/rev4/std_inband.rbf | ||||
							
								
								
									
										1
									
								
								debian/osmo-trx.install
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								debian/osmo-trx.install
									
									
									
									
										vendored
									
									
								
							| @@ -1 +0,0 @@ | ||||
| /usr/bin/osmo-trx | ||||
|   | ||||
							
								
								
									
										57
									
								
								debian/patches/build-for-debian8.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								debian/patches/build-for-debian8.patch
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,57 @@ | ||||
| Index: osmo-trx/debian/control | ||||
| =================================================================== | ||||
| --- osmo-trx.orig/debian/control | ||||
| +++ osmo-trx/debian/control | ||||
| @@ -13,7 +13,6 @@ Build-Depends: debhelper (>= 9), | ||||
|                 libfftw3-dev, | ||||
|                 libtalloc-dev, | ||||
|                 libusrp-dev, | ||||
| -               liblimesuite-dev, | ||||
|                 libosmocore-dev (>= 0.10.0) | ||||
|  Standards-Version: 3.9.6 | ||||
|  Vcs-Browser: http://cgit.osmocom.org/osmo-trx | ||||
| @@ -29,7 +28,7 @@ Package: osmo-trx-dbg | ||||
|  Architecture: any | ||||
|  Section: debug | ||||
|  Priority: extra | ||||
| -Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), osmo-trx-lms (= ${binary:Version}), ${misc:Depends} | ||||
| +Depends: osmo-trx-uhd (= ${binary:Version}), osmo-trx-usrp1 (= ${binary:Version}), ${misc:Depends} | ||||
|  Description: Debug symbols for the osmo-trx-* | ||||
|   Make debugging possible | ||||
|   | ||||
| @@ -70,22 +70,3 @@ Description: SDR transceiver that implem | ||||
|   3GPP is the "3rd Generation Partnership Project" which is the collaboration | ||||
|   between different telecommunication associations for developing new | ||||
|   generations of mobile phone networks. (post-2G/GSM) | ||||
| - | ||||
| -Package: osmo-trx-lms | ||||
| -Architecture: any | ||||
| -Depends: ${shlibs:Depends}, ${misc:Depends} | ||||
| -Description: SDR transceiver that implements Layer 1 of a GSM BTS (LimeSuite) | ||||
| - OsmoTRX is a software-defined radio transceiver that implements the Layer 1 | ||||
| - physical layer of a BTS comprising the following 3GPP specifications: | ||||
| - . | ||||
| - TS 05.01 "Physical layer on the radio path" | ||||
| - TS 05.02 "Multiplexing and Multiple Access on the Radio Path" | ||||
| - TS 05.04 "Modulation" | ||||
| - TS 05.10 "Radio subsystem synchronization" | ||||
| - . | ||||
| - In this context, BTS is "Base transceiver station". It's the stations that | ||||
| - connect mobile phones to the mobile network. | ||||
| - . | ||||
| - 3GPP is the "3rd Generation Partnership Project" which is the collaboration | ||||
| - between different telecommunication associations for developing new | ||||
| - generations of mobile phone networks. (post-2G/GSM) | ||||
| Index: osmo-trx/debian/rules | ||||
| =================================================================== | ||||
| --- osmo-trx.orig/debian/rules | ||||
| +++ osmo-trx/debian/rules | ||||
| @@ -9,7 +9,7 @@ override_dh_shlibdeps: | ||||
|  	dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info | ||||
|   | ||||
|  override_dh_auto_configure: | ||||
| -	dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-systemdsystemunitdir=/lib/systemd/system | ||||
| +	dh_auto_configure -- --with-uhd --with-usrp1 --with-systemdsystemunitdir=/lib/systemd/system | ||||
|   | ||||
|  override_dh_strip: | ||||
|  	dh_strip --dbg-package=osmo-trx-dbg | ||||
							
								
								
									
										1
									
								
								debian/patches/series
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								debian/patches/series
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1 @@ | ||||
| # build-for-debian8.patch | ||||
							
								
								
									
										3
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								debian/rules
									
									
									
									
										vendored
									
									
								
							| @@ -8,5 +8,8 @@ export DEB_BUILD_MAINT_OPTIONS = hardening=+all | ||||
| override_dh_shlibdeps: | ||||
| 	dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info | ||||
|  | ||||
| override_dh_auto_configure: | ||||
| 	dh_auto_configure -- --with-uhd --with-usrp1 --with-lms --with-systemdsystemunitdir=/lib/systemd/system | ||||
|  | ||||
| override_dh_strip: | ||||
| 	dh_strip --dbg-package=osmo-trx-dbg | ||||
|   | ||||
							
								
								
									
										4
									
								
								doc/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								doc/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,4 @@ | ||||
| SUBDIRS = \ | ||||
|         examples \ | ||||
| 	manuals \ | ||||
|         $(NULL) | ||||
							
								
								
									
										41
									
								
								doc/examples/Makefile.am
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								doc/examples/Makefile.am
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,41 @@ | ||||
| OSMOCONF_FILES = | ||||
| osmoconfdir = $(sysconfdir)/osmocom | ||||
|  | ||||
| if DEVICE_UHD | ||||
| OSMOCONF_FILES += osmo-trx-uhd/osmo-trx-uhd.cfg | ||||
| endif | ||||
|  | ||||
| # if DEVICE_USRP1 | ||||
| # TODO: no usrp1 sample file yet | ||||
| # OSMOCONF_FILES += osmo-trx-usrp1/osmo-trx-usrp1.cfg | ||||
| # endif | ||||
|  | ||||
| if DEVICE_LMS | ||||
| OSMOCONF_FILES += osmo-trx-lms/osmo-trx-lms.cfg | ||||
| endif | ||||
|  | ||||
| osmoconf_DATA = $(OSMOCONF_FILES) | ||||
| EXTRA_DIST = $(OSMOCONF_FILES) | ||||
|  | ||||
| CFG_FILES = find $(srcdir) -type f -name '*.cfg*' | sed -e 's,^$(srcdir),,' | ||||
|  | ||||
| dist-hook: | ||||
| 	for f in $$($(CFG_FILES)); do \ | ||||
| 		j="$(distdir)/$$f" && \ | ||||
| 		mkdir -p "$$(dirname $$j)" && \ | ||||
| 		$(INSTALL_DATA) $(srcdir)/$$f $$j; \ | ||||
| 	done | ||||
|  | ||||
| install-data-hook: | ||||
| 	for f in $$($(CFG_FILES)); do \ | ||||
| 		j="$(DESTDIR)$(docdir)/examples/$$f" && \ | ||||
| 		mkdir -p "$$(dirname $$j)" && \ | ||||
| 		$(INSTALL_DATA) $(srcdir)/$$f $$j; \ | ||||
| 	done | ||||
|  | ||||
| uninstall-hook: | ||||
| 	@$(PRE_UNINSTALL) | ||||
| 	for f in $$($(CFG_FILES)); do \ | ||||
| 		j="$(DESTDIR)$(docdir)/examples/$$f" && \ | ||||
| 		$(RM) $$j; \ | ||||
| 	done | ||||
							
								
								
									
										22
									
								
								doc/examples/osmo-trx-lms/osmo-trx-limesdr.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								doc/examples/osmo-trx-lms/osmo-trx-limesdr.cfg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| log stderr | ||||
|  logging filter all 1 | ||||
|  logging color 1 | ||||
|  logging print category 1 | ||||
|  logging timestamp 1 | ||||
|  logging print file basename | ||||
|  logging level set-all info | ||||
| ! | ||||
| line vty | ||||
|  no login | ||||
| ! | ||||
| trx | ||||
|  bind-ip 127.0.0.1 | ||||
|  remote-ip 127.0.0.1 | ||||
|  base-port 5700 | ||||
|  egprs disable | ||||
|  tx-sps 4 | ||||
|  rx-sps 4 | ||||
|  rt-prio 18 | ||||
|  chan 0 | ||||
|   tx-path BAND1 | ||||
|   rx-path LNAW | ||||
							
								
								
									
										1
									
								
								doc/examples/osmo-trx-lms/osmo-trx-lms.cfg
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								doc/examples/osmo-trx-lms/osmo-trx-lms.cfg
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| osmo-trx-limesdr.cfg | ||||
							
								
								
									
										22
									
								
								doc/examples/osmo-trx-uhd/osmo-trx-limesdr.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								doc/examples/osmo-trx-uhd/osmo-trx-limesdr.cfg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| log stderr | ||||
|  logging filter all 1 | ||||
|  logging color 1 | ||||
|  logging print category 1 | ||||
|  logging timestamp 1 | ||||
|  logging print file basename | ||||
|  logging level set-all info | ||||
| ! | ||||
| line vty | ||||
|  no login | ||||
| ! | ||||
| trx | ||||
|  bind-ip 127.0.0.1 | ||||
|  remote-ip 127.0.0.1 | ||||
|  base-port 5700 | ||||
|  egprs disable | ||||
|  tx-sps 4 | ||||
|  rx-sps 4 | ||||
|  rt-prio 18 | ||||
|  chan 0 | ||||
|   tx-path BAND1 | ||||
|   rx-path LNAW | ||||
							
								
								
									
										1
									
								
								doc/examples/osmo-trx-uhd/osmo-trx-uhd.cfg
									
									
									
									
									
										Symbolic link
									
								
							
							
						
						
									
										1
									
								
								doc/examples/osmo-trx-uhd/osmo-trx-uhd.cfg
									
									
									
									
									
										Symbolic link
									
								
							| @@ -0,0 +1 @@ | ||||
| osmo-trx-usrp_b200.cfg | ||||
							
								
								
									
										23
									
								
								doc/examples/osmo-trx-uhd/osmo-trx-umtrx.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								doc/examples/osmo-trx-uhd/osmo-trx-umtrx.cfg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| log stderr | ||||
|  logging filter all 1 | ||||
|  logging color 1 | ||||
|  logging print category 1 | ||||
|  logging timestamp 1 | ||||
|  logging print file basename | ||||
|  logging level set-all info | ||||
| ! | ||||
| line vty | ||||
|  no login | ||||
| ! | ||||
| trx | ||||
|  bind-ip 127.0.0.1 | ||||
|  remote-ip 127.0.0.1 | ||||
|  base-port 5700 | ||||
|  dev-args addr=192.168.10.2,pa=NONE,pa_power_max_dbm=23,fifo_ctrl_window=0,status_port=12345 | ||||
|  egprs disable | ||||
|  tx-sps 4 | ||||
|  rx-sps 4 | ||||
|  rssi-offset 38 | ||||
|  rt-prio 18 | ||||
|  chan 0 | ||||
|  chan 1 | ||||
							
								
								
									
										22
									
								
								doc/examples/osmo-trx-uhd/osmo-trx-usrp_b200.cfg
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										22
									
								
								doc/examples/osmo-trx-uhd/osmo-trx-usrp_b200.cfg
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,22 @@ | ||||
| log stderr | ||||
|  logging filter all 1 | ||||
|  logging color 1 | ||||
|  logging print category 1 | ||||
|  logging timestamp 1 | ||||
|  logging print file basename | ||||
|  logging level set-all info | ||||
| ! | ||||
| line vty | ||||
|  no login | ||||
| ! | ||||
| trx | ||||
|  bind-ip 127.0.0.1 | ||||
|  remote-ip 127.0.0.1 | ||||
|  base-port 5700 | ||||
|  egprs disable | ||||
|  tx-sps 4 | ||||
|  rx-sps 4 | ||||
|  clock-ref external | ||||
|  rt-prio 18 | ||||
|  chan 0 | ||||
|  | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user