mirror of
				https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
				synced 2025-11-03 21:53:18 +00:00 
			
		
		
		
	Compare commits
	
		
			63 Commits
		
	
	
		
			1.7.0
			...
			achemeris/
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 
						 | 
					9fb1c0bbc0 | ||
| 
						 | 
					71392fbc7b | ||
| 
						 | 
					78d7fa0f6d | ||
| 
						 | 
					fd51ae550f | ||
| 
						 | 
					308556bbef | ||
| 
						 | 
					4c192001fc | ||
| 
						 | 
					1339e427e1 | ||
| 
						 | 
					7b638e53d0 | ||
| 
						 | 
					0ffe815bbc | ||
| 
						 | 
					19ae715e71 | ||
| 
						 | 
					c4038ef54c | ||
| 
						 | 
					604f65e69f | ||
| 
						 | 
					129ad76b15 | ||
| 
						 | 
					de202e3435 | ||
| 
						 | 
					e279124660 | ||
| 
						 | 
					302a9198df | ||
| 
						 | 
					d4a8c1360e | ||
| 
						 | 
					69b6a6dfcd | ||
| 
						 | 
					b864694652 | ||
| 
						 | 
					8eaa40dd6c | ||
| 
						 | 
					dbd27a60b6 | ||
| 
						 | 
					c5da6607b4 | ||
| 
						 | 
					42ade041d7 | ||
| 
						 | 
					5e18001bb0 | ||
| 
						 | 
					621e52ab4a | ||
| 
						 | 
					f86aa2c923 | ||
| 
						 | 
					48f8fb34aa | ||
| 
						 | 
					40c3d0a6d9 | ||
| 
						 | 
					41c6657938 | ||
| 
						 | 
					cd576c9636 | ||
| 
						 | 
					3eeda4841d | ||
| 
						 | 
					5d64491f9b | ||
| 
						 | 
					b5c450dfdf | ||
| 
						 | 
					afb04f8b63 | ||
| 
						 | 
					5f13377b83 | ||
| 
						 | 
					a6ca73ca67 | ||
| 
						 | 
					5a37840dfa | ||
| 
						 | 
					ca5d35cce8 | ||
| 
						 | 
					507e6d4e12 | ||
| 
						 | 
					1f330a9801 | ||
| 
						 | 
					7c6f58af7a | ||
| 
						 | 
					996f426c16 | ||
| 
						 | 
					711e6afddf | ||
| 
						 | 
					222688d3dc | ||
| 
						 | 
					20bc24d367 | ||
| 
						 | 
					59796e1e3e | ||
| 
						 | 
					19a506dffa | ||
| 
						 | 
					fbd6e1c985 | ||
| 
						 | 
					03669856b7 | ||
| 
						 | 
					5d0e392b21 | ||
| 
						 | 
					801ce60d4a | ||
| 
						 | 
					ed64e799bc | ||
| 
						 | 
					d1bcab2731 | ||
| 
						 | 
					00493f1a41 | ||
| 
						 | 
					d565556b4b | ||
| 
						 | 
					2b48784c61 | ||
| 
						 | 
					635e34239c | ||
| 
						 | 
					9e5e208b6e | ||
| 
						 | 
					8f47387777 | ||
| 
						 | 
					c707f42396 | ||
| 
						 | 
					00ed1441a1 | ||
| 
						 | 
					8d804a4cd8 | ||
| 
						 | 
					82ede3e810 | 
@@ -35,7 +35,7 @@
 | 
				
			|||||||
#ifdef DEBUG_CONFIG
 | 
					#ifdef DEBUG_CONFIG
 | 
				
			||||||
#define	debugLogEarly gLogEarly
 | 
					#define	debugLogEarly gLogEarly
 | 
				
			||||||
#else
 | 
					#else
 | 
				
			||||||
#define	debugLogEarly
 | 
					#define	debugLogEarly(x,y,z)
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2011 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under the terms of the GNU Affero Public License.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* See the COPYING file in the main directory for details.
 | 
				
			||||||
@@ -41,6 +42,7 @@
 | 
				
			|||||||
//@{
 | 
					//@{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UNUSED in osmo-trx
 | 
				
			||||||
/** Pointer FIFO for interthread operations.  */
 | 
					/** Pointer FIFO for interthread operations.  */
 | 
				
			||||||
// (pat) The elements in the queue are type T*, and
 | 
					// (pat) The elements in the queue are type T*, and
 | 
				
			||||||
// the Fifo class implements the underlying queue.
 | 
					// the Fifo class implements the underlying queue.
 | 
				
			||||||
@@ -98,7 +100,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue {
 | 
				
			|||||||
	{
 | 
						{
 | 
				
			||||||
		ScopedLock lock(mLock);
 | 
							ScopedLock lock(mLock);
 | 
				
			||||||
		T* retVal = (T*)mQ.get();
 | 
							T* retVal = (T*)mQ.get();
 | 
				
			||||||
		while (retVal==NULL) {
 | 
							if (retVal==NULL) {
 | 
				
			||||||
			mWriteSignal.wait(mLock);
 | 
								mWriteSignal.wait(mLock);
 | 
				
			||||||
			retVal = (T*)mQ.get();
 | 
								retVal = (T*)mQ.get();
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -155,6 +157,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UNUSED in osmo-trx
 | 
				
			||||||
// (pat) Identical to above but with the threading problem fixed.
 | 
					// (pat) Identical to above but with the threading problem fixed.
 | 
				
			||||||
template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
 | 
					template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -276,6 +279,7 @@ template <class T, class Fifo=PointerFIFO> class InterthreadQueue2 {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UNUSED in osmo-trx
 | 
				
			||||||
/** Pointer FIFO for interthread operations.  */
 | 
					/** Pointer FIFO for interthread operations.  */
 | 
				
			||||||
template <class T> class InterthreadQueueWithWait {
 | 
					template <class T> class InterthreadQueueWithWait {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -380,7 +384,7 @@ template <class T> class InterthreadQueueWithWait {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UNUSED in osmo-trx
 | 
				
			||||||
/** Thread-safe map of pointers to class D, keyed by class K. */
 | 
					/** Thread-safe map of pointers to class D, keyed by class K. */
 | 
				
			||||||
template <class K, class D > class InterthreadMap {
 | 
					template <class K, class D > class InterthreadMap {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -644,7 +648,7 @@ template <class T, class C = std::vector<T*>, class Cmp = PointerCompare<T> > cl
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// UNUSED in osmo-trx
 | 
				
			||||||
class Semaphore {
 | 
					class Semaphore {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private:
 | 
						private:
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
					* Copyright 2008 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under the terms of the GNU Affero Public License.
 | 
				
			||||||
@@ -27,15 +28,23 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "Threads.h"
 | 
					#include "Threads.h"
 | 
				
			||||||
#include "Interthread.h"
 | 
					#include "Interthread.h"
 | 
				
			||||||
 | 
					#include "Configuration.h"
 | 
				
			||||||
#include <iostream>
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace std;
 | 
					using namespace std;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ConfigurationTable gConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
InterthreadQueue<int> gQ;
 | 
					InterthreadQueue<int> gQ;
 | 
				
			||||||
InterthreadMap<int,int> gMap;
 | 
					InterthreadMap<int,int> gMap;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void* qWriter(void*)
 | 
					class QueueWriter : public Thread
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						QueueWriter() : Thread("QueueWriter") {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						virtual void runThread()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		int *p;
 | 
							int *p;
 | 
				
			||||||
		for (int i=0; i<20; i++) {
 | 
							for (int i=0; i<20; i++) {
 | 
				
			||||||
@@ -43,15 +52,21 @@ void* qWriter(void*)
 | 
				
			|||||||
			*p = i;
 | 
								*p = i;
 | 
				
			||||||
			COUT("queue write " << *p);
 | 
								COUT("queue write " << *p);
 | 
				
			||||||
			gQ.write(p);
 | 
								gQ.write(p);
 | 
				
			||||||
		if (random()%2) sleep(1);
 | 
								msleep(1);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		p = new int;
 | 
							p = new int;
 | 
				
			||||||
		*p = -1;
 | 
							*p = -1;
 | 
				
			||||||
		gQ.write(p);
 | 
							gQ.write(p);
 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void* qReader(void*)
 | 
					class QueueReader : public Thread
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						QueueReader() : Thread("QueueReader") {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						virtual void runThread()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		bool done = false;
 | 
							bool done = false;
 | 
				
			||||||
		while (!done) {
 | 
							while (!done) {
 | 
				
			||||||
@@ -60,11 +75,17 @@ void* qReader(void*)
 | 
				
			|||||||
			if (*p<0) done=true;
 | 
								if (*p<0) done=true;
 | 
				
			||||||
			delete p;
 | 
								delete p;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void* mapWriter(void*)
 | 
					class MapWriter : public Thread
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						MapWriter() : Thread("MapWriter") {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						virtual void runThread()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		int *p;
 | 
							int *p;
 | 
				
			||||||
		for (int i=0; i<20; i++) {
 | 
							for (int i=0; i<20; i++) {
 | 
				
			||||||
@@ -72,21 +93,26 @@ void* mapWriter(void*)
 | 
				
			|||||||
			*p = i;
 | 
								*p = i;
 | 
				
			||||||
			COUT("map write " << *p);
 | 
								COUT("map write " << *p);
 | 
				
			||||||
			gMap.write(i,p);
 | 
								gMap.write(i,p);
 | 
				
			||||||
		if (random()%2) sleep(1);
 | 
								msleep(1);
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void* mapReader(void*)
 | 
					class MapReader : public Thread
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						MapReader() : Thread("MapReader") {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						virtual void runThread()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		for (int i=0; i<20; i++) {
 | 
							for (int i=0; i<20; i++) {
 | 
				
			||||||
			int *p = gMap.read(i);
 | 
								int *p = gMap.read(i);
 | 
				
			||||||
			COUT("map read " << *p);
 | 
								COUT("map read " << *p);
 | 
				
			||||||
			// InterthreadMap will delete the pointers
 | 
								// InterthreadMap will delete the pointers
 | 
				
			||||||
		// delete p;
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -95,20 +121,25 @@ void* mapReader(void*)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
int main(int argc, char *argv[])
 | 
					int main(int argc, char *argv[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	Thread qReaderThread;
 | 
						COUT("TEST 1: InterthreadQueue")
 | 
				
			||||||
	qReaderThread.start(qReader,NULL);
 | 
						QueueReader qReaderThread;
 | 
				
			||||||
	Thread mapReaderThread;
 | 
						QueueWriter qWriterThread;
 | 
				
			||||||
	mapReaderThread.start(mapReader,NULL);
 | 
						qReaderThread.startThread();
 | 
				
			||||||
 | 
						qWriterThread.startThread();
 | 
				
			||||||
 | 
						// stopThread() will wait for a thread to stop for 5 seconds, which
 | 
				
			||||||
 | 
						// is more than enough for this test to finish.
 | 
				
			||||||
 | 
						qReaderThread.stopThread();
 | 
				
			||||||
 | 
						qWriterThread.stopThread();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Thread qWriterThread;
 | 
						COUT("TEST 2: InterthreadMap")
 | 
				
			||||||
	qWriterThread.start(qWriter,NULL);
 | 
						MapReader mapReaderThread;
 | 
				
			||||||
	Thread mapWriterThread;
 | 
						mapReaderThread.startThread();
 | 
				
			||||||
	mapWriterThread.start(mapWriter,NULL);
 | 
						MapWriter mapWriterThread;
 | 
				
			||||||
 | 
						mapWriterThread.startThread();
 | 
				
			||||||
	qReaderThread.join();
 | 
						// stopThread() will wait for a thread to stop for 5 seconds, which
 | 
				
			||||||
	qWriterThread.join();
 | 
						// is more than enough for this test to finish.
 | 
				
			||||||
	mapReaderThread.join();
 | 
						mapReaderThread.stopThread();
 | 
				
			||||||
	mapWriterThread.join();
 | 
						mapWriterThread.stopThread();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -44,6 +44,7 @@ libcommon_la_SOURCES = \
 | 
				
			|||||||
	Utils.cpp
 | 
						Utils.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
noinst_PROGRAMS = \
 | 
					noinst_PROGRAMS = \
 | 
				
			||||||
 | 
						ThreadsTest \
 | 
				
			||||||
	BitVectorTest \
 | 
						BitVectorTest \
 | 
				
			||||||
	InterthreadTest \
 | 
						InterthreadTest \
 | 
				
			||||||
	SocketsTest \
 | 
						SocketsTest \
 | 
				
			||||||
@@ -80,12 +81,16 @@ URLEncodeTest_LDADD = libcommon.la
 | 
				
			|||||||
BitVectorTest_SOURCES = BitVectorTest.cpp
 | 
					BitVectorTest_SOURCES = BitVectorTest.cpp
 | 
				
			||||||
BitVectorTest_LDADD = libcommon.la $(SQLITE_LA)
 | 
					BitVectorTest_LDADD = libcommon.la $(SQLITE_LA)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ThreadsTest_SOURCES = ThreadsTest.cpp
 | 
				
			||||||
 | 
					ThreadsTest_LDADD = libcommon.la $(SQLITE_LA)
 | 
				
			||||||
 | 
					ThreadsTest_LDFLAGS = -lpthread
 | 
				
			||||||
 | 
					
 | 
				
			||||||
InterthreadTest_SOURCES = InterthreadTest.cpp
 | 
					InterthreadTest_SOURCES = InterthreadTest.cpp
 | 
				
			||||||
InterthreadTest_LDADD = libcommon.la
 | 
					InterthreadTest_LDADD = libcommon.la $(SQLITE_LA)
 | 
				
			||||||
InterthreadTest_LDFLAGS = -lpthread
 | 
					InterthreadTest_LDFLAGS = -lpthread
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SocketsTest_SOURCES = SocketsTest.cpp
 | 
					SocketsTest_SOURCES = SocketsTest.cpp
 | 
				
			||||||
SocketsTest_LDADD = libcommon.la
 | 
					SocketsTest_LDADD = libcommon.la $(SQLITE_LA)
 | 
				
			||||||
SocketsTest_LDFLAGS = -lpthread
 | 
					SocketsTest_LDFLAGS = -lpthread
 | 
				
			||||||
 | 
					
 | 
				
			||||||
TimevalTest_SOURCES = TimevalTest.cpp
 | 
					TimevalTest_SOURCES = TimevalTest.cpp
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under the terms of the GNU Affero Public License.
 | 
				
			||||||
@@ -129,6 +130,11 @@ void DatagramSocket::close()
 | 
				
			|||||||
	::close(mSocketFD);
 | 
						::close(mSocketFD);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DatagramSocket::shutdown()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  ::shutdown(mSocketFD, SHUT_RDWR);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
DatagramSocket::~DatagramSocket()
 | 
					DatagramSocket::~DatagramSocket()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2010 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under the terms of the GNU Affero Public License.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* See the COPYING file in the main directory for details.
 | 
				
			||||||
@@ -134,6 +135,11 @@ public:
 | 
				
			|||||||
	/** Close the socket. */
 | 
						/** Close the socket. */
 | 
				
			||||||
	void close();
 | 
						void close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Shutdown the socket without destroying the descriptor
 | 
				
			||||||
 | 
					   *  Use this to interrupt blocking read()
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  void shutdown();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
					* Copyright 2008 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under the terms of the GNU Affero Public License.
 | 
				
			||||||
@@ -28,14 +29,22 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "Sockets.h"
 | 
					#include "Sockets.h"
 | 
				
			||||||
#include "Threads.h"
 | 
					#include "Threads.h"
 | 
				
			||||||
#include <stdio.h>
 | 
					#include "Configuration.h"
 | 
				
			||||||
 | 
					#include "Timeval.h"
 | 
				
			||||||
#include <stdlib.h>
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ConfigurationTable gConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const int gNumToSend = 10;
 | 
					static const int gNumToSend = 10;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void *testReaderIP(void *)
 | 
					class TestReaderIP : public Thread
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						TestReaderIP() : Thread("TestReaderIP") {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						virtual void runThread()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		UDPSocket readSocket(5934, "localhost", 5061);
 | 
							UDPSocket readSocket(5934, "localhost", 5061);
 | 
				
			||||||
		readSocket.nonblocking();
 | 
							readSocket.nonblocking();
 | 
				
			||||||
@@ -44,18 +53,23 @@ void *testReaderIP(void *)
 | 
				
			|||||||
			char buf[MAX_UDP_LENGTH];
 | 
								char buf[MAX_UDP_LENGTH];
 | 
				
			||||||
			int count = readSocket.read(buf);
 | 
								int count = readSocket.read(buf);
 | 
				
			||||||
			if (count>0) {
 | 
								if (count>0) {
 | 
				
			||||||
			COUT("read: " << buf);
 | 
									COUT("IP read: " << buf);
 | 
				
			||||||
				rc++;
 | 
									rc++;
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
 | 
									COUT("IP sleeping...");
 | 
				
			||||||
				sleep(2);
 | 
									sleep(2);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class TestReaderUnix : public Thread
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						TestReaderUnix() : Thread("TestReaderUnix") {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
void *testReaderUnix(void *)
 | 
						virtual void runThread()
 | 
				
			||||||
	{
 | 
						{
 | 
				
			||||||
		UDDSocket readSocket("testDestination");
 | 
							UDDSocket readSocket("testDestination");
 | 
				
			||||||
		readSocket.nonblocking();
 | 
							readSocket.nonblocking();
 | 
				
			||||||
@@ -64,23 +78,24 @@ void *testReaderUnix(void *)
 | 
				
			|||||||
			char buf[MAX_UDP_LENGTH];
 | 
								char buf[MAX_UDP_LENGTH];
 | 
				
			||||||
			int count = readSocket.read(buf);
 | 
								int count = readSocket.read(buf);
 | 
				
			||||||
			if (count>0) {
 | 
								if (count>0) {
 | 
				
			||||||
			COUT("read: " << buf);
 | 
									COUT("UNIX read: " << buf);
 | 
				
			||||||
				rc++;
 | 
									rc++;
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
 | 
									COUT("UNIX sleeping...");
 | 
				
			||||||
				sleep(2);
 | 
									sleep(2);
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	return NULL;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int main(int argc, char * argv[] )
 | 
					int main(int argc, char * argv[] )
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Thread readerThreadIP;
 | 
					  TestReaderIP readerThreadIP;
 | 
				
			||||||
  readerThreadIP.start(testReaderIP,NULL);
 | 
					  TestReaderUnix readerThreadUnix;
 | 
				
			||||||
  Thread readerThreadUnix;
 | 
					  readerThreadIP.startThread();
 | 
				
			||||||
  readerThreadUnix.start(testReaderUnix,NULL);
 | 
					  readerThreadUnix.startThread();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  UDPSocket socket1(5061, "127.0.0.1",5934);
 | 
					  UDPSocket socket1(5061, "127.0.0.1",5934);
 | 
				
			||||||
  UDDSocket socket1U("testSource","testDestination");
 | 
					  UDDSocket socket1U("testSource","testDestination");
 | 
				
			||||||
@@ -93,11 +108,9 @@ int main(int argc, char * argv[] )
 | 
				
			|||||||
  for (int i=0; i<gNumToSend; i++) {
 | 
					  for (int i=0; i<gNumToSend; i++) {
 | 
				
			||||||
    socket1.write("Hello IP land");	
 | 
					    socket1.write("Hello IP land");	
 | 
				
			||||||
    socket1U.write("Hello Unix domain");
 | 
					    socket1U.write("Hello Unix domain");
 | 
				
			||||||
	sleep(1);
 | 
					    msleep(1);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  readerThreadIP.join();
 | 
					 | 
				
			||||||
  readerThreadUnix.join();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// vim: ts=4 sw=4
 | 
					// vim: ts=4 sw=4
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
					* Copyright 2008 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under the terms of the GNU Affero Public License.
 | 
				
			||||||
@@ -29,11 +30,26 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "Threads.h"
 | 
					#include "Threads.h"
 | 
				
			||||||
#include "Timeval.h"
 | 
					#include "Timeval.h"
 | 
				
			||||||
 | 
					#include "Logger.h"
 | 
				
			||||||
 | 
					#include <pthread.h>
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					#include <errno.h> // for ETIMEDOUT
 | 
				
			||||||
 | 
					#include <sys/syscall.h> // for SYS_gettid
 | 
				
			||||||
 | 
					#include <sys/prctl.h> // Linux specific, for prctl(PR_SET_NAME)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Make sure we get MCL_CURRENT and MCL_FUTURE (for mlockall) on OS X 10.3
 | 
				
			||||||
 | 
					#define _P1003_1B_VISIBLE
 | 
				
			||||||
 | 
					#include <sys/mman.h>
 | 
				
			||||||
 | 
					#undef _P1003_1B_VISIBLE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
using namespace std;
 | 
					using namespace std;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define POSIX_OK           0
 | 
				
			||||||
 | 
					#define POSIX_NO_WAIT      0
 | 
				
			||||||
 | 
					#define POSIX_WAIT_FOREVER (-1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline int gettid() {return syscall(SYS_gettid);}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Mutex gStreamLock;		///< Global lock to control access to cout and cerr.
 | 
					Mutex gStreamLock;		///< Global lock to control access to cout and cerr.
 | 
				
			||||||
@@ -95,27 +111,235 @@ Mutex::~Mutex()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Block for the signal up to the cancellation timeout. */
 | 
					/** Block for the signal up to the cancellation timeout. */
 | 
				
			||||||
void Signal::wait(Mutex& wMutex, unsigned timeout) const
 | 
					int Signal::wait(Mutex& wMutex, unsigned timeout) const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	Timeval then(timeout);
 | 
						Timeval then(timeout);
 | 
				
			||||||
	struct timespec waitTime = then.timespec();
 | 
						struct timespec waitTime = then.timespec();
 | 
				
			||||||
	pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
 | 
						return pthread_cond_timedwait(&mSignal,&wMutex.mMutex,&waitTime);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Thread::Thread(const string &name, size_t stackSize)
 | 
				
			||||||
void Thread::start(void *(*task)(void*), void *arg)
 | 
					: mThreadId((pthread_t)0)
 | 
				
			||||||
 | 
					, mThreadName(name)
 | 
				
			||||||
 | 
					, mStackSize(stackSize)
 | 
				
			||||||
 | 
					, mThreadState(THREAD_STATE_IDLE)
 | 
				
			||||||
 | 
					, mThreadData(NULL)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	assert(mThread==((pthread_t)0));
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Thread::~Thread()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    stopThread();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void *Thread::threadAdaptor(void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    Thread *pThread = (Thread*)data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // If we ever receive a thread cancel request, it means that the Thread
 | 
				
			||||||
 | 
					    // object is in the process of being destroyed.  To avoid the situation
 | 
				
			||||||
 | 
					    // where a thread attempts to run after its containing Thread object has
 | 
				
			||||||
 | 
					    // been freed, we set the thread up so that the cancel takes effect
 | 
				
			||||||
 | 
					    // immediately (as opposed to waiting until the next thread cancellation
 | 
				
			||||||
 | 
					    // point).
 | 
				
			||||||
 | 
					    pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // =====================================================================
 | 
				
			||||||
 | 
					    // Synchronize with the start() in the parent thread.
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        // 1. Lock synchronization mutex.
 | 
				
			||||||
 | 
					        ScopedLock lock(pThread->mThreadStartupMutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 2. Btw, set the thread name, while we're inside the mutex.
 | 
				
			||||||
 | 
					        // FIXME: This works on Linux with glibc >= 2.12. Under *BSD and MacOS X
 | 
				
			||||||
 | 
					        //        this function has different arguments.
 | 
				
			||||||
 | 
					//        pthread_setname_np(pThread->mThreadId, pThread->mThreadName.c_str());
 | 
				
			||||||
 | 
					        // FIXME: For some reason the previous call doesn't work on my Ubuntu 12.04,
 | 
				
			||||||
 | 
					        //        so we use this one which works.
 | 
				
			||||||
 | 
					        prctl(PR_SET_NAME, pThread->mThreadName.c_str());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 3. Signal that we've started.
 | 
				
			||||||
 | 
					        pThread->mThreadStartStopEvent.signal();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 4. Wait until start() finishes its initialization.
 | 
				
			||||||
 | 
					        //
 | 
				
			||||||
 | 
					        // The actual thread is created and started with pthread_create(), then
 | 
				
			||||||
 | 
					        // start() does its housekeeping and sets mThreadState=THREAD_STATE_RUNNING.
 | 
				
			||||||
 | 
					        // If we allow Thread::run() to start before this initialization completes,
 | 
				
			||||||
 | 
					        // callers might think (among other things) that the thread is not started
 | 
				
			||||||
 | 
					        // while it's actually started.
 | 
				
			||||||
 | 
					        pThread->mThreadInitializedEvent.wait(pThread->mThreadStartupMutex);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    // Synchronization with the parent thread is finished.
 | 
				
			||||||
 | 
					    // =====================================================================
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Log Thread ID for debugging purposes
 | 
				
			||||||
 | 
					    LOG(INFO) << "Thread started: " << pThread->mThreadName
 | 
				
			||||||
 | 
					              << " with lwp=" << gettid() << ", pid=" << getpid();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Keep all memory locked into physical mem, to guarantee realtime-behaviour
 | 
				
			||||||
 | 
					    int res = mlockall(MCL_CURRENT|MCL_FUTURE);
 | 
				
			||||||
 | 
					    if (res != POSIX_OK) {
 | 
				
			||||||
 | 
					      LOG(WARNING) << "Failed to lock memory for thread: " << pThread->mThreadName;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Run the actual code
 | 
				
			||||||
 | 
					    pThread->runThread();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Huh, we're done. Signal to a (potentially) waiting stop()'s.
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ScopedLock lock(pThread->mThreadStateMutex);
 | 
				
			||||||
 | 
					        pThread->mThreadState = THREAD_STATE_IDLE;
 | 
				
			||||||
 | 
					        pThread->mThreadStartStopEvent.broadcast();
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Thread::ReturnStatus Thread::startThread(void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    pthread_attr_t attrib;
 | 
				
			||||||
 | 
					//    timeval        threadStartTime;
 | 
				
			||||||
 | 
					//    timespec       threadStartTimeout;
 | 
				
			||||||
    bool res;
 | 
					    bool res;
 | 
				
			||||||
	// (pat) Moved initialization to constructor to avoid crash in destructor.
 | 
					
 | 
				
			||||||
	//res = pthread_attr_init(&mAttrib);
 | 
					    // Lock startup synchronization mutex. It will be used in conjunction with
 | 
				
			||||||
	//assert(!res);
 | 
					    // mThreadInitializedEvent and mThreadStartStopEvent conditional variables.
 | 
				
			||||||
	res = pthread_attr_setstacksize(&mAttrib, mStackSize);
 | 
					    ScopedLock lock(mThreadStartupMutex);
 | 
				
			||||||
	assert(!res);
 | 
					
 | 
				
			||||||
	res = pthread_create(&mThread, &mAttrib, task, arg);
 | 
					    {
 | 
				
			||||||
	assert(!res);
 | 
					        ScopedLock lock(mThreadStateMutex);
 | 
				
			||||||
 | 
					        if (mThreadState != THREAD_STATE_IDLE)
 | 
				
			||||||
 | 
					            return ALREADY_STARTED;
 | 
				
			||||||
 | 
					        mThreadState = THREAD_STATE_STARTING;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Save thread data pointer
 | 
				
			||||||
 | 
					    mThreadData = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LOG(DEBUG) <<  "Starting thread " << mThreadName << " (" << this << ")";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // construct thread attribute
 | 
				
			||||||
 | 
					    res = pthread_attr_init(&attrib);
 | 
				
			||||||
 | 
					    if (res != POSIX_OK) {
 | 
				
			||||||
 | 
					        LOG(ALERT) <<  "pthread_attr_init failed, returned " << res
 | 
				
			||||||
 | 
					                   << " in " << mThreadName << " (" << this << ")";
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Set the thread stack size
 | 
				
			||||||
 | 
					    res = pthread_attr_setstacksize(&attrib, mStackSize);
 | 
				
			||||||
 | 
					    if (res != POSIX_OK)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        LOG(ALERT) <<  "pthread_attr_setstacksize failed, returned " << res
 | 
				
			||||||
 | 
					                   << " in " << mThreadName << " (" << this << ")";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Create the thread detached
 | 
				
			||||||
 | 
					    res = pthread_attr_setdetachstate(&attrib, PTHREAD_CREATE_DETACHED);
 | 
				
			||||||
 | 
					    if (res != POSIX_OK)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					       LOG(ALERT) <<  "pthread_attr_setdetachstate failed, returned " << res
 | 
				
			||||||
 | 
					                  << " in " << mThreadName << " (" << this << ")";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // =====================================================================
 | 
				
			||||||
 | 
					    // Start the thread and synchronize with it
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Start the thread!
 | 
				
			||||||
 | 
					    res = pthread_create(&mThreadId, &attrib, threadAdaptor, (void *)this);
 | 
				
			||||||
 | 
					    // Attributes are no longer needed.
 | 
				
			||||||
 | 
					    pthread_attr_destroy(&attrib);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    if (res != POSIX_OK)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        LOG(ALERT) << "pthread_create failed, returned " << res
 | 
				
			||||||
 | 
					                   << " in " << mThreadName << " (" << this << ")";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return PTHREAD_ERROR;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Wait for the thread to startup.
 | 
				
			||||||
 | 
					    res = mThreadStartStopEvent.wait(mThreadStartupMutex, THREAD_STARTUP_TIMEOUT*1000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // If the thread does not start in THREAD_STARTUP_TIMEOUT seconds,
 | 
				
			||||||
 | 
					    // then something is terribly wrong here.
 | 
				
			||||||
 | 
					    if (res == ETIMEDOUT)
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					       LOG(ALERT) << "thread " << mThreadName << " (" << this << ") hasn't started up in "
 | 
				
			||||||
 | 
					                  << THREAD_STARTUP_TIMEOUT << " seconds. Bailing out.";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					       return RETURN_TIMEOUT;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // We're done with the initialization.
 | 
				
			||||||
 | 
					    ackThreadStart();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // ToDo: Add other initialization here, e.g. adding this thread to a list of all threads.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Startup initialization finished. Signal this to started thread, so
 | 
				
			||||||
 | 
					    // it could go on.
 | 
				
			||||||
 | 
					    mThreadInitializedEvent.signal();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    return RETURN_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Thread::ReturnStatus Thread::stopThread()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    int res;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LOG(DEBUG) <<  "Stopping thread " << mThreadName << " (" << this << ")";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    while (1) {
 | 
				
			||||||
 | 
					        ScopedLock lock(mThreadStateMutex);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        switch (mThreadState) {
 | 
				
			||||||
 | 
					        case THREAD_STATE_IDLE:
 | 
				
			||||||
 | 
					            // Nothing to do.
 | 
				
			||||||
 | 
					            return RETURN_OK;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case THREAD_STATE_STARTING:
 | 
				
			||||||
 | 
					            // Something is wrong in thi world.
 | 
				
			||||||
 | 
					            assert(mThreadState != THREAD_STATE_STARTING);
 | 
				
			||||||
 | 
					            LOG(ALERT) << "Trying to stop thread " << mThreadName
 | 
				
			||||||
 | 
					                       << " (" << this << ") while it's trying to start.";
 | 
				
			||||||
 | 
					            return WRONG_STATE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case THREAD_STATE_RUNNING:
 | 
				
			||||||
 | 
					            // Request shudown
 | 
				
			||||||
 | 
					            mThreadState = THREAD_STATE_STOPPING;
 | 
				
			||||||
 | 
					            // no "break" here to fall through to the next case
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        case THREAD_STATE_STOPPING:
 | 
				
			||||||
 | 
					            // Wait for the thread to stop.
 | 
				
			||||||
 | 
					            LOG(DEBUG) << "Waiting for thread " << mThreadName << " (" << this << ") to stop.";
 | 
				
			||||||
 | 
					            res = mThreadStartStopEvent.wait(mThreadStateMutex, THREAD_STOP_TIMEOUT*1000);
 | 
				
			||||||
 | 
					            LOG(DEBUG) << "Thread " << mThreadName << " (" << this << ") signalled stop "
 | 
				
			||||||
 | 
					                       << "with res=" << res << " and mThreadState=" << mThreadState;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // If the thread does not stop in THREAD_STOP_TIMEOUT seconds,
 | 
				
			||||||
 | 
					            // return error. It may be waiting for something.
 | 
				
			||||||
 | 
					            if (res == ETIMEDOUT)
 | 
				
			||||||
 | 
					            {
 | 
				
			||||||
 | 
					               LOG(ALERT) << "thread " << mThreadName << " (" << this << ") hasn't stopped in "
 | 
				
			||||||
 | 
					                          << THREAD_STARTUP_TIMEOUT << " seconds. Bailing out.";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					               return RETURN_TIMEOUT;
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            // Conditional variable could return in case of a signal, so we should
 | 
				
			||||||
 | 
					            // double check that the thread has indeed stopped.
 | 
				
			||||||
 | 
					            if (mThreadState == THREAD_STATE_IDLE)
 | 
				
			||||||
 | 
					                return RETURN_OK;
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                // Try again...
 | 
				
			||||||
 | 
					                break;
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // We should never reach this line
 | 
				
			||||||
 | 
					    assert(false);
 | 
				
			||||||
 | 
					    return RETURN_OK;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// vim: ts=4 sw=4
 | 
					// vim: ts=4 sw=4
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2011 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2011 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under the terms of the GNU Affero Public License.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* See the COPYING file in the main directory for details.
 | 
				
			||||||
@@ -121,14 +122,14 @@ class Signal {
 | 
				
			|||||||
		Block for the signal up to the cancellation timeout.
 | 
							Block for the signal up to the cancellation timeout.
 | 
				
			||||||
		Under Linux, spurious returns are possible.
 | 
							Under Linux, spurious returns are possible.
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
	void wait(Mutex& wMutex, unsigned timeout) const;
 | 
						int wait(Mutex& wMutex, unsigned timeout) const;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/**
 | 
						/**
 | 
				
			||||||
		Block for the signal.
 | 
							Block for the signal.
 | 
				
			||||||
		Under Linux, spurious returns are possible.
 | 
							Under Linux, spurious returns are possible.
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
	void wait(Mutex& wMutex) const
 | 
						int wait(Mutex& wMutex) const
 | 
				
			||||||
		{ pthread_cond_wait(&mSignal,&wMutex.mMutex); }
 | 
							{ return pthread_cond_wait(&mSignal,&wMutex.mMutex); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void signal() { pthread_cond_signal(&mSignal); }
 | 
						void signal() { pthread_cond_signal(&mSignal); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -137,46 +138,104 @@ class Signal {
 | 
				
			|||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
#define START_THREAD(thread,function,argument) \
 | 
					 | 
				
			||||||
	thread.start((void *(*)(void*))function, (void*)argument);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** A C++ wrapper for pthread threads.  */
 | 
					/** A C++ wrapper for pthread threads.  */
 | 
				
			||||||
class Thread {
 | 
					class Thread {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	private:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	pthread_t mThread;
 | 
					 | 
				
			||||||
	pthread_attr_t mAttrib;
 | 
					 | 
				
			||||||
	// FIXME -- Can this be reduced now?
 | 
					 | 
				
			||||||
	size_t mStackSize;
 | 
					 | 
				
			||||||
	
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	/** Create a thread in a non-running state. */
 | 
					    typedef void *(*Adaptor)(void*);
 | 
				
			||||||
	Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) {
 | 
					    enum ReturnStatus {
 | 
				
			||||||
		pthread_attr_init(&mAttrib);	// (pat) moved this here.
 | 
					        RETURN_OK = 0,
 | 
				
			||||||
		mStackSize=wStackSize;
 | 
					        ALREADY_STARTED,
 | 
				
			||||||
	}
 | 
					        ALREADY_IDLE,
 | 
				
			||||||
 | 
					        PTHREAD_ERROR,
 | 
				
			||||||
	/**
 | 
					        WRONG_STATE,
 | 
				
			||||||
		Destroy the Thread.
 | 
					        RETURN_TIMEOUT
 | 
				
			||||||
		It should be stopped and joined.
 | 
					    };
 | 
				
			||||||
	*/
 | 
					    enum ThreadState {
 | 
				
			||||||
	// (pat) If the Thread is destroyed without being started, then mAttrib is undefined.  Oops.
 | 
					        THREAD_STATE_IDLE,      ///< Thread is not started. On start() => STARTING
 | 
				
			||||||
	~Thread() { pthread_attr_destroy(&mAttrib); }
 | 
					        THREAD_STATE_STARTING,  ///< Thread is about to start. When actually started => RUNNING
 | 
				
			||||||
 | 
					        THREAD_STATE_RUNNING,   ///< Thread is active. On stop() => STOPPING
 | 
				
			||||||
 | 
					        THREAD_STATE_STOPPING   ///< Thread is about to stop. When actually stopped => IDLE
 | 
				
			||||||
	/** Start the thread on a task. */
 | 
					    };
 | 
				
			||||||
	void start(void *(*task)(void*), void *arg);
 | 
					    enum {
 | 
				
			||||||
 | 
					       THREAD_STARTUP_TIMEOUT=5, ///< Time to wait for thread startup (in seconds).
 | 
				
			||||||
	/** Join a thread that will stop on its own. */
 | 
					       THREAD_STOP_TIMEOUT=5     ///< Time to wait for thread stop (in seconds).
 | 
				
			||||||
	void join() { int s = pthread_join(mThread,NULL); assert(!s); mThread = 0; }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    };
 | 
					    };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Create a thread in a non-running state. */
 | 
				
			||||||
 | 
					    Thread(const std::string &name, size_t stackSize = (65536*4));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Destroy the Thread. */
 | 
				
			||||||
 | 
					    virtual ~Thread();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Start the thread. */
 | 
				
			||||||
 | 
					    ReturnStatus startThread(void *data=NULL);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Stop the thread. */
 | 
				
			||||||
 | 
					    ReturnStatus stopThread();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    ThreadState getThreadState() const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ScopedLock lock(mThreadStateMutex);
 | 
				
			||||||
 | 
					        return mThreadState;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    bool isThreadRunning() const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ScopedLock lock(mThreadStateMutex);
 | 
				
			||||||
 | 
					        return mThreadState == THREAD_STATE_RUNNING;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    void requestThreadStop()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ScopedLock lock(mThreadStateMutex);
 | 
				
			||||||
 | 
					        if (mThreadState == THREAD_STATE_RUNNING)
 | 
				
			||||||
 | 
					            mThreadState = THREAD_STATE_STOPPING;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    bool isThreadStopping() const
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        ScopedLock lock(mThreadStateMutex);
 | 
				
			||||||
 | 
					        return mThreadState == THREAD_STATE_STOPPING;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const std::string &getThreadName() const {return mThreadName;}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pthread_t mThreadId; ///< OS id of the thread.
 | 
				
			||||||
 | 
					    const std::string mThreadName; ///< Name of the thread.
 | 
				
			||||||
 | 
					    size_t mStackSize;  ///< Requested stack size for the thread.
 | 
				
			||||||
 | 
					    ThreadState mThreadState; ///< The current state of the thread.
 | 
				
			||||||
 | 
					    mutable Mutex mThreadStateMutex; ///< Mutex to protect ThreadState variable
 | 
				
			||||||
 | 
					    void *mThreadData;  ///< Data to be passed to the thread loop.
 | 
				
			||||||
 | 
					    Mutex mThreadStartupMutex; ///< Mutex, used with the next two conditional
 | 
				
			||||||
 | 
					                        ///< variables to synchronize thread startup.
 | 
				
			||||||
 | 
					    Signal  mThreadInitializedEvent; ///< Conditional variable, signaling
 | 
				
			||||||
 | 
					                        ///< that this thread object initialization is completed
 | 
				
			||||||
 | 
					                        ///< and the thread could go on.
 | 
				
			||||||
 | 
					    Signal  mThreadStartStopEvent; ///< Conditional variable, signaling
 | 
				
			||||||
 | 
					                        ///< that the thread is started and start() method could
 | 
				
			||||||
 | 
					                        ///< return to caller.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    /** Function with the actual thread loop.
 | 
				
			||||||
 | 
					     *  Override this function in child classes to do real work.
 | 
				
			||||||
 | 
					     */
 | 
				
			||||||
 | 
					    virtual void runThread() =0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // Static funciton which actually starts the run() method.
 | 
				
			||||||
 | 
					    static void *threadAdaptor(void *data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void ackThreadStart() {
 | 
				
			||||||
 | 
					        ScopedLock lock(mThreadStateMutex);
 | 
				
			||||||
 | 
					        assert(mThreadState == THREAD_STATE_STARTING);
 | 
				
			||||||
 | 
					        mThreadState = THREAD_STATE_RUNNING;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    void ackThreadStop() {
 | 
				
			||||||
 | 
					        ScopedLock lock(mThreadStateMutex);
 | 
				
			||||||
 | 
					        assert(mThreadState == THREAD_STATE_STOPPING);
 | 
				
			||||||
 | 
					        mThreadState = THREAD_STATE_IDLE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										94
									
								
								CommonLibs/ThreadsTest.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										94
									
								
								CommonLibs/ThreadsTest.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,94 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under the terms of the GNU Affero Public License.
 | 
				
			||||||
 | 
					* See the COPYING file in the main directory for details.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						This program is 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 "Threads.h"
 | 
				
			||||||
 | 
					#include "Timeval.h"
 | 
				
			||||||
 | 
					#include "Configuration.h"
 | 
				
			||||||
 | 
					#include <iostream>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ConfigurationTable gConfig;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class SimpleThreadTest : public Thread
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					    SimpleThreadTest() : Thread("SimpleThreadTest") {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    void runThread()
 | 
				
			||||||
 | 
					    {
 | 
				
			||||||
 | 
					        COUT(getThreadName() << ": Started thread");
 | 
				
			||||||
 | 
					        while (isThreadRunning()) {
 | 
				
			||||||
 | 
					            COUT(getThreadName() << ": Sleeping...");
 | 
				
			||||||
 | 
					            msleep(50);
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        COUT(getThreadName() << ": Stopped thread");
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void testSimpleStartStop()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    SimpleThreadTest simpleThreadTest;
 | 
				
			||||||
 | 
					    COUT("Main: Starting thread " << simpleThreadTest.getThreadName());
 | 
				
			||||||
 | 
					    simpleThreadTest.startThread();
 | 
				
			||||||
 | 
					    COUT("Main: Started  thread " << simpleThreadTest.getThreadName());
 | 
				
			||||||
 | 
					    msleep(30);
 | 
				
			||||||
 | 
					    COUT("Main: Stopping thread " << simpleThreadTest.getThreadName());
 | 
				
			||||||
 | 
					    simpleThreadTest.stopThread();
 | 
				
			||||||
 | 
					    COUT("Main: Stopped  thread " << simpleThreadTest.getThreadName());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void testDoubleRequestStop()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    SimpleThreadTest simpleThreadTest;
 | 
				
			||||||
 | 
					    COUT("Main: Starting thread " << simpleThreadTest.getThreadName());
 | 
				
			||||||
 | 
					    simpleThreadTest.startThread();
 | 
				
			||||||
 | 
					    COUT("Main: Started  thread " << simpleThreadTest.getThreadName());
 | 
				
			||||||
 | 
					    msleep(30);
 | 
				
			||||||
 | 
					    COUT("Main: Requesting stop for thread " << simpleThreadTest.getThreadName());
 | 
				
			||||||
 | 
					    simpleThreadTest.requestThreadStop();
 | 
				
			||||||
 | 
					    msleep(30);
 | 
				
			||||||
 | 
					    COUT("Main: Requesting stop for thread " << simpleThreadTest.getThreadName());
 | 
				
			||||||
 | 
					    simpleThreadTest.requestThreadStop();
 | 
				
			||||||
 | 
					    msleep(30);
 | 
				
			||||||
 | 
					    COUT("Main: Stopping thread " << simpleThreadTest.getThreadName());
 | 
				
			||||||
 | 
					    simpleThreadTest.stopThread();
 | 
				
			||||||
 | 
					    COUT("Main: Stopped  thread " << simpleThreadTest.getThreadName());
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char *argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    std::cout<< std::endl << "Simple start/stop test" << std::endl << std::endl ;
 | 
				
			||||||
 | 
					    testSimpleStartStop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    std::cout << std::endl << "Double requestThreadStop() test" << std::endl << std::endl ;
 | 
				
			||||||
 | 
					    testDoubleRequestStop();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// vim: ts=4 sw=4
 | 
				
			||||||
@@ -18,18 +18,11 @@
 | 
				
			|||||||
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					# along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#hack to get around symlink svn:externals in git -kurtis
 | 
					 | 
				
			||||||
top_srcdir = $(abs_top_srcdir)
 | 
					 | 
				
			||||||
top_builddir = $(abs_top_builddir)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
 | 
					COMMON_INCLUDEDIR = $(top_srcdir)/CommonLibs
 | 
				
			||||||
GSM_INCLUDEDIR = $(top_srcdir)/GSM
 | 
					GSM_INCLUDEDIR = $(top_srcdir)/GSM
 | 
				
			||||||
SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
 | 
					SQLITE_INCLUDEDIR = $(top_srcdir)/sqlite3
 | 
				
			||||||
 | 
					
 | 
				
			||||||
SVNDEV = -D'SVN_REV="$(shell svnversion -n $(top_builddir))"'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
STD_DEFINES_AND_INCLUDES = \
 | 
					STD_DEFINES_AND_INCLUDES = \
 | 
				
			||||||
	$(SVNDEV) \
 | 
					 | 
				
			||||||
	-I$(COMMON_INCLUDEDIR) \
 | 
						-I$(COMMON_INCLUDEDIR) \
 | 
				
			||||||
	-I$(GSM_INCLUDEDIR) \
 | 
						-I$(GSM_INCLUDEDIR) \
 | 
				
			||||||
	-I$(SQLITE_INCLUDEDIR)
 | 
						-I$(SQLITE_INCLUDEDIR)
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										278
									
								
								Transceiver52M/DriveLoop.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										278
									
								
								Transceiver52M/DriveLoop.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,278 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2008, 2009, 2010, 2012 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under the terms of the GNU Public License.
 | 
				
			||||||
 | 
					* See the COPYING file in the main directory for details.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					    it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					    the Free Software Foundation, either version 3 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 <stdio.h>
 | 
				
			||||||
 | 
					#include "DriveLoop.h"
 | 
				
			||||||
 | 
					#include <Logger.h>
 | 
				
			||||||
 | 
					#include "RTMD.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					using namespace GSM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DriveLoop::DriveLoop(int wBasePort, const char *TRXAddress,
 | 
				
			||||||
 | 
					                     RadioInterface *wRadioInterface,
 | 
				
			||||||
 | 
							     int wChanM, int wC0, int wSamplesPerSymbol,
 | 
				
			||||||
 | 
					                     GSM::Time wTransmitLatency)
 | 
				
			||||||
 | 
					: Thread("DriveLoop")
 | 
				
			||||||
 | 
					, mClockSocket(wBasePort, TRXAddress, wBasePort + 100)
 | 
				
			||||||
 | 
					, mC0(wC0)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  mChanM = wChanM;
 | 
				
			||||||
 | 
					  mSamplesPerSymbol = wSamplesPerSymbol;
 | 
				
			||||||
 | 
					  mRadioInterface = wRadioInterface;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mStartTime = (random() % gHyperframe, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mTransmitDeadlineClock = mStartTime;
 | 
				
			||||||
 | 
					  mLatencyUpdateTime = mStartTime;
 | 
				
			||||||
 | 
					  mTransmitLatency = wTransmitLatency;
 | 
				
			||||||
 | 
					  mLastClockUpdateTime = mStartTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mRadioInterface->getClock()->set(mStartTime);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // generate pulse and setup up signal processing library
 | 
				
			||||||
 | 
					  gsmPulse = generateGSMPulse(2, mSamplesPerSymbol);
 | 
				
			||||||
 | 
					  LOG(DEBUG) << "gsmPulse: " << *gsmPulse;
 | 
				
			||||||
 | 
					  sigProcLibSetup(mSamplesPerSymbol);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  txFullScale = mRadioInterface->fullScaleInputValue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // initialize filler tables with dummy bursts on C0, empty bursts otherwise
 | 
				
			||||||
 | 
					  for (int i = 0; i < 8; i++) {
 | 
				
			||||||
 | 
					    signalVector* modBurst = modulateBurst(gDummyBurst, *gsmPulse,
 | 
				
			||||||
 | 
					                                           8 + (i % 4 == 0), mSamplesPerSymbol);
 | 
				
			||||||
 | 
					    scaleVector(*modBurst, txFullScale);
 | 
				
			||||||
 | 
					    for (int j = 0; j < 102; j++) {
 | 
				
			||||||
 | 
					      for (int n = 0; n < mChanM; n++) {
 | 
				
			||||||
 | 
					        if (n == mC0)
 | 
				
			||||||
 | 
					          fillerTable[n][j][i] = new signalVector(*modBurst);
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					          fillerTable[n][j][i] = new signalVector(modBurst->size());
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    delete modBurst;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (int n = 0; n < mChanM; n++) {
 | 
				
			||||||
 | 
					      fillerModulus[n][i] = 26;
 | 
				
			||||||
 | 
					      mChanType[n][i] = NONE;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DriveLoop::~DriveLoop()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  stopThread();
 | 
				
			||||||
 | 
					  delete gsmPulse;
 | 
				
			||||||
 | 
					  sigProcLibDestroy();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DriveLoop::pushRadioVector(GSM::Time &nowTime)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  int i;
 | 
				
			||||||
 | 
					  radioVector *staleBurst;
 | 
				
			||||||
 | 
					  radioVector *next;
 | 
				
			||||||
 | 
					  RTMD_SET("pushRadioVector");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (i = 0; i < mChanM; i++) {
 | 
				
			||||||
 | 
					    // dump stale bursts, if any
 | 
				
			||||||
 | 
					    while (staleBurst = mTransmitPriorityQueue[i].getStaleBurst(nowTime)) {
 | 
				
			||||||
 | 
					      // Even if the burst is stale, put it in the fillter table.
 | 
				
			||||||
 | 
					      // (It might be an idle pattern.)
 | 
				
			||||||
 | 
					      LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int TN = nowTime.TN();
 | 
				
			||||||
 | 
					    int modFN = nowTime.FN() % fillerModulus[i][nowTime.TN()];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mTxBursts[i] = fillerTable[i][modFN][TN];
 | 
				
			||||||
 | 
					    mIsFiller[i] = true;
 | 
				
			||||||
 | 
					    mIsZero[i] = (mChanType[i][TN] == NONE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // if queue contains data at the desired timestamp, stick it into FIFO
 | 
				
			||||||
 | 
					    if (next = (radioVector*) mTransmitPriorityQueue[i].getCurrentBurst(nowTime)) {
 | 
				
			||||||
 | 
					      LOG(DEBUG) << "transmitFIFO: wrote burst " << next << " at time: " << nowTime;
 | 
				
			||||||
 | 
					      mTxBursts[i] = next;
 | 
				
			||||||
 | 
					      mIsFiller[i] = false;
 | 
				
			||||||
 | 
					      mIsZero[i] = false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mRadioInterface->driveTransmitRadio(mTxBursts, mIsZero);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (i = 0; i < mChanM; i++) {
 | 
				
			||||||
 | 
					    if (!mIsFiller[i])
 | 
				
			||||||
 | 
					      delete mTxBursts[i];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  RTMD_CLEAR("pushRadioVector");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DriveLoop::setModulus(int channel, int timeslot)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  switch (mChanType[channel][timeslot]) {
 | 
				
			||||||
 | 
					  case NONE:
 | 
				
			||||||
 | 
					  case I:
 | 
				
			||||||
 | 
					  case II:
 | 
				
			||||||
 | 
					  case III:
 | 
				
			||||||
 | 
					  case FILL:
 | 
				
			||||||
 | 
					    fillerModulus[channel][timeslot] = 26;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case IV:
 | 
				
			||||||
 | 
					  case VI:
 | 
				
			||||||
 | 
					  case V:
 | 
				
			||||||
 | 
					    fillerModulus[channel][timeslot] = 51;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					    //case V: 
 | 
				
			||||||
 | 
					  case VII:
 | 
				
			||||||
 | 
					    fillerModulus[channel][timeslot] = 102;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  default:
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					DriveLoop::CorrType DriveLoop::expectedCorrType(int channel, GSM::Time currTime)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  unsigned burstTN = currTime.TN();
 | 
				
			||||||
 | 
					  unsigned burstFN = currTime.FN();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  switch (mChanType[channel][burstTN]) {
 | 
				
			||||||
 | 
					  case NONE:
 | 
				
			||||||
 | 
					    return OFF;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case FILL:
 | 
				
			||||||
 | 
					    return IDLE;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case I:
 | 
				
			||||||
 | 
					    return TSC;
 | 
				
			||||||
 | 
					    /*if (burstFN % 26 == 25) 
 | 
				
			||||||
 | 
					      return IDLE;
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      return TSC;*/
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case II:
 | 
				
			||||||
 | 
					    if (burstFN % 2 == 1)
 | 
				
			||||||
 | 
					      return IDLE;
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      return TSC;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case III:
 | 
				
			||||||
 | 
					    return TSC;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case IV:
 | 
				
			||||||
 | 
					  case VI:
 | 
				
			||||||
 | 
					    return RACH;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case V: {
 | 
				
			||||||
 | 
					    int mod51 = burstFN % 51;
 | 
				
			||||||
 | 
					    if ((mod51 <= 36) && (mod51 >= 14))
 | 
				
			||||||
 | 
					      return RACH;
 | 
				
			||||||
 | 
					    else if ((mod51 == 4) || (mod51 == 5))
 | 
				
			||||||
 | 
					      return RACH;
 | 
				
			||||||
 | 
					    else if ((mod51 == 45) || (mod51 == 46))
 | 
				
			||||||
 | 
					      return RACH;
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      return TSC;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  case VII:
 | 
				
			||||||
 | 
					    if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
 | 
				
			||||||
 | 
					      return IDLE;
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      return TSC;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case LOOPBACK:
 | 
				
			||||||
 | 
					    if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
 | 
				
			||||||
 | 
					      return IDLE;
 | 
				
			||||||
 | 
					    else
 | 
				
			||||||
 | 
					      return TSC;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  default:
 | 
				
			||||||
 | 
					    return OFF;
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					void DriveLoop::driveReceiveFIFO() 
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  SoftVector *rxBurst = NULL;
 | 
				
			||||||
 | 
					  int RSSI;
 | 
				
			||||||
 | 
					  int TOA;  // in 1/256 of a symbol
 | 
				
			||||||
 | 
					  GSM::Time burstTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mRadioInterface->driveReceiveRadio();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 *  Features a carefully controlled latency mechanism, to
 | 
				
			||||||
 | 
					 *  assure that transmit packets arrive at the radio/USRP
 | 
				
			||||||
 | 
					 *  before they need to be transmitted.
 | 
				
			||||||
 | 
					 *  
 | 
				
			||||||
 | 
					 *  Deadline clock indicates the burst that needs to be
 | 
				
			||||||
 | 
					 *  pushed into the FIFO right NOW.  If transmit queue does
 | 
				
			||||||
 | 
					 *  not have a burst, stick in filler data.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					void DriveLoop::driveTransmitFIFO() 
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  int i;
 | 
				
			||||||
 | 
					  GSM::Time timeDiff;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  RadioClock *radioClock = (mRadioInterface->getClock());
 | 
				
			||||||
 | 
					  timeDiff = radioClock->get() + mTransmitLatency - mTransmitDeadlineClock;
 | 
				
			||||||
 | 
					  while (timeDiff > 0) {
 | 
				
			||||||
 | 
					    RTMD_VAL("DrvTxFIFO-TDiff", timeDiff.FN()*8+timeDiff.TN());
 | 
				
			||||||
 | 
					    pushRadioVector(mTransmitDeadlineClock);
 | 
				
			||||||
 | 
					    mTransmitDeadlineClock.incTN();
 | 
				
			||||||
 | 
					    timeDiff = radioClock->get() + mTransmitLatency - mTransmitDeadlineClock;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // FIXME -- This should not be a hard spin.
 | 
				
			||||||
 | 
					  // But any delay here causes us to throw omni_thread_fatal.
 | 
				
			||||||
 | 
					  //else radioClock->wait();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DriveLoop::writeClockInterface()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  RTMD_SET("TRX-writeClk");
 | 
				
			||||||
 | 
					  char command[50];
 | 
				
			||||||
 | 
					  // FIXME -- This should be adaptive.
 | 
				
			||||||
 | 
					  sprintf(command,"IND CLOCK %llu",
 | 
				
			||||||
 | 
					          (unsigned long long) (mTransmitDeadlineClock.FN() + 2));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  LOG(INFO) << "ClockInterface: sending " << command;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mClockSocket.write(command,strlen(command)+1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mLastClockUpdateTime = mTransmitDeadlineClock;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  RTMD_CLEAR("TRX-writeClk");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void DriveLoop::runThread()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  setPriority();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  while (isThreadRunning()) {
 | 
				
			||||||
 | 
					    driveReceiveFIFO();
 | 
				
			||||||
 | 
					    driveTransmitFIFO();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
							
								
								
									
										188
									
								
								Transceiver52M/DriveLoop.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										188
									
								
								Transceiver52M/DriveLoop.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,188 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					* Copyright 2008, 2012 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This software is distributed under the terms of the GNU Public License.
 | 
				
			||||||
 | 
					* See the COPYING file in the main directory for details.
 | 
				
			||||||
 | 
					*
 | 
				
			||||||
 | 
					* This use of this software may be subject to additional restrictions.
 | 
				
			||||||
 | 
					* See the LEGAL file in the main directory for details.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					    it under the terms of the GNU General Public License as published by
 | 
				
			||||||
 | 
					    the Free Software Foundation, either version 3 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/>.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
						Compilation switches
 | 
				
			||||||
 | 
						TRANSMIT_LOGGING	write every burst on the given slot to a log
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef _DRIVELOOP_H_
 | 
				
			||||||
 | 
					#define _DRIVELOOP_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "radioInterface.h"
 | 
				
			||||||
 | 
					#include "Interthread.h"
 | 
				
			||||||
 | 
					#include "GSMCommon.h"
 | 
				
			||||||
 | 
					#include "Sockets.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					#include <sys/socket.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** Define this to be the slot number to be logged. */
 | 
				
			||||||
 | 
					//#define TRANSMIT_LOGGING 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** The Transceiver class, responsible for physical layer of basestation */
 | 
				
			||||||
 | 
					class DriveLoop : public Thread {
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  GSM::Time mTransmitLatency;     ///< latency between basestation clock and transmit deadline clock
 | 
				
			||||||
 | 
					  GSM::Time mLatencyUpdateTime;   ///< last time latency was updated
 | 
				
			||||||
 | 
					  GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  UDPSocket mClockSocket;	  ///< socket for writing clock updates to GSM core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VectorQueue  mTransmitPriorityQueue[CHAN_MAX];   ///< priority queue of transmit bursts received from GSM core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  GSM::Time mTransmitDeadlineClock;       ///< deadline for pushing bursts into transmit FIFO 
 | 
				
			||||||
 | 
					  GSM::Time mStartTime;                   ///< random start time of the radio clock
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  RadioInterface *mRadioInterface;	  ///< associated radioInterface object
 | 
				
			||||||
 | 
					  double txFullScale;                     ///< full scale input to radio
 | 
				
			||||||
 | 
					  double rxFullScale;                     ///< full scale output to radio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Number of channels supported by the channelizer */
 | 
				
			||||||
 | 
					  int mChanM;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** unmodulate a modulated burst */
 | 
				
			||||||
 | 
					#ifdef TRANSMIT_LOGGING
 | 
				
			||||||
 | 
					  void unModulateVector(signalVector wVector); 
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
 | 
				
			||||||
 | 
					  void pushRadioVector(GSM::Time &nowTime);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Pull and demodulate a burst from the receive FIFO */ 
 | 
				
			||||||
 | 
					  SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI, int &timingOffset);
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					  signalVector *gsmPulse;              ///< the GSM shaping pulse for modulation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int mSamplesPerSymbol;               ///< number of samples per GSM symbol
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int fillerModulus[CHAN_MAX][8];                ///< modulus values of all timeslots, in frames
 | 
				
			||||||
 | 
					  signalVector *fillerTable[CHAN_MAX][102][8];   ///< table of modulated filler waveforms for all timeslots
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Channelizer path for primary ARFCN */
 | 
				
			||||||
 | 
					  int mC0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  signalVector *mTxBursts[CHAN_MAX];
 | 
				
			||||||
 | 
					  bool         mIsFiller[CHAN_MAX];
 | 
				
			||||||
 | 
					  bool         mIsZero[CHAN_MAX];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Transceiver constructor 
 | 
				
			||||||
 | 
					      @param wBasePort base port number of UDP sockets
 | 
				
			||||||
 | 
					      @param TRXAddress IP address of the TRX manager, as a string
 | 
				
			||||||
 | 
					      @param wSamplesPerSymbol number of samples per GSM symbol
 | 
				
			||||||
 | 
					      @param wTransmitLatency initial setting of transmit latency
 | 
				
			||||||
 | 
					      @param radioInterface associated radioInterface object
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					  DriveLoop(int wBasePort, const char *TRXAddress,
 | 
				
			||||||
 | 
						    RadioInterface *wRadioInterface,
 | 
				
			||||||
 | 
						    int wChanM = 1, int wC0 = 0,
 | 
				
			||||||
 | 
					            int wSamplesPerSymbol = SAMPSPERSYM,
 | 
				
			||||||
 | 
						    GSM::Time wTransmitLatency = GSM::Time(3, 0));
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					  /** Destructor */
 | 
				
			||||||
 | 
					  ~DriveLoop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  VectorQueue *priorityQueue(int m) { return &mTransmitPriorityQueue[m]; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Codes for burst types of received bursts*/
 | 
				
			||||||
 | 
					  typedef enum {
 | 
				
			||||||
 | 
					    OFF,               ///< timeslot is off
 | 
				
			||||||
 | 
					    TSC,	       ///< timeslot should contain a normal burst
 | 
				
			||||||
 | 
					    RACH,	       ///< timeslot should contain an access burst
 | 
				
			||||||
 | 
					    IDLE	       ///< timeslot is an idle (or dummy) burst
 | 
				
			||||||
 | 
					  } CorrType;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Codes for channel combinations */
 | 
				
			||||||
 | 
					  typedef enum {
 | 
				
			||||||
 | 
					    FILL,               ///< Channel is transmitted, but unused
 | 
				
			||||||
 | 
					    I,                  ///< TCH/FS
 | 
				
			||||||
 | 
					    II,                 ///< TCH/HS, idle every other slot
 | 
				
			||||||
 | 
					    III,                ///< TCH/HS
 | 
				
			||||||
 | 
					    IV,                 ///< FCCH+SCH+CCCH+BCCH, uplink RACH
 | 
				
			||||||
 | 
					    V,                  ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
 | 
				
			||||||
 | 
					    VI,                 ///< CCCH+BCCH, uplink RACH
 | 
				
			||||||
 | 
					    VII,                ///< SDCCH/8 + SACCH/8
 | 
				
			||||||
 | 
					    NONE,               ///< Channel is inactive, default
 | 
				
			||||||
 | 
					    LOOPBACK            ///< similar go VII, used in loopback testing
 | 
				
			||||||
 | 
					  } ChannelCombination;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** Set modulus for specific timeslot */
 | 
				
			||||||
 | 
					  void setModulus(int channel, int timeslot);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** return the expected burst type for the specified timestamp */
 | 
				
			||||||
 | 
					  CorrType expectedCorrType(int channel, GSM::Time currTime);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void setTimeslot(int m, int timeslot, ChannelCombination comb)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    mChanType[m][timeslot] = comb;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  GSM::Time getStartTime() { return mStartTime; }
 | 
				
			||||||
 | 
					  GSM::Time getLastClockUpdate() { return mLastClockUpdateTime; }
 | 
				
			||||||
 | 
					  GSM::Time getDeadlineClock() { return mTransmitDeadlineClock; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** send messages over the clock socket */
 | 
				
			||||||
 | 
					  void writeClockInterface(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  ChannelCombination mChanType[CHAN_MAX][8];     ///< channel types for all timeslots
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** drive reception and demodulation of GSM bursts */ 
 | 
				
			||||||
 | 
					  void driveReceiveFIFO();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** drive transmission of GSM bursts */
 | 
				
			||||||
 | 
					  void driveTransmitFIFO();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** drive handling of control messages from GSM core */
 | 
				
			||||||
 | 
					  void driveControl();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /**
 | 
				
			||||||
 | 
					    drive modulation and sorting of GSM bursts from GSM core
 | 
				
			||||||
 | 
					    @return true if a burst was transferred successfully
 | 
				
			||||||
 | 
					  */
 | 
				
			||||||
 | 
					  bool driveTransmitPriorityQueue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  virtual void runThread();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** set priority on current thread */
 | 
				
			||||||
 | 
					  void setPriority() { mRadioInterface->setPriority(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** FIFO thread loop */
 | 
				
			||||||
 | 
					void *RadioDriveLoopAdapter(DriveLoop *);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* _DRIVELOOP_H_ */
 | 
				
			||||||
@@ -33,7 +33,8 @@ else
 | 
				
			|||||||
AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
 | 
					AM_CPPFLAGS = $(STD_DEFINES_AND_INCLUDES)
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
endif
 | 
					endif
 | 
				
			||||||
AM_CXXFLAGS = -ldl -lpthread
 | 
					RTMD_FLAGS = -DWITH_RTMD
 | 
				
			||||||
 | 
					AM_CXXFLAGS = -ldl -lpthread $(RTMD_FLAGS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
rev2dir = $(datadir)/usrp/rev2
 | 
					rev2dir = $(datadir)/usrp/rev2
 | 
				
			||||||
rev4dir = $(datadir)/usrp/rev4
 | 
					rev4dir = $(datadir)/usrp/rev4
 | 
				
			||||||
@@ -47,26 +48,25 @@ EXTRA_DIST = \
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
noinst_LTLIBRARIES = libtransceiver.la
 | 
					noinst_LTLIBRARIES = libtransceiver.la
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RTMD_SOURCES = \
 | 
				
			||||||
 | 
						RTMD.c
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
COMMON_SOURCES = \
 | 
					COMMON_SOURCES = \
 | 
				
			||||||
	radioInterface.cpp \
 | 
						radioInterface.cpp \
 | 
				
			||||||
	radioVector.cpp \
 | 
						radioVector.cpp \
 | 
				
			||||||
	radioClock.cpp \
 | 
						radioClock.cpp \
 | 
				
			||||||
	sigProcLib.cpp \
 | 
						sigProcLib.cpp \
 | 
				
			||||||
 | 
						DriveLoop.cpp \
 | 
				
			||||||
	Transceiver.cpp \
 | 
						Transceiver.cpp \
 | 
				
			||||||
	DummyLoad.cpp
 | 
						DummyLoad.cpp
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if RESAMPLE
 | 
					 | 
				
			||||||
libtransceiver_la_SOURCES = \
 | 
					libtransceiver_la_SOURCES = \
 | 
				
			||||||
	$(COMMON_SOURCES) \
 | 
						$(COMMON_SOURCES) \
 | 
				
			||||||
	radioIOResamp.cpp
 | 
						$(RTMD_SOURCES)
 | 
				
			||||||
else
 | 
					 | 
				
			||||||
libtransceiver_la_SOURCES = \
 | 
					 | 
				
			||||||
	$(COMMON_SOURCES) \
 | 
					 | 
				
			||||||
	radioIO.cpp
 | 
					 | 
				
			||||||
endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
noinst_PROGRAMS = \
 | 
					noinst_PROGRAMS = \
 | 
				
			||||||
	USRPping \
 | 
					 | 
				
			||||||
	transceiver \
 | 
						transceiver \
 | 
				
			||||||
	sigProcLibTest 
 | 
						sigProcLibTest 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -81,14 +81,11 @@ noinst_HEADERS = \
 | 
				
			|||||||
	USRPDevice.h \
 | 
						USRPDevice.h \
 | 
				
			||||||
	DummyLoad.h \
 | 
						DummyLoad.h \
 | 
				
			||||||
	rcvLPF_651.h \
 | 
						rcvLPF_651.h \
 | 
				
			||||||
	sendLPF_961.h
 | 
						sendLPF_961.h \
 | 
				
			||||||
 | 
						RTMD.h \
 | 
				
			||||||
 | 
						cycle.h
 | 
				
			||||||
 | 
					
 | 
				
			||||||
USRPping_SOURCES = USRPping.cpp
 | 
					transceiver_SOURCES = multiTRX.cpp
 | 
				
			||||||
USRPping_LDADD = \
 | 
					 | 
				
			||||||
	libtransceiver.la \
 | 
					 | 
				
			||||||
	$(COMMON_LA) $(SQLITE_LA)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
transceiver_SOURCES = runTransceiver.cpp
 | 
					 | 
				
			||||||
transceiver_LDADD = \
 | 
					transceiver_LDADD = \
 | 
				
			||||||
	libtransceiver.la \
 | 
						libtransceiver.la \
 | 
				
			||||||
	$(GSM_LA) \
 | 
						$(GSM_LA) \
 | 
				
			||||||
@@ -104,13 +101,11 @@ sigProcLibTest_LDADD = \
 | 
				
			|||||||
if UHD
 | 
					if UHD
 | 
				
			||||||
libtransceiver_la_SOURCES += UHDDevice.cpp
 | 
					libtransceiver_la_SOURCES += UHDDevice.cpp
 | 
				
			||||||
transceiver_LDADD += $(UHD_LIBS)
 | 
					transceiver_LDADD += $(UHD_LIBS)
 | 
				
			||||||
USRPping_LDADD += $(UHD_LIBS)
 | 
					 | 
				
			||||||
sigProcLibTest_LDADD += $(UHD_LIBS)
 | 
					sigProcLibTest_LDADD += $(UHD_LIBS)
 | 
				
			||||||
else
 | 
					else
 | 
				
			||||||
if USRP1
 | 
					if USRP1
 | 
				
			||||||
libtransceiver_la_SOURCES += USRPDevice.cpp
 | 
					libtransceiver_la_SOURCES += USRPDevice.cpp
 | 
				
			||||||
transceiver_LDADD += $(USRP_LIBS)
 | 
					transceiver_LDADD += $(USRP_LIBS)
 | 
				
			||||||
USRPping_LDADD += $(USRP_LIBS)
 | 
					 | 
				
			||||||
sigProcLibTest_LDADD += $(USRP_LIBS)
 | 
					sigProcLibTest_LDADD += $(USRP_LIBS)
 | 
				
			||||||
else
 | 
					else
 | 
				
			||||||
#we should never be here, as one of the above mustbe defined for us to build
 | 
					#we should never be here, as one of the above mustbe defined for us to build
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										266
									
								
								Transceiver52M/RTMD.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										266
									
								
								Transceiver52M/RTMD.c
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,266 @@
 | 
				
			|||||||
 | 
					#include <stdio.h>
 | 
				
			||||||
 | 
					#include <sys/time.h>
 | 
				
			||||||
 | 
					#include <stdlib.h>
 | 
				
			||||||
 | 
					#include <string.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __hpux
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#include <stdint.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RTMD_THREAD_SAFE
 | 
				
			||||||
 | 
					//#define NO_CYCLE
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef WITH_RTMD
 | 
				
			||||||
 | 
					#define WITH_RTMD
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#include "RTMD.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define  secs       _unsec._tval.secs
 | 
				
			||||||
 | 
					#define  usecs      _unsec._tval.usecs
 | 
				
			||||||
 | 
					#define  val        _unval._sln.val
 | 
				
			||||||
 | 
					#define  lineNumber _unval._sln.lineNumber
 | 
				
			||||||
 | 
					#define  tick       _unsec.tick
 | 
				
			||||||
 | 
					#define  val64      _unval.val64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef NO_CYCLE
 | 
				
			||||||
 | 
					#include "cycle.h"
 | 
				
			||||||
 | 
					#define EXTRA_SPACE (1024)
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					#define EXTRA_SPACE 0
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static RTMD_Node_t* mem = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if _POSIX_C_SOURCE >= 200112L || _XOPEN_SOURCE >= 600
 | 
				
			||||||
 | 
					static void* node_alloc(size_t size)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    void* mem = NULL;
 | 
				
			||||||
 | 
					    int res = posix_memalign(&mem, sizeof(RTMD_Node_t), size*sizeof(RTMD_Node_t));
 | 
				
			||||||
 | 
					    if (res)
 | 
				
			||||||
 | 
						perror("posix_memalign");
 | 
				
			||||||
 | 
					    return mem;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					//#warning Compiling without posix_memalign
 | 
				
			||||||
 | 
					#define node_alloc(x)  malloc((x)*sizeof(RTMD_Node_t))
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Need to be volatile for proper work from signal handlers */
 | 
				
			||||||
 | 
					static volatile unsigned    ptr = 0;
 | 
				
			||||||
 | 
					static unsigned    count = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int RTMD_IsFull(void) {
 | 
				
			||||||
 | 
						return ptr==count;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RTMD_InitStorage(unsigned max)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						if (mem != NULL)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						mem = node_alloc((max + 2*EXTRA_SPACE));
 | 
				
			||||||
 | 
						count = max;
 | 
				
			||||||
 | 
						ptr = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef NO_CYCLE
 | 
				
			||||||
 | 
						for (i = 0; i < 32; i++)
 | 
				
			||||||
 | 
							RTMD_VAL("#init", i);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
						for (i = 0; i < 1024; i++)
 | 
				
			||||||
 | 
							RTMD_InitCycleTime("#cycle");
 | 
				
			||||||
 | 
						for (i = 0; i < 64; i++)
 | 
				
			||||||
 | 
							RTMD_VAL("#cyctst", i);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RTMD_FlushStorage(const char* filename)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						FILE* f;
 | 
				
			||||||
 | 
					#ifndef NO_CYCLE
 | 
				
			||||||
 | 
						int i;
 | 
				
			||||||
 | 
						for (i = 0; i < 1024; i++)
 | 
				
			||||||
 | 
							RTMD_InitCycleTime("#cycleend");
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						f = fopen(filename, "w+b");
 | 
				
			||||||
 | 
						if (f != NULL)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fwrite(mem, ptr * sizeof(RTMD_Node_t), 1, f);
 | 
				
			||||||
 | 
							fclose(f);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							fprintf(stderr, "RTMD: Can't flush RTMD to %s file!\n", filename);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						free(mem);
 | 
				
			||||||
 | 
						ptr = 0;
 | 
				
			||||||
 | 
						count = 0;
 | 
				
			||||||
 | 
						mem = NULL;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef NO_CYCLE
 | 
				
			||||||
 | 
					void RTMD_SetInt(const char* name, int clineNumber, int cval)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (mem == NULL)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct timeval tv;
 | 
				
			||||||
 | 
						int err = gettimeofday(&tv, NULL);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
					/*		fprintf(stderr, "RTMD: Can't get time!\n"); */
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						RTMD_SetTime(name, clineNumber, cval, &tv);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RTMD_SetTime(const char* name, int clineNumber, int cval, const struct timeval *tv)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (mem == NULL)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ptr < count)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							unsigned slot;
 | 
				
			||||||
 | 
					#ifdef __hpux
 | 
				
			||||||
 | 
							slot = ptr++;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
							slot =  __sync_fetch_and_add(&ptr, 1);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * You can use ' slot = ptr++; ' instead the instruction above,
 | 
				
			||||||
 | 
							 * but this doesn't guarantee proper work in a multi-thread application
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (slot < count)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								mem[slot].secs = tv->tv_sec;
 | 
				
			||||||
 | 
								mem[slot].usecs = tv->tv_usec;
 | 
				
			||||||
 | 
								mem[slot].lineNumber = clineNumber;
 | 
				
			||||||
 | 
								mem[slot].val = cval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								strncpy(mem[slot].name, name, RTMD_MAX_NAME - 1);
 | 
				
			||||||
 | 
								mem[slot].cycleFlag = RTMD_FLAG_GTOFDAY;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
					/* 		fprintf(stderr, "RTMD: Storage is full!\n");  */
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef NO_CYCLE
 | 
				
			||||||
 | 
					void RTMD_InitCycleTime(const char* name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct timeval tv;
 | 
				
			||||||
 | 
						int err = gettimeofday(&tv, NULL);
 | 
				
			||||||
 | 
						if (err)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
					/*		fprintf(stderr, "RTMD: Can't get time!\n"); */
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (mem == NULL)
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (ptr < count + EXTRA_SPACE)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							unsigned slot;
 | 
				
			||||||
 | 
					#ifdef __hpux
 | 
				
			||||||
 | 
							slot = ptr++;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
							slot =  __sync_fetch_and_add(&ptr, 1);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * You can use ' slot = ptr++; ' instead the instruction above,
 | 
				
			||||||
 | 
							 * but this doesn't guarantee proper work in a multi-thread application
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (slot < count + EXTRA_SPACE)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								mem[slot].secs = tv.tv_sec;
 | 
				
			||||||
 | 
								mem[slot].usecs = tv.tv_usec;
 | 
				
			||||||
 | 
								mem[slot].val64 = getticks();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								strncpy(mem[slot].name, name, RTMD_MAX_NAME - 1);
 | 
				
			||||||
 | 
								mem[slot].cycleFlag = RTMD_FLAG_CYCLEINI;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
					/* 		fprintf(stderr, "RTMD: Storage is full!\n");  */
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __linux
 | 
				
			||||||
 | 
					#define _GNU_SOURCE
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					#include <sys/types.h>
 | 
				
			||||||
 | 
					#include <sys/syscall.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					inline void RTMD_SetIntThread(const char*  name, int clineNumber, int cval)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    static __thread pid_t stid; // __thread variables can't be initialized (zeroed on clone)
 | 
				
			||||||
 | 
					    if (stid == 0) {
 | 
				
			||||||
 | 
					        stid = (pid_t) syscall (SYS_gettid);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    int s = -(int)stid;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    RTMD_SetInt(name, s, cval);
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RTMD_SetInt(const char*  name, int clineNumber, int cval)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if (ptr < count)
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
							unsigned slot;
 | 
				
			||||||
 | 
					#ifdef __hpux
 | 
				
			||||||
 | 
							slot = ptr++;
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					# ifdef RTMD_THREAD_SAFE
 | 
				
			||||||
 | 
							slot =  __sync_fetch_and_add(&ptr, 1);
 | 
				
			||||||
 | 
					# else
 | 
				
			||||||
 | 
							slot = ptr++;
 | 
				
			||||||
 | 
					# endif
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * You can use ' slot = ptr++; ' instead the instruction above,
 | 
				
			||||||
 | 
							 * but this doesn't guarantee proper work in a multi-thread application
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if (slot < count)
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								mem[slot].tick = getticks();
 | 
				
			||||||
 | 
								mem[slot].lineNumber = clineNumber;
 | 
				
			||||||
 | 
								mem[slot].val = cval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef RTMD_SAFE_ALIGN
 | 
				
			||||||
 | 
								strncpy(mem[slot].name, name, RTMD_MAX_NAME - 1);
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
								//May not work on PA-RISC due to memory align
 | 
				
			||||||
 | 
								*((int64_t * )mem[slot].name) = *((const int64_t * )name);
 | 
				
			||||||
 | 
								*((int64_t * )&mem[slot].name[8]) = *((const int64_t * )&name[8]);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
								mem[slot].cycleFlag = RTMD_FLAG_CYCLE;
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						else
 | 
				
			||||||
 | 
						{
 | 
				
			||||||
 | 
					/* 		fprintf(stderr, "RTMD: Storage is full!\n");  */
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
							
								
								
									
										102
									
								
								Transceiver52M/RTMD.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										102
									
								
								Transceiver52M/RTMD.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,102 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * RTMD.h
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 *  Created on: Oct 18, 2010
 | 
				
			||||||
 | 
					 *      Author: serg
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef RTMD_H_
 | 
				
			||||||
 | 
					#define RTMD_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C" {
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Library prototypes
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if defined WITH_RTMD && !defined NO_RTMD
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RTMD_InitStorage(unsigned max);
 | 
				
			||||||
 | 
					void RTMD_FlushStorage(const char* filename);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RTMD_SetInt(const char* name, int lineNumber, int val);
 | 
				
			||||||
 | 
					void RTMD_SetTime(const char* name, int lineNumber, int val, const struct timeval *tv);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Cycle extension */
 | 
				
			||||||
 | 
					void RTMD_SetCycleTime(const char* name, int lineNumber, int val);
 | 
				
			||||||
 | 
					void RTMD_InitCycleTime(const char* name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RTMD_SetIntThread(const char*  name, int clineNumber, int cval);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int RTMD_IsFull(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RTMD_MAX_NAME		16
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define RTMD_FLAG_GTOFDAY  'G'
 | 
				
			||||||
 | 
					#define RTMD_FLAG_CYCLE    'C'
 | 
				
			||||||
 | 
					#define RTMD_FLAG_CYCLEINI 'I'
 | 
				
			||||||
 | 
					#define RTMD_FLAG_THREADNM 'N'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef struct RTMD_Node {
 | 
				
			||||||
 | 
						char      name[RTMD_MAX_NAME-1];
 | 
				
			||||||
 | 
						char      cycleFlag;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							struct {
 | 
				
			||||||
 | 
								uint32_t   secs;
 | 
				
			||||||
 | 
								uint32_t   usecs;
 | 
				
			||||||
 | 
							} _tval;
 | 
				
			||||||
 | 
							uint64_t       tick;
 | 
				
			||||||
 | 
						} _unsec;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						union {
 | 
				
			||||||
 | 
							struct {
 | 
				
			||||||
 | 
								uint32_t   lineNumber;
 | 
				
			||||||
 | 
								uint32_t   val;
 | 
				
			||||||
 | 
							} _sln;
 | 
				
			||||||
 | 
							uint64_t       val64;
 | 
				
			||||||
 | 
						} _unval;
 | 
				
			||||||
 | 
					} RTMD_Node_t;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//# define RTMD_VAL(a, v)        RTMD_SetInt((a), __LINE__, (v))
 | 
				
			||||||
 | 
					//# define RTMD_SET(a)           RTMD_SetInt((a), __LINE__, 1)
 | 
				
			||||||
 | 
					//# define RTMD_CLEAR(a)         RTMD_SetInt((a), __LINE__, 0)
 | 
				
			||||||
 | 
					//# define RTMD_PULSE(a)         ( RTMD_SET(a), RTMD_CLEAR(a))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# define RTMD_THREAD_NAME(a)       RTMD_SetThreadName(a)
 | 
				
			||||||
 | 
					# define RTMD_VAL(a, v)     RTMD_SetIntThread((a), __LINE__, (v))
 | 
				
			||||||
 | 
					# define RTMD_SET(a)        RTMD_SetIntThread((a), __LINE__, 1)
 | 
				
			||||||
 | 
					# define RTMD_CLEAR(a)      RTMD_SetIntThread((a), __LINE__, 0)
 | 
				
			||||||
 | 
					# define RTMD_PULSE(a)      ( RTMD_SET(a), RTMD_CLEAR(a))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# define RTMD_INIT(x)             RTMD_InitStorage((x))
 | 
				
			||||||
 | 
					# define RTMD_FLUSH(x)            RTMD_FlushStorage((x))
 | 
				
			||||||
 | 
					# define RTMD_SETTIME(a, tv)      RTMD_SetTime((a), __LINE__, 1, tv);
 | 
				
			||||||
 | 
					# define RTMD_CLEARTIME(a, tv)    RTMD_SetTime((a), __LINE__, 0, tv);
 | 
				
			||||||
 | 
					# define RTMD_FULL	              RTMD_IsFull
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					# define RTMD_VAL(a, v)
 | 
				
			||||||
 | 
					# define RTMD_SET(a)
 | 
				
			||||||
 | 
					# define RTMD_CLEAR(a)
 | 
				
			||||||
 | 
					# define RTMD_PULSE(a)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# define RTMD_TH_NAME(a)
 | 
				
			||||||
 | 
					# define RTMD_TH_VAL(a, v)
 | 
				
			||||||
 | 
					# define RTMD_TH_SET(a)
 | 
				
			||||||
 | 
					# define RTMD_TH_CLEAR(a)
 | 
				
			||||||
 | 
					# define RTMD_TH_PULSE(a)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# define RTMD_INIT(x)
 | 
				
			||||||
 | 
					# define RTMD_FLUSH(x)
 | 
				
			||||||
 | 
					# define RTMD_SETTIME(a, tv) 
 | 
				
			||||||
 | 
					# define RTMD_CLEARTIME(a, tv) 
 | 
				
			||||||
 | 
					# define RTMD_FULL
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef __cplusplus
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#endif /* RTMD_H_ */
 | 
				
			||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009, 2010 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2010, 2012 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Public License.
 | 
					* This software is distributed under the terms of the GNU Public License.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* See the COPYING file in the main directory for details.
 | 
				
			||||||
@@ -31,6 +32,7 @@
 | 
				
			|||||||
#include <stdio.h>
 | 
					#include <stdio.h>
 | 
				
			||||||
#include "Transceiver.h"
 | 
					#include "Transceiver.h"
 | 
				
			||||||
#include <Logger.h>
 | 
					#include <Logger.h>
 | 
				
			||||||
 | 
					#include "RTMD.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef HAVE_CONFIG_H
 | 
					#ifdef HAVE_CONFIG_H
 | 
				
			||||||
#include "config.h"
 | 
					#include "config.h"
 | 
				
			||||||
@@ -48,72 +50,57 @@ using namespace GSM;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#define INIT_ENERGY_THRSHD		5.0f
 | 
					#define INIT_ENERGY_THRSHD		5.0f
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Transceiver::Transceiver(int wBasePort,
 | 
					Transceiver::Transceiver(int wBasePort, const char *TRXAddress,
 | 
				
			||||||
			 const char *TRXAddress,
 | 
								 DriveLoop *wDriveLoop, RadioInterface *wRadioInterface,
 | 
				
			||||||
			 int wSamplesPerSymbol,
 | 
								 int wSamplesPerSymbol, int wChannel, bool wPrimary)
 | 
				
			||||||
			 GSM::Time wTransmitLatency,
 | 
						:mBasePort(wBasePort), mTRXAddress(TRXAddress),
 | 
				
			||||||
			 RadioInterface *wRadioInterface)
 | 
						 mDataSocket(wBasePort+2,TRXAddress,wBasePort+102),
 | 
				
			||||||
	:mDataSocket(wBasePort+2,TRXAddress,wBasePort+102),
 | 
					 | 
				
			||||||
	 mControlSocket(wBasePort+1,TRXAddress,wBasePort+101),
 | 
						 mControlSocket(wBasePort+1,TRXAddress,wBasePort+101),
 | 
				
			||||||
	 mClockSocket(wBasePort,TRXAddress,wBasePort+100)
 | 
						 mDriveLoop(wDriveLoop), mRadioInterface(wRadioInterface),
 | 
				
			||||||
 | 
						 mSamplesPerSymbol(wSamplesPerSymbol), mTransmitPriorityQueue(NULL),
 | 
				
			||||||
 | 
						 mChannel(wChannel), mPrimary(wPrimary)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  //GSM::Time startTime(0,0);
 | 
					 | 
				
			||||||
  //GSM::Time startTime(gHyperframe/2 - 4*216*60,0);
 | 
					 | 
				
			||||||
  GSM::Time startTime(random() % gHyperframe,0);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  mFIFOServiceLoopThread = new Thread(32768);  ///< thread to push bursts into transmit FIFO
 | 
					 | 
				
			||||||
  mControlServiceLoopThread = new Thread(32768);       ///< thread to process control messages from GSM core
 | 
					 | 
				
			||||||
  mTransmitPriorityQueueServiceLoopThread = new Thread(32768);///< thread to process transmit bursts from GSM core
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  mSamplesPerSymbol = wSamplesPerSymbol;
 | 
					 | 
				
			||||||
  mRadioInterface = wRadioInterface;
 | 
					 | 
				
			||||||
  mTransmitLatency = wTransmitLatency;
 | 
					 | 
				
			||||||
  mTransmitDeadlineClock = startTime;
 | 
					 | 
				
			||||||
  mLastClockUpdateTime = startTime;
 | 
					 | 
				
			||||||
  mLatencyUpdateTime = startTime;
 | 
					 | 
				
			||||||
  mRadioInterface->getClock()->set(startTime);
 | 
					 | 
				
			||||||
  mMaxExpectedDelay = 0;
 | 
					  mMaxExpectedDelay = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // generate pulse and setup up signal processing library
 | 
					  // generate pulse and setup up signal processing library
 | 
				
			||||||
  gsmPulse = generateGSMPulse(2,mSamplesPerSymbol);
 | 
					  gsmPulse = generateGSMPulse(2,mSamplesPerSymbol);
 | 
				
			||||||
  LOG(DEBUG) << "gsmPulse: " << *gsmPulse;
 | 
					  LOG(DEBUG) << "gsmPulse: " << *gsmPulse;
 | 
				
			||||||
  sigProcLibSetup(mSamplesPerSymbol);
 | 
					
 | 
				
			||||||
 | 
					  mTransmitPriorityQueue = mDriveLoop->priorityQueue(mChannel);
 | 
				
			||||||
 | 
					  mReceiveFIFO = mRadioInterface->receiveFIFO(mChannel);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  txFullScale = mRadioInterface->fullScaleInputValue();
 | 
					  txFullScale = mRadioInterface->fullScaleInputValue();
 | 
				
			||||||
  rxFullScale = mRadioInterface->fullScaleOutputValue();
 | 
					  rxFullScale = mRadioInterface->fullScaleOutputValue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // initialize filler tables with dummy bursts, initialize other per-timeslot variables
 | 
					  // initialize per-timeslot variables
 | 
				
			||||||
  for (int i = 0; i < 8; i++) {
 | 
					  for (int i = 0; i < 8; i++) {
 | 
				
			||||||
    signalVector* modBurst = modulateBurst(gDummyBurst,*gsmPulse,
 | 
					 | 
				
			||||||
					   8 + (i % 4 == 0),
 | 
					 | 
				
			||||||
					   mSamplesPerSymbol);
 | 
					 | 
				
			||||||
    scaleVector(*modBurst,txFullScale);
 | 
					 | 
				
			||||||
    fillerModulus[i]=26;
 | 
					 | 
				
			||||||
    for (int j = 0; j < 102; j++) {
 | 
					 | 
				
			||||||
      fillerTable[j][i] = new signalVector(*modBurst);
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
    delete modBurst;
 | 
					 | 
				
			||||||
    mChanType[i] = NONE;
 | 
					 | 
				
			||||||
    channelResponse[i] = NULL;
 | 
					    channelResponse[i] = NULL;
 | 
				
			||||||
    DFEForward[i] = NULL;
 | 
					    DFEForward[i] = NULL;
 | 
				
			||||||
    DFEFeedback[i] = NULL;
 | 
					    DFEFeedback[i] = NULL;
 | 
				
			||||||
    channelEstimateTime[i] = startTime;
 | 
					    channelEstimateTime[i] = mDriveLoop->getStartTime();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mOn = false;
 | 
					  mOn = false;
 | 
				
			||||||
 | 
					  mRunning = false;
 | 
				
			||||||
  mTxFreq = 0.0;
 | 
					  mTxFreq = 0.0;
 | 
				
			||||||
  mRxFreq = 0.0;
 | 
					  mRxFreq = 0.0;
 | 
				
			||||||
 | 
					  mFreqOffset = 0.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mPower = -10;
 | 
					  mPower = -10;
 | 
				
			||||||
  mEnergyThreshold = INIT_ENERGY_THRSHD;
 | 
					  mEnergyThreshold = INIT_ENERGY_THRSHD;
 | 
				
			||||||
  prevFalseDetectionTime = startTime;
 | 
					  prevFalseDetectionTime = mDriveLoop->getStartTime();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Transceiver::~Transceiver()
 | 
					Transceiver::~Transceiver()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					  // Stop all threads before freeing up
 | 
				
			||||||
 | 
					  mFIFOServiceLoop.shutdown();
 | 
				
			||||||
 | 
					  mControlServiceLoop.shutdown();
 | 
				
			||||||
 | 
					  mTransmitPriorityQueueServiceLoop.shutdown();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  // Now free all allocated data
 | 
				
			||||||
  delete gsmPulse;
 | 
					  delete gsmPulse;
 | 
				
			||||||
  sigProcLibDestroy();
 | 
					  mTransmitPriorityQueue->clear();
 | 
				
			||||||
  mTransmitPriorityQueue.clear();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -127,193 +114,36 @@ void Transceiver::addRadioVector(BitVector &burst,
 | 
				
			|||||||
					 mSamplesPerSymbol);
 | 
										 mSamplesPerSymbol);
 | 
				
			||||||
  scaleVector(*modBurst,txFullScale * pow(10,-RSSI/10));
 | 
					  scaleVector(*modBurst,txFullScale * pow(10,-RSSI/10));
 | 
				
			||||||
  radioVector *newVec = new radioVector(*modBurst,wTime);
 | 
					  radioVector *newVec = new radioVector(*modBurst,wTime);
 | 
				
			||||||
  mTransmitPriorityQueue.write(newVec);
 | 
					  mTransmitPriorityQueue->write(newVec);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  delete modBurst;
 | 
					  delete modBurst;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef TRANSMIT_LOGGING
 | 
					 | 
				
			||||||
void Transceiver::unModulateVector(signalVector wVector) 
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  SoftVector *burst = demodulateBurst(wVector,
 | 
					 | 
				
			||||||
				   *gsmPulse,
 | 
					 | 
				
			||||||
				   mSamplesPerSymbol,
 | 
					 | 
				
			||||||
				   1.0,0.0);
 | 
					 | 
				
			||||||
  LOG(DEBUG) << "LOGGED BURST: " << *burst;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
  unsigned char burstStr[gSlotLen+1];
 | 
					 | 
				
			||||||
  SoftVector::iterator burstItr = burst->begin();
 | 
					 | 
				
			||||||
  for (int i = 0; i < gSlotLen; i++) {
 | 
					 | 
				
			||||||
    // FIXME: Demod bits are inverted!
 | 
					 | 
				
			||||||
    burstStr[i] = (unsigned char) ((*burstItr++)*255.0);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  burstStr[gSlotLen]='\0';
 | 
					 | 
				
			||||||
  LOG(DEBUG) << "LOGGED BURST: " << burstStr;
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
  delete burst;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void Transceiver::pushRadioVector(GSM::Time &nowTime)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // dump stale bursts, if any
 | 
					 | 
				
			||||||
  while (radioVector* staleBurst = mTransmitPriorityQueue.getStaleBurst(nowTime)) {
 | 
					 | 
				
			||||||
    // Even if the burst is stale, put it in the fillter table.
 | 
					 | 
				
			||||||
    // (It might be an idle pattern.)
 | 
					 | 
				
			||||||
    LOG(NOTICE) << "dumping STALE burst in TRX->USRP interface";
 | 
					 | 
				
			||||||
    const GSM::Time& nextTime = staleBurst->getTime();
 | 
					 | 
				
			||||||
    int TN = nextTime.TN();
 | 
					 | 
				
			||||||
    int modFN = nextTime.FN() % fillerModulus[TN];
 | 
					 | 
				
			||||||
    delete fillerTable[modFN][TN];
 | 
					 | 
				
			||||||
    fillerTable[modFN][TN] = staleBurst;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  int TN = nowTime.TN();
 | 
					 | 
				
			||||||
  int modFN = nowTime.FN() % fillerModulus[nowTime.TN()];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // if queue contains data at the desired timestamp, stick it into FIFO
 | 
					 | 
				
			||||||
  if (radioVector *next = (radioVector*) mTransmitPriorityQueue.getCurrentBurst(nowTime)) {
 | 
					 | 
				
			||||||
    LOG(DEBUG) << "transmitFIFO: wrote burst " << next << " at time: " << nowTime;
 | 
					 | 
				
			||||||
    delete fillerTable[modFN][TN];
 | 
					 | 
				
			||||||
    fillerTable[modFN][TN] = new signalVector(*(next));
 | 
					 | 
				
			||||||
    mRadioInterface->driveTransmitRadio(*(next),(mChanType[TN]==NONE)); //fillerTable[modFN][TN]));
 | 
					 | 
				
			||||||
    delete next;
 | 
					 | 
				
			||||||
#ifdef TRANSMIT_LOGGING
 | 
					 | 
				
			||||||
    if (nowTime.TN()==TRANSMIT_LOGGING) { 
 | 
					 | 
				
			||||||
      unModulateVector(*(fillerTable[modFN][TN]));
 | 
					 | 
				
			||||||
    }
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
    return;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  // otherwise, pull filler data, and push to radio FIFO
 | 
					 | 
				
			||||||
  mRadioInterface->driveTransmitRadio(*(fillerTable[modFN][TN]),(mChanType[TN]==NONE));
 | 
					 | 
				
			||||||
#ifdef TRANSMIT_LOGGING
 | 
					 | 
				
			||||||
  if (nowTime.TN()==TRANSMIT_LOGGING) 
 | 
					 | 
				
			||||||
    unModulateVector(*fillerTable[modFN][TN]);
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void Transceiver::setModulus(int timeslot)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  switch (mChanType[timeslot]) {
 | 
					 | 
				
			||||||
  case NONE:
 | 
					 | 
				
			||||||
  case I:
 | 
					 | 
				
			||||||
  case II:
 | 
					 | 
				
			||||||
  case III:
 | 
					 | 
				
			||||||
  case FILL:
 | 
					 | 
				
			||||||
    fillerModulus[timeslot] = 26;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  case IV:
 | 
					 | 
				
			||||||
  case VI:
 | 
					 | 
				
			||||||
  case V:
 | 
					 | 
				
			||||||
    fillerModulus[timeslot] = 51;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
    //case V: 
 | 
					 | 
				
			||||||
  case VII:
 | 
					 | 
				
			||||||
    fillerModulus[timeslot] = 102;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  case XIII:
 | 
					 | 
				
			||||||
    fillerModulus[timeslot] = 52;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  default:
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  unsigned burstTN = currTime.TN();
 | 
					 | 
				
			||||||
  unsigned burstFN = currTime.FN();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  switch (mChanType[burstTN]) {
 | 
					 | 
				
			||||||
  case NONE:
 | 
					 | 
				
			||||||
    return OFF;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  case FILL:
 | 
					 | 
				
			||||||
    return IDLE;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  case I:
 | 
					 | 
				
			||||||
    return TSC;
 | 
					 | 
				
			||||||
    /*if (burstFN % 26 == 25) 
 | 
					 | 
				
			||||||
      return IDLE;
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      return TSC;*/
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  case II:
 | 
					 | 
				
			||||||
    return TSC;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  case III:
 | 
					 | 
				
			||||||
    return TSC;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  case IV:
 | 
					 | 
				
			||||||
  case VI:
 | 
					 | 
				
			||||||
    return RACH;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  case V: {
 | 
					 | 
				
			||||||
    int mod51 = burstFN % 51;
 | 
					 | 
				
			||||||
    if ((mod51 <= 36) && (mod51 >= 14))
 | 
					 | 
				
			||||||
      return RACH;
 | 
					 | 
				
			||||||
    else if ((mod51 == 4) || (mod51 == 5))
 | 
					 | 
				
			||||||
      return RACH;
 | 
					 | 
				
			||||||
    else if ((mod51 == 45) || (mod51 == 46))
 | 
					 | 
				
			||||||
      return RACH;
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      return TSC;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  case VII:
 | 
					 | 
				
			||||||
    if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
 | 
					 | 
				
			||||||
      return IDLE;
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      return TSC;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  case XIII: {
 | 
					 | 
				
			||||||
    int mod52 = burstFN % 52;
 | 
					 | 
				
			||||||
    if ((mod52 == 12) || (mod52 == 38))
 | 
					 | 
				
			||||||
      return RACH;
 | 
					 | 
				
			||||||
    else if ((mod52 == 25) || (mod52 == 51))
 | 
					 | 
				
			||||||
      return IDLE;
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      return TSC;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  case LOOPBACK:
 | 
					 | 
				
			||||||
    if ((burstFN % 51 <= 50) && (burstFN % 51 >=48))
 | 
					 | 
				
			||||||
      return IDLE;
 | 
					 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      return TSC;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  default:
 | 
					 | 
				
			||||||
    return OFF;
 | 
					 | 
				
			||||||
    break;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
 | 
					SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
 | 
				
			||||||
				      int &RSSI,
 | 
									      int &RSSI,
 | 
				
			||||||
				      int &timingOffset)
 | 
									      int &timingOffset)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					  RTMD_SET("TRX-pullRadioVector");
 | 
				
			||||||
  bool needDFE = (mMaxExpectedDelay > 1);
 | 
					  bool needDFE = (mMaxExpectedDelay > 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  radioVector *rxBurst = (radioVector *) mReceiveFIFO->get();
 | 
					  radioVector *rxBurst = (radioVector *) mReceiveFIFO->read();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!rxBurst) return NULL;
 | 
					  if (!rxBurst) {
 | 
				
			||||||
 | 
					    RTMD_VAL("TRX-pullRadioVector", -1);
 | 
				
			||||||
 | 
					    RTMD_CLEAR("TRX-pullRadioVector");
 | 
				
			||||||
 | 
					    return NULL;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  LOG(DEBUG) << "receiveFIFO: read radio vector at time: " << rxBurst->getTime() << ", new size: " << mReceiveFIFO->size();
 | 
					  LOG(DEBUG) << "receiveFIFO: read radio vector at time: " << rxBurst->getTime() << ", new size: " << mReceiveFIFO->size();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  int timeslot = rxBurst->getTime().TN();
 | 
					  int timeslot = rxBurst->getTime().TN();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  CorrType corrType = expectedCorrType(rxBurst->getTime());
 | 
					  DriveLoop::CorrType corrType = mDriveLoop->expectedCorrType(mChannel, rxBurst->getTime());
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if ((corrType==OFF) || (corrType==IDLE)) {
 | 
					  if ((corrType == DriveLoop::OFF) || (corrType == DriveLoop::IDLE)) {
 | 
				
			||||||
    delete rxBurst;
 | 
					    delete rxBurst;
 | 
				
			||||||
 | 
					    RTMD_VAL("TRX-pullRadioVector", -2);
 | 
				
			||||||
 | 
					    RTMD_CLEAR("TRX-pullRadioVector");
 | 
				
			||||||
    return NULL;
 | 
					    return NULL;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
@@ -322,6 +152,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
 | 
				
			|||||||
  complex amplitude = 0.0;
 | 
					  complex amplitude = 0.0;
 | 
				
			||||||
  float TOA = 0.0;
 | 
					  float TOA = 0.0;
 | 
				
			||||||
  float avgPwr = 0.0;
 | 
					  float avgPwr = 0.0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!energyDetect(*vectorBurst,20*mSamplesPerSymbol,mEnergyThreshold,&avgPwr)) {
 | 
					  if (!energyDetect(*vectorBurst,20*mSamplesPerSymbol,mEnergyThreshold,&avgPwr)) {
 | 
				
			||||||
     LOG(DEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->getTime();
 | 
					     LOG(DEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->getTime();
 | 
				
			||||||
     double framesElapsed = rxBurst->getTime()-prevFalseDetectionTime;
 | 
					     double framesElapsed = rxBurst->getTime()-prevFalseDetectionTime;
 | 
				
			||||||
@@ -333,14 +164,17 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
 | 
				
			|||||||
        prevFalseDetectionTime = rxBurst->getTime();
 | 
					        prevFalseDetectionTime = rxBurst->getTime();
 | 
				
			||||||
     }
 | 
					     }
 | 
				
			||||||
     delete rxBurst;
 | 
					     delete rxBurst;
 | 
				
			||||||
 | 
					     RTMD_VAL("TRX-pullRadioVector", -3);
 | 
				
			||||||
 | 
					     RTMD_CLEAR("TRX-pullRadioVector");
 | 
				
			||||||
     return NULL;
 | 
					     return NULL;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  LOG(DEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->getTime();
 | 
					  LOG(DEBUG) << "Estimated Energy: " << sqrt(avgPwr) << ", at time " << rxBurst->getTime();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // run the proper correlator
 | 
					  // run the proper correlator
 | 
				
			||||||
  bool success = false;
 | 
					  bool success = false;
 | 
				
			||||||
  if (corrType==TSC) {
 | 
					  if (corrType == DriveLoop::TSC) {
 | 
				
			||||||
    LOG(DEBUG) << "looking for TSC at time: " << rxBurst->getTime();
 | 
					    LOG(DEBUG) << "looking for TSC at time: " << rxBurst->getTime();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    signalVector *channelResp;
 | 
					    signalVector *channelResp;
 | 
				
			||||||
    double framesElapsed = rxBurst->getTime()-channelEstimateTime[timeslot];
 | 
					    double framesElapsed = rxBurst->getTime()-channelEstimateTime[timeslot];
 | 
				
			||||||
    bool estimateChannel = false;
 | 
					    bool estimateChannel = false;
 | 
				
			||||||
@@ -409,11 +243,12 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  LOG(DEBUG) << "energy Threshold = " << mEnergyThreshold; 
 | 
					  LOG(DEBUG) << "energy Threshold = " << mEnergyThreshold; 
 | 
				
			||||||
 | 
					  RTMD_VAL("TRX-EnergyThresh", int(mEnergyThreshold*10));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // demodulate burst
 | 
					  // demodulate burst
 | 
				
			||||||
  SoftVector *burst = NULL;
 | 
					  SoftVector *burst = NULL;
 | 
				
			||||||
  if ((rxBurst) && (success)) {
 | 
					  if ((rxBurst) && (success)) {
 | 
				
			||||||
    if ((corrType==RACH) || (!needDFE)) {
 | 
					    if ((corrType == DriveLoop::RACH) || (!needDFE)) {
 | 
				
			||||||
      burst = demodulateBurst(*vectorBurst,
 | 
					      burst = demodulateBurst(*vectorBurst,
 | 
				
			||||||
			      *gsmPulse,
 | 
								      *gsmPulse,
 | 
				
			||||||
			      mSamplesPerSymbol,
 | 
								      mSamplesPerSymbol,
 | 
				
			||||||
@@ -437,24 +272,84 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  delete rxBurst;
 | 
					  delete rxBurst;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  RTMD_CLEAR("TRX-pullRadioVector");
 | 
				
			||||||
  return burst;
 | 
					  return burst;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Transceiver::pullFIFO()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  RTMD_SET("TRX-pullFIFO");
 | 
				
			||||||
 | 
					  SoftVector *rxBurst = NULL;
 | 
				
			||||||
 | 
					  int RSSI;
 | 
				
			||||||
 | 
					  int TOA;  // in 1/256 of a symbol
 | 
				
			||||||
 | 
					  GSM::Time burstTime;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  rxBurst = pullRadioVector(burstTime,RSSI,TOA);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (rxBurst) {
 | 
				
			||||||
 | 
					    LOG(DEBUG) << "burst parameters: "
 | 
				
			||||||
 | 
					               << " time: " << burstTime
 | 
				
			||||||
 | 
					               << " RSSI: " << RSSI
 | 
				
			||||||
 | 
					               << " TOA: "  << TOA
 | 
				
			||||||
 | 
					               << " bits: " << *rxBurst;
 | 
				
			||||||
 | 
					    RTMD_VAL("TRX-RSSI", RSSI);
 | 
				
			||||||
 | 
					    RTMD_CLEAR("TRX-RSSI");
 | 
				
			||||||
 | 
					    RTMD_VAL("TRX-TOA", TOA);
 | 
				
			||||||
 | 
					    RTMD_CLEAR("TRX-TOA");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    char burstString[gSlotLen+10];
 | 
				
			||||||
 | 
					    burstString[0] = burstTime.TN();
 | 
				
			||||||
 | 
					    for (int i = 0; i < 4; i++) {
 | 
				
			||||||
 | 
					            burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    burstString[5] = RSSI;
 | 
				
			||||||
 | 
					    burstString[6] = (TOA >> 8) & 0x0ff;
 | 
				
			||||||
 | 
					    burstString[7] = TOA & 0x0ff;
 | 
				
			||||||
 | 
					    SoftVector::iterator burstItr = rxBurst->begin();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    for (unsigned int i = 0; i < gSlotLen; i++) {
 | 
				
			||||||
 | 
					            burstString[8+i] =(char) round((*burstItr++)*255.0);
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    burstString[gSlotLen+9] = '\0';
 | 
				
			||||||
 | 
					    delete rxBurst;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    mDataSocket.write(burstString,gSlotLen+10);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  RTMD_CLEAR("TRX-pullFIFO");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Transceiver::start()
 | 
					void Transceiver::start()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  mControlServiceLoopThread->start((void * (*)(void*))ControlServiceLoopAdapter,(void*) this);
 | 
					  mRunning = true;
 | 
				
			||||||
 | 
					  mControlServiceLoop.startThread((void*) this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (!mPrimary) {
 | 
				
			||||||
 | 
					    mOn = true;
 | 
				
			||||||
 | 
					    mFIFOServiceLoop.startThread((void*) this);
 | 
				
			||||||
 | 
					    mTransmitPriorityQueueServiceLoop.startThread((void*) this);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void Transceiver::shutdown()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  mOn = false;
 | 
				
			||||||
 | 
					  mRunning = false;
 | 
				
			||||||
 | 
					  mControlServiceLoop.shutdown();
 | 
				
			||||||
 | 
					  mFIFOServiceLoop.shutdown();
 | 
				
			||||||
 | 
					  mTransmitPriorityQueueServiceLoop.shutdown();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Transceiver::reset()
 | 
					void Transceiver::reset()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  mTransmitPriorityQueue.clear();
 | 
					  mTransmitPriorityQueue->clear();
 | 
				
			||||||
  //mTransmitFIFO->clear();
 | 
					 | 
				
			||||||
  //mReceiveFIFO->clear();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
void Transceiver::driveControl()
 | 
					void Transceiver::driveControl()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					  RTMD_SET("driveControl");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  int MAX_PACKET_LENGTH = 100;
 | 
					  int MAX_PACKET_LENGTH = 100;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -463,9 +358,24 @@ void Transceiver::driveControl()
 | 
				
			|||||||
  int msgLen = -1;
 | 
					  int msgLen = -1;
 | 
				
			||||||
  buffer[0] = '\0';
 | 
					  buffer[0] = '\0';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  try { 
 | 
				
			||||||
    msgLen = mControlSocket.read(buffer);
 | 
					    msgLen = mControlSocket.read(buffer);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    if (msgLen < 1) {
 | 
					    if (msgLen < 1) {
 | 
				
			||||||
 | 
					      RTMD_VAL("driveControl", -1);
 | 
				
			||||||
 | 
					      RTMD_CLEAR("driveControl");
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } catch (...) {
 | 
				
			||||||
 | 
					    /* Ignore the read exception on shutdown */
 | 
				
			||||||
 | 
					    if (!mRunning) {
 | 
				
			||||||
 | 
					      RTMD_VAL("driveControl", -2);
 | 
				
			||||||
 | 
					      RTMD_CLEAR("driveControl");
 | 
				
			||||||
 | 
					      return;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LOG(ALERT) << "Caught UHD socket exception";
 | 
				
			||||||
 | 
					    RTMD_VAL("driveControl", -3);
 | 
				
			||||||
 | 
					    RTMD_CLEAR("driveControl");
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -475,10 +385,12 @@ void Transceiver::driveControl()
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  sscanf(buffer,"%3s %s",cmdcheck,command);
 | 
					  sscanf(buffer,"%3s %s",cmdcheck,command);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  writeClockInterface();
 | 
					  mDriveLoop->writeClockInterface();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (strcmp(cmdcheck,"CMD")!=0) {
 | 
					  if (strcmp(cmdcheck,"CMD")!=0) {
 | 
				
			||||||
    LOG(WARNING) << "bogus message on control interface";
 | 
					    LOG(WARNING) << "bogus message on control interface";
 | 
				
			||||||
 | 
					    RTMD_VAL("driveControl", -4);
 | 
				
			||||||
 | 
					    RTMD_CLEAR("driveControl");
 | 
				
			||||||
    return;
 | 
					    return;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  LOG(INFO) << "command is " << buffer;
 | 
					  LOG(INFO) << "command is " << buffer;
 | 
				
			||||||
@@ -489,22 +401,23 @@ void Transceiver::driveControl()
 | 
				
			|||||||
  }
 | 
					  }
 | 
				
			||||||
  else if (strcmp(command,"POWERON")==0) {
 | 
					  else if (strcmp(command,"POWERON")==0) {
 | 
				
			||||||
    // turn on transmitter/demod
 | 
					    // turn on transmitter/demod
 | 
				
			||||||
    if (!mTxFreq || !mRxFreq) 
 | 
					    if (!mTxFreq || !mRxFreq || (mTSC<0))
 | 
				
			||||||
      sprintf(response,"RSP POWERON 1");
 | 
					      sprintf(response,"RSP POWERON 1");
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
      sprintf(response,"RSP POWERON 0");
 | 
					      sprintf(response,"RSP POWERON 0");
 | 
				
			||||||
      if (!mOn) {
 | 
					      if (mPrimary && !mOn) {
 | 
				
			||||||
        // Prepare for thread start
 | 
					        // Prepare for thread start
 | 
				
			||||||
        mPower = -20;
 | 
					        mPower = -20;
 | 
				
			||||||
        mRadioInterface->start();
 | 
					        mRadioInterface->start();
 | 
				
			||||||
 | 
					        mDriveLoop->startThread();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        mDriveLoop->writeClockInterface();
 | 
				
			||||||
        generateRACHSequence(*gsmPulse,mSamplesPerSymbol);
 | 
					        generateRACHSequence(*gsmPulse,mSamplesPerSymbol);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        // Start radio interface threads.
 | 
					        // Start radio interface threads.
 | 
				
			||||||
        mFIFOServiceLoopThread->start((void * (*)(void*))FIFOServiceLoopAdapter,(void*) this);
 | 
					 | 
				
			||||||
        mTransmitPriorityQueueServiceLoopThread->start((void * (*)(void*))TransmitPriorityQueueServiceLoopAdapter,(void*) this);
 | 
					 | 
				
			||||||
        writeClockInterface();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        mOn = true;
 | 
					        mOn = true;
 | 
				
			||||||
 | 
					        mFIFOServiceLoop.startThread((void*) this);
 | 
				
			||||||
 | 
					        mTransmitPriorityQueueServiceLoop.startThread((void*) this);
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -519,8 +432,8 @@ void Transceiver::driveControl()
 | 
				
			|||||||
    //set expected maximum time-of-arrival
 | 
					    //set expected maximum time-of-arrival
 | 
				
			||||||
    int newGain;
 | 
					    int newGain;
 | 
				
			||||||
    sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain);
 | 
					    sscanf(buffer,"%3s %s %d",cmdcheck,command,&newGain);
 | 
				
			||||||
    newGain = mRadioInterface->setRxGain(newGain);
 | 
					 | 
				
			||||||
    mEnergyThreshold = INIT_ENERGY_THRSHD;
 | 
					    mEnergyThreshold = INIT_ENERGY_THRSHD;
 | 
				
			||||||
 | 
					    newGain = mRadioInterface->setRxGain(newGain, mChannel);
 | 
				
			||||||
    sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
 | 
					    sprintf(response,"RSP SETRXGAIN 0 %d",newGain);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  else if (strcmp(command,"NOISELEV")==0) {
 | 
					  else if (strcmp(command,"NOISELEV")==0) {
 | 
				
			||||||
@@ -540,7 +453,7 @@ void Transceiver::driveControl()
 | 
				
			|||||||
      sprintf(response,"RSP SETPOWER 1 %d",dbPwr);
 | 
					      sprintf(response,"RSP SETPOWER 1 %d",dbPwr);
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
      mPower = dbPwr;
 | 
					      mPower = dbPwr;
 | 
				
			||||||
      mRadioInterface->setPowerAttenuation(dbPwr);
 | 
					      mRadioInterface->setPowerAttenuation(dbPwr, mChannel);
 | 
				
			||||||
      sprintf(response,"RSP SETPOWER 0 %d",dbPwr);
 | 
					      sprintf(response,"RSP SETPOWER 0 %d",dbPwr);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -555,37 +468,36 @@ void Transceiver::driveControl()
 | 
				
			|||||||
      sprintf(response,"RSP ADJPOWER 0 %d",mPower);
 | 
					      sprintf(response,"RSP ADJPOWER 0 %d",mPower);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
#define FREQOFFSET 0//11.2e3
 | 
					 | 
				
			||||||
  else if (strcmp(command,"RXTUNE")==0) {
 | 
					  else if (strcmp(command,"RXTUNE")==0) {
 | 
				
			||||||
    // tune receiver
 | 
					    // tune receiver
 | 
				
			||||||
    int freqKhz;
 | 
					    int freqKhz;
 | 
				
			||||||
    sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
 | 
					    sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
 | 
				
			||||||
    mRxFreq = freqKhz*1.0e3+FREQOFFSET;
 | 
					    mRxFreq = freqKhz * 1.0e3 + mFreqOffset;
 | 
				
			||||||
    if (!mRadioInterface->tuneRx(mRxFreq)) {
 | 
					    if (!mRadioInterface->tuneRx(mRxFreq, mChannel)) {
 | 
				
			||||||
      LOG(ALERT) << "RX failed to tune";
 | 
					      LOG(ALERT) << "RX failed to tune";
 | 
				
			||||||
      sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
 | 
					      sprintf(response,"RSP RXTUNE 1 %d",freqKhz);
 | 
				
			||||||
    }
 | 
					    } else {
 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
 | 
					      sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  else if (strcmp(command,"TXTUNE")==0) {
 | 
					  else if (strcmp(command,"TXTUNE")==0) {
 | 
				
			||||||
    // tune txmtr
 | 
					    // tune txmtr
 | 
				
			||||||
    int freqKhz;
 | 
					    int freqKhz;
 | 
				
			||||||
    sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
 | 
					    sscanf(buffer,"%3s %s %d",cmdcheck,command,&freqKhz);
 | 
				
			||||||
    //freqKhz = 890e3;
 | 
					    //freqKhz = 890e3;
 | 
				
			||||||
    mTxFreq = freqKhz*1.0e3+FREQOFFSET;
 | 
					    mTxFreq = freqKhz * 1.0e3 + mFreqOffset;
 | 
				
			||||||
    if (!mRadioInterface->tuneTx(mTxFreq)) {
 | 
					    if (!mRadioInterface->tuneTx(mTxFreq, mChannel)) {
 | 
				
			||||||
      LOG(ALERT) << "TX failed to tune";
 | 
					      LOG(ALERT) << "TX failed to tune";
 | 
				
			||||||
      sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
 | 
					      sprintf(response,"RSP TXTUNE 1 %d",freqKhz);
 | 
				
			||||||
    }
 | 
					    } else {
 | 
				
			||||||
    else
 | 
					 | 
				
			||||||
      sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
 | 
					      sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
  else if (strcmp(command,"SETTSC")==0) {
 | 
					  else if (strcmp(command,"SETTSC")==0) {
 | 
				
			||||||
    // set TSC
 | 
					    // set TSC
 | 
				
			||||||
    int TSC;
 | 
					    int TSC;
 | 
				
			||||||
    sscanf(buffer,"%3s %s %d",cmdcheck,command,&TSC);
 | 
					    sscanf(buffer,"%3s %s %d",cmdcheck,command,&TSC);
 | 
				
			||||||
    if (mOn)
 | 
					    if (mOn || (TSC<0) || (TSC>7))
 | 
				
			||||||
      sprintf(response,"RSP SETTSC 1 %d",TSC);
 | 
					      sprintf(response,"RSP SETTSC 1 %d",TSC);
 | 
				
			||||||
    else {
 | 
					    else {
 | 
				
			||||||
      mTSC = TSC;
 | 
					      mTSC = TSC;
 | 
				
			||||||
@@ -594,38 +506,62 @@ void Transceiver::driveControl()
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  else if (strcmp(command,"SETSLOT")==0) {
 | 
					  else if (strcmp(command,"SETSLOT")==0) {
 | 
				
			||||||
    // set TSC 
 | 
					    // set slot type
 | 
				
			||||||
    int  corrCode;
 | 
					    int  corrCode;
 | 
				
			||||||
    int  timeslot;
 | 
					    int  timeslot;
 | 
				
			||||||
    sscanf(buffer,"%3s %s %d %d",cmdcheck,command,×lot,&corrCode);
 | 
					    sscanf(buffer,"%3s %s %d %d",cmdcheck,command,×lot,&corrCode);
 | 
				
			||||||
    if ((timeslot < 0) || (timeslot > 7)) {
 | 
					    if ((timeslot < 0) || (timeslot > 7)) {
 | 
				
			||||||
      LOG(WARNING) << "bogus message on control interface";
 | 
					      LOG(WARNING) << "bogus message on control interface";
 | 
				
			||||||
      sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
 | 
					      sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
 | 
				
			||||||
 | 
					      RTMD_VAL("driveControl", -5);
 | 
				
			||||||
 | 
					      RTMD_CLEAR("driveControl");
 | 
				
			||||||
      return;
 | 
					      return;
 | 
				
			||||||
    }     
 | 
					    }     
 | 
				
			||||||
    mChanType[timeslot] = (ChannelCombination) corrCode;
 | 
					    mDriveLoop->setTimeslot(mChannel, timeslot, (DriveLoop::ChannelCombination) corrCode);
 | 
				
			||||||
    setModulus(timeslot);
 | 
					    mDriveLoop->setModulus(mChannel, timeslot);
 | 
				
			||||||
    sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
 | 
					    sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  else {
 | 
					  else {
 | 
				
			||||||
    LOG(WARNING) << "bogus command " << command << " on control interface.";
 | 
					    LOG(WARNING) << "bogus command " << command << " on control interface.";
 | 
				
			||||||
 | 
					    sprintf(response,"RSP ERR 1");
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mControlSocket.write(response,strlen(response)+1);
 | 
					  mControlSocket.write(response,strlen(response)+1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  RTMD_CLEAR("driveControl");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool Transceiver::driveTransmitPriorityQueue() 
 | 
					bool Transceiver::driveTransmitPriorityQueue() 
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					  RTMD_SET("TRX-drvTxPQueue");
 | 
				
			||||||
  char buffer[gSlotLen+50];
 | 
					  char buffer[gSlotLen+50];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // check data socket
 | 
					  if (!mOn) {
 | 
				
			||||||
  size_t msgLen = mDataSocket.read(buffer);
 | 
					    RTMD_VAL("TRX-drvTxPQueue", -1);
 | 
				
			||||||
 | 
					    RTMD_CLEAR("TRX-drvTxPQueue");
 | 
				
			||||||
 | 
					    return true;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  try { 
 | 
				
			||||||
 | 
					    size_t msgLen = mDataSocket.read(buffer);
 | 
				
			||||||
    if (msgLen!=gSlotLen+1+4+1) {
 | 
					    if (msgLen!=gSlotLen+1+4+1) {
 | 
				
			||||||
      LOG(ERR) << "badly formatted packet on GSM->TRX interface";
 | 
					      LOG(ERR) << "badly formatted packet on GSM->TRX interface";
 | 
				
			||||||
 | 
					      RTMD_VAL("TRX-drvTxPQueue", -2);
 | 
				
			||||||
 | 
					      RTMD_CLEAR("TRX-drvTxPQueue");
 | 
				
			||||||
 | 
					      return false;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  } catch (...) {
 | 
				
			||||||
 | 
					    if (!mOn) {
 | 
				
			||||||
 | 
					      /* Shutdown condition. End the thread. */
 | 
				
			||||||
 | 
					      RTMD_VAL("TRX-drvTxPQueue", -3);
 | 
				
			||||||
 | 
					      RTMD_CLEAR("TRX-drvTxPQueue");
 | 
				
			||||||
 | 
					      return true;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    LOG(ALERT) << "Caught UHD socket exception";
 | 
				
			||||||
 | 
					    RTMD_VAL("TRX-drvTxPQueue", -4);
 | 
				
			||||||
 | 
					    RTMD_CLEAR("TRX-drvTxPQueue");
 | 
				
			||||||
    return false;
 | 
					    return false;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -634,28 +570,12 @@ bool Transceiver::driveTransmitPriorityQueue()
 | 
				
			|||||||
  for (int i = 0; i < 4; i++)
 | 
					  for (int i = 0; i < 4; i++)
 | 
				
			||||||
    frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
 | 
					    frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  /*
 | 
					 | 
				
			||||||
  if (GSM::Time(frameNum,timeSlot) >  mTransmitDeadlineClock + GSM::Time(51,0)) {
 | 
					 | 
				
			||||||
    // stale burst
 | 
					 | 
				
			||||||
    //LOG(DEBUG) << "FAST! "<< GSM::Time(frameNum,timeSlot);
 | 
					 | 
				
			||||||
    //writeClockInterface();
 | 
					 | 
				
			||||||
    }*/
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
  DAB -- Just let these go through the demod.
 | 
					 | 
				
			||||||
  if (GSM::Time(frameNum,timeSlot) < mTransmitDeadlineClock) {
 | 
					 | 
				
			||||||
    // stale burst from GSM core
 | 
					 | 
				
			||||||
    LOG(NOTICE) << "STALE packet on GSM->TRX interface at time "<< GSM::Time(frameNum,timeSlot);
 | 
					 | 
				
			||||||
    return false;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
*/
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  // periodically update GSM core clock
 | 
					  // periodically update GSM core clock
 | 
				
			||||||
  LOG(DEBUG) << "mTransmitDeadlineClock " << mTransmitDeadlineClock
 | 
					  LOG(DEBUG) << "mTransmitDeadlineClock " << mDriveLoop->getDeadlineClock()
 | 
				
			||||||
		<< " mLastClockUpdateTime " << mLastClockUpdateTime;
 | 
					             << " mLastClockUpdateTime " << mDriveLoop->getLastClockUpdate();
 | 
				
			||||||
  if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
 | 
					  if (mDriveLoop->getDeadlineClock() > mDriveLoop->getLastClockUpdate() + GSM::Time(216,0)) {
 | 
				
			||||||
    writeClockInterface();
 | 
					    mDriveLoop->writeClockInterface();
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
 | 
					  LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
@@ -672,158 +592,111 @@ bool Transceiver::driveTransmitPriorityQueue()
 | 
				
			|||||||
  
 | 
					  
 | 
				
			||||||
  LOG(DEBUG) "added burst - time: " << currTime << ", RSSI: " << RSSI; // << ", data: " << newBurst; 
 | 
					  LOG(DEBUG) "added burst - time: " << currTime << ", RSSI: " << RSSI; // << ", data: " << newBurst; 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  RTMD_CLEAR("TRX-drvTxPQueue");
 | 
				
			||||||
  return true;
 | 
					  return true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void Transceiver::driveReceiveFIFO() 
 | 
					Thread::ReturnStatus FIFOServiceLoopThread::shutdown()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					  Transceiver *transceiver = (Transceiver *)mThreadData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  SoftVector *rxBurst = NULL;
 | 
					  if (transceiver == NULL)
 | 
				
			||||||
  int RSSI;
 | 
					    // Nothing to do
 | 
				
			||||||
  int TOA;  // in 1/256 of a symbol
 | 
					    return ALREADY_IDLE;
 | 
				
			||||||
  GSM::Time burstTime;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mRadioInterface->driveReceiveRadio();
 | 
					  transceiver->mFIFOServiceLoop.requestThreadStop();
 | 
				
			||||||
 | 
					  if (transceiver->mReceiveFIFO != NULL) {
 | 
				
			||||||
  rxBurst = pullRadioVector(burstTime,RSSI,TOA);
 | 
					    // Write twice, because read() function may read twice in case of NULL.
 | 
				
			||||||
 | 
					    transceiver->mReceiveFIFO->write(NULL);
 | 
				
			||||||
  if (rxBurst) { 
 | 
					    transceiver->mReceiveFIFO->write(NULL);
 | 
				
			||||||
 | 
					 | 
				
			||||||
    LOG(DEBUG) << "burst parameters: "
 | 
					 | 
				
			||||||
	  << " time: " << burstTime
 | 
					 | 
				
			||||||
	  << " RSSI: " << RSSI
 | 
					 | 
				
			||||||
	  << " TOA: "  << TOA
 | 
					 | 
				
			||||||
	  << " bits: " << *rxBurst;
 | 
					 | 
				
			||||||
    
 | 
					 | 
				
			||||||
    char burstString[gSlotLen+10];
 | 
					 | 
				
			||||||
    burstString[0] = burstTime.TN();
 | 
					 | 
				
			||||||
    for (int i = 0; i < 4; i++)
 | 
					 | 
				
			||||||
      burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
 | 
					 | 
				
			||||||
    burstString[5] = RSSI;
 | 
					 | 
				
			||||||
    burstString[6] = (TOA >> 8) & 0x0ff;
 | 
					 | 
				
			||||||
    burstString[7] = TOA & 0x0ff;
 | 
					 | 
				
			||||||
    SoftVector::iterator burstItr = rxBurst->begin();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    for (unsigned int i = 0; i < gSlotLen; i++) {
 | 
					 | 
				
			||||||
      burstString[8+i] =(char) round((*burstItr++)*255.0);
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
    burstString[gSlotLen+9] = '\0';
 | 
					  return transceiver->mFIFOServiceLoop.stopThread();
 | 
				
			||||||
    delete rxBurst;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    mDataSocket.write(burstString,gSlotLen+10);
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
}
 | 
					void FIFOServiceLoopThread::runThread()
 | 
				
			||||||
 | 
					 | 
				
			||||||
void Transceiver::driveTransmitFIFO() 
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
					  Transceiver *transceiver = (Transceiver *)mThreadData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  while (isThreadRunning()) {
 | 
				
			||||||
      Features a carefully controlled latency mechanism, to 
 | 
					    transceiver->pullFIFO();
 | 
				
			||||||
      assure that transmit packets arrive at the radio/USRP
 | 
					 | 
				
			||||||
      before they need to be transmitted.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      Deadline clock indicates the burst that needs to be
 | 
					 | 
				
			||||||
      pushed into the FIFO right NOW.  If transmit queue does
 | 
					 | 
				
			||||||
      not have a burst, stick in filler data.
 | 
					 | 
				
			||||||
  */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  RadioClock *radioClock = (mRadioInterface->getClock());
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  if (mOn) {
 | 
					 | 
				
			||||||
    //radioClock->wait(); // wait until clock updates
 | 
					 | 
				
			||||||
    LOG(DEBUG) << "radio clock " << radioClock->get();
 | 
					 | 
				
			||||||
    while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
 | 
					 | 
				
			||||||
      // if underrun, then we're not providing bursts to radio/USRP fast
 | 
					 | 
				
			||||||
      //   enough.  Need to increase latency by one GSM frame.
 | 
					 | 
				
			||||||
      if (mRadioInterface->getBus() == RadioDevice::USB) {
 | 
					 | 
				
			||||||
        if (mRadioInterface->isUnderrun()) {
 | 
					 | 
				
			||||||
          // 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;
 | 
					 | 
				
			||||||
            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 (radioClock->get() > mLatencyUpdateTime + GSM::Time(216,0)) {
 | 
					 | 
				
			||||||
              mTransmitLatency.decTN();
 | 
					 | 
				
			||||||
              LOG(INFO) << "reduced latency: " << mTransmitLatency;
 | 
					 | 
				
			||||||
              mLatencyUpdateTime = radioClock->get();
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      // time to push burst to transmit FIFO
 | 
					 | 
				
			||||||
      pushRadioVector(mTransmitDeadlineClock);
 | 
					 | 
				
			||||||
      mTransmitDeadlineClock.incTN();
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  }
 | 
					  LOG(DEBUG) << "FIFOServiceLoopThread has finished operations";
 | 
				
			||||||
  // FIXME -- This should not be a hard spin.
 | 
					 | 
				
			||||||
  // But any delay here causes us to throw omni_thread_fatal.
 | 
					 | 
				
			||||||
  //else radioClock->wait();
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Thread::ReturnStatus ControlServiceLoopThread::shutdown()
 | 
				
			||||||
 | 
					 | 
				
			||||||
void Transceiver::writeClockInterface()
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  char command[50];
 | 
					  Transceiver *transceiver = (Transceiver *)mThreadData;
 | 
				
			||||||
  // FIXME -- This should be adaptive.
 | 
					 | 
				
			||||||
  sprintf(command,"IND CLOCK %llu",(unsigned long long) (mTransmitDeadlineClock.FN()+2));
 | 
					 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  LOG(INFO) << "ClockInterface: sending " << command;
 | 
					  if (transceiver == NULL)
 | 
				
			||||||
 | 
					    // Nothing to do
 | 
				
			||||||
 | 
					    return ALREADY_IDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mClockSocket.write(command,strlen(command)+1);
 | 
					  transceiver->mControlServiceLoop.requestThreadStop();
 | 
				
			||||||
 | 
					  // FIXME: We should use shutdown() here, but the socket should be
 | 
				
			||||||
  mLastClockUpdateTime = mTransmitDeadlineClock;
 | 
					  //        re-openned on the next start() then. Righ now the socket
 | 
				
			||||||
 | 
					  //        is created in the constructor and if we shutdown() it here,
 | 
				
			||||||
}   
 | 
					  //        we'll get errors when we try to use it on the next start.
 | 
				
			||||||
  
 | 
					//  transceiver->mControlSocket.shutdown();
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void *FIFOServiceLoopAdapter(Transceiver *transceiver)
 | 
					 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
  transceiver->setPriority();
 | 
					    // mBasePort+1 is mControlSocket port
 | 
				
			||||||
 | 
					    UDPSocket tmpSock(0, "127.0.0.1", transceiver->mBasePort+1);
 | 
				
			||||||
  while (1) {
 | 
					    tmpSock.write(NULL, 0);
 | 
				
			||||||
    transceiver->driveReceiveFIFO();
 | 
					 | 
				
			||||||
    transceiver->driveTransmitFIFO();
 | 
					 | 
				
			||||||
    pthread_testcancel();
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return NULL;
 | 
					  return transceiver->mControlServiceLoop.stopThread();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void *ControlServiceLoopAdapter(Transceiver *transceiver)
 | 
					void ControlServiceLoopThread::runThread()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  while (1) {
 | 
					  Transceiver *transceiver = (Transceiver *)mThreadData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  while (isThreadRunning()) {
 | 
				
			||||||
    transceiver->driveControl();
 | 
					    transceiver->driveControl();
 | 
				
			||||||
    pthread_testcancel();
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
  return NULL;
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void *TransmitPriorityQueueServiceLoopAdapter(Transceiver *transceiver)
 | 
					  LOG(DEBUG) << "ControlServiceLoopThread has finished operations";
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Thread::ReturnStatus TransmitPriorityQueueServiceLoopThread::shutdown()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  while (1) {
 | 
					  Transceiver *transceiver = (Transceiver *)mThreadData;
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
 | 
					  if (transceiver == NULL)
 | 
				
			||||||
 | 
					    // Nothing to do
 | 
				
			||||||
 | 
					    return ALREADY_IDLE;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  transceiver->mTransmitPriorityQueueServiceLoop.requestThreadStop();
 | 
				
			||||||
 | 
					  // FIXME: We should use shutdown() here, but the socket should be
 | 
				
			||||||
 | 
					  //        re-openned on the next start() then. Righ now the socket
 | 
				
			||||||
 | 
					  //        is created in the constructor and if we shutdown() it here,
 | 
				
			||||||
 | 
					  //        we'll get errors when we try to use it on the next start.
 | 
				
			||||||
 | 
					//  transceiver->mDataSocket.shutdown();
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    // mBasePort+2 is mDataSocket port
 | 
				
			||||||
 | 
					    UDPSocket tmpSock(0, "127.0.0.1", transceiver->mBasePort+2);
 | 
				
			||||||
 | 
					    tmpSock.write(NULL, 0);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					  return transceiver->mTransmitPriorityQueueServiceLoop.stopThread();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void TransmitPriorityQueueServiceLoopThread::runThread()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  Transceiver *transceiver = (Transceiver *)mThreadData;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  while (isThreadRunning()) {
 | 
				
			||||||
    bool stale = false;
 | 
					    bool stale = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Flush the UDP packets until a successful transfer.
 | 
					    // Flush the UDP packets until a successful transfer.
 | 
				
			||||||
    while (!transceiver->driveTransmitPriorityQueue()) {
 | 
					    while (!transceiver->driveTransmitPriorityQueue()) {
 | 
				
			||||||
      stale = true; 
 | 
					      stale = true; 
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    if (stale) {
 | 
					    if (stale) {
 | 
				
			||||||
      // If a packet was stale, remind the GSM stack of the clock.
 | 
					      // If a packet was stale, remind the GSM stack of the clock.
 | 
				
			||||||
      transceiver->writeClockInterface();
 | 
					      transceiver->getDriveLoop()->writeClockInterface();
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    pthread_testcancel();
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  return NULL;
 | 
					
 | 
				
			||||||
 | 
					  LOG(DEBUG) << "TransmitPriorityQueueServiceLoopThread has finished operations";
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,6 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2012 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					* Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Public License.
 | 
					* This software is distributed under the terms of the GNU Public License.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* See the COPYING file in the main directory for details.
 | 
				
			||||||
@@ -29,6 +30,7 @@
 | 
				
			|||||||
	TRANSMIT_LOGGING	write every burst on the given slot to a log
 | 
						TRANSMIT_LOGGING	write every burst on the given slot to a log
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "DriveLoop.h"
 | 
				
			||||||
#include "radioInterface.h"
 | 
					#include "radioInterface.h"
 | 
				
			||||||
#include "Interthread.h"
 | 
					#include "Interthread.h"
 | 
				
			||||||
#include "GSMCommon.h"
 | 
					#include "GSMCommon.h"
 | 
				
			||||||
@@ -40,63 +42,63 @@
 | 
				
			|||||||
/** Define this to be the slot number to be logged. */
 | 
					/** Define this to be the slot number to be logged. */
 | 
				
			||||||
//#define TRANSMIT_LOGGING 1
 | 
					//#define TRANSMIT_LOGGING 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** FIFO thread loop */
 | 
				
			||||||
 | 
					class FIFOServiceLoopThread : public Thread {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					  FIFOServiceLoopThread() : Thread("FIFOServiceLoopThread") {}
 | 
				
			||||||
 | 
					  Thread::ReturnStatus shutdown();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					  virtual void runThread();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** control message handler thread loop */
 | 
				
			||||||
 | 
					class ControlServiceLoopThread : public Thread {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					  ControlServiceLoopThread() : Thread("ControlServiceLoopThread") {}
 | 
				
			||||||
 | 
					  Thread::ReturnStatus shutdown();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					  virtual void runThread();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** transmit queueing thread loop */
 | 
				
			||||||
 | 
					class TransmitPriorityQueueServiceLoopThread : public Thread {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
					  TransmitPriorityQueueServiceLoopThread() : Thread("TransmitPriorityQueueServiceLoopThread") {}
 | 
				
			||||||
 | 
					  Thread::ReturnStatus shutdown();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
					  virtual void runThread();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** The Transceiver class, responsible for physical layer of basestation */
 | 
					/** The Transceiver class, responsible for physical layer of basestation */
 | 
				
			||||||
class Transceiver {
 | 
					class Transceiver {
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
 | 
					  DriveLoop *mDriveLoop;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  GSM::Time mTransmitLatency;     ///< latency between basestation clock and transmit deadline clock
 | 
					  int mBasePort;                  ///< Base port address for all our ports
 | 
				
			||||||
  GSM::Time mLatencyUpdateTime;   ///< last time latency was updated
 | 
					  std::string mTRXAddress;        ///< Address of the BTS TRX control interface
 | 
				
			||||||
 | 
					 | 
				
			||||||
  UDPSocket mDataSocket;	  ///< socket for writing to/reading from GSM core
 | 
					  UDPSocket mDataSocket;	  ///< socket for writing to/reading from GSM core
 | 
				
			||||||
  UDPSocket mControlSocket;	  ///< socket for writing/reading control commands from GSM core
 | 
					  UDPSocket mControlSocket;	  ///< socket for writing/reading control commands from GSM core
 | 
				
			||||||
  UDPSocket mClockSocket;	  ///< socket for writing clock updates to GSM core
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  VectorQueue  mTransmitPriorityQueue;   ///< priority queue of transmit bursts received from GSM core
 | 
					  VectorQueue  *mTransmitPriorityQueue;   ///< priority queue of transmit bursts received from GSM core
 | 
				
			||||||
  VectorFIFO*  mTransmitFIFO;     ///< radioInterface FIFO of transmit bursts 
 | 
					 | 
				
			||||||
  VectorFIFO*  mReceiveFIFO;      ///< radioInterface FIFO of receive bursts 
 | 
					  VectorFIFO*  mReceiveFIFO;      ///< radioInterface FIFO of receive bursts 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Thread *mFIFOServiceLoopThread;  ///< thread to push/pull bursts into transmit/receive FIFO
 | 
					  friend class FIFOServiceLoopThread;
 | 
				
			||||||
  Thread *mControlServiceLoopThread;       ///< thread to process control messages from GSM core
 | 
					  FIFOServiceLoopThread mFIFOServiceLoop;      ///< thread to push/pull bursts into transmit/receive FIFO
 | 
				
			||||||
  Thread *mTransmitPriorityQueueServiceLoopThread;///< thread to process transmit bursts from GSM core
 | 
					  friend class ControlServiceLoopThread;
 | 
				
			||||||
 | 
					  ControlServiceLoopThread mControlServiceLoop;   ///< thread to process control messages from GSM core
 | 
				
			||||||
 | 
					  friend class TransmitPriorityQueueServiceLoopThread;
 | 
				
			||||||
 | 
					  TransmitPriorityQueueServiceLoopThread mTransmitPriorityQueueServiceLoop;///< thread to process transmit bursts from GSM core
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  GSM::Time mTransmitDeadlineClock;       ///< deadline for pushing bursts into transmit FIFO 
 | 
					  int mChannel;                           ///< channelizer attach number between 0 and 'M-1'
 | 
				
			||||||
  GSM::Time mLastClockUpdateTime;         ///< last time clock update was sent up to core
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  RadioInterface *mRadioInterface;	  ///< associated radioInterface object
 | 
					  RadioInterface *mRadioInterface;	  ///< associated radioInterface object
 | 
				
			||||||
  double txFullScale;                     ///< full scale input to radio
 | 
					  double txFullScale;                     ///< full scale input to radio
 | 
				
			||||||
  double rxFullScale;                     ///< full scale output to radio
 | 
					  double rxFullScale;                     ///< full scale output to radio
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Codes for burst types of received bursts*/
 | 
					 | 
				
			||||||
  typedef enum {
 | 
					 | 
				
			||||||
    OFF,               ///< timeslot is off
 | 
					 | 
				
			||||||
    TSC,	       ///< timeslot should contain a normal burst
 | 
					 | 
				
			||||||
    RACH,	       ///< timeslot should contain an access burst
 | 
					 | 
				
			||||||
    IDLE	       ///< timeslot is an idle (or dummy) burst
 | 
					 | 
				
			||||||
  } CorrType;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** Codes for channel combinations */
 | 
					 | 
				
			||||||
  typedef enum {
 | 
					 | 
				
			||||||
    FILL,               ///< Channel is transmitted, but unused
 | 
					 | 
				
			||||||
    I,                  ///< TCH/FS
 | 
					 | 
				
			||||||
    II,                 ///< TCH/HS, idle every other slot
 | 
					 | 
				
			||||||
    III,                ///< TCH/HS
 | 
					 | 
				
			||||||
    IV,                 ///< FCCH+SCH+CCCH+BCCH, uplink RACH
 | 
					 | 
				
			||||||
    V,                  ///< FCCH+SCH+CCCH+BCCH+SDCCH/4+SACCH/4, uplink RACH+SDCCH/4
 | 
					 | 
				
			||||||
    VI,                 ///< CCCH+BCCH, uplink RACH
 | 
					 | 
				
			||||||
    VII,                ///< SDCCH/8 + SACCH/8
 | 
					 | 
				
			||||||
    VIII,               ///< TCH/F + FACCH/F + SACCH/M
 | 
					 | 
				
			||||||
    IX,                 ///< TCH/F + SACCH/M
 | 
					 | 
				
			||||||
    X,                  ///< TCH/FD + SACCH/MD
 | 
					 | 
				
			||||||
    XI,                 ///< PBCCH+PCCCH+PDTCH+PACCH+PTCCH
 | 
					 | 
				
			||||||
    XII,                ///< PCCCH+PDTCH+PACCH+PTCCH
 | 
					 | 
				
			||||||
    XIII,               ///< PDTCH+PACCH+PTCCH
 | 
					 | 
				
			||||||
    NONE,               ///< Channel is inactive, default
 | 
					 | 
				
			||||||
    LOOPBACK            ///< similar go VII, used in loopback testing
 | 
					 | 
				
			||||||
  } ChannelCombination;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** unmodulate a modulated burst */
 | 
					  /** unmodulate a modulated burst */
 | 
				
			||||||
#ifdef TRANSMIT_LOGGING
 | 
					#ifdef TRANSMIT_LOGGING
 | 
				
			||||||
  void unModulateVector(signalVector wVector); 
 | 
					  void unModulateVector(signalVector wVector); 
 | 
				
			||||||
@@ -115,29 +117,24 @@ private:
 | 
				
			|||||||
			   int &RSSI,
 | 
								   int &RSSI,
 | 
				
			||||||
			   int &timingOffset);
 | 
								   int &timingOffset);
 | 
				
			||||||
   
 | 
					   
 | 
				
			||||||
  /** Set modulus for specific timeslot */
 | 
					 | 
				
			||||||
  void setModulus(int timeslot);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** return the expected burst type for the specified timestamp */
 | 
					 | 
				
			||||||
  CorrType expectedCorrType(GSM::Time currTime);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** send messages over the clock socket */
 | 
					  /** send messages over the clock socket */
 | 
				
			||||||
  void writeClockInterface(void);
 | 
					  void writeClockInterface(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  void pullFIFO(void);                 ///< blocking call on receive FIFO 
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  signalVector *gsmPulse;              ///< the GSM shaping pulse for modulation
 | 
					  signalVector *gsmPulse;              ///< the GSM shaping pulse for modulation
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  int mSamplesPerSymbol;               ///< number of samples per GSM symbol
 | 
					  int mSamplesPerSymbol;               ///< number of samples per GSM symbol
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool mOn;			       ///< flag to indicate that transceiver is powered on
 | 
					  bool mOn;			       ///< flag to indicate that transceiver is powered on
 | 
				
			||||||
  ChannelCombination mChanType[8];     ///< channel types for all timeslots
 | 
					  bool mRunning;                       ///< flag to indicate control loop is running
 | 
				
			||||||
 | 
					  bool mPrimary;                       ///< flag to indicate C0 channel 
 | 
				
			||||||
  double mTxFreq;                      ///< the transmit frequency
 | 
					  double mTxFreq;                      ///< the transmit frequency
 | 
				
			||||||
  double mRxFreq;                      ///< the receive frequency
 | 
					  double mRxFreq;                      ///< the receive frequency
 | 
				
			||||||
 | 
					  double mFreqOffset;                  ///< RF frequency offset
 | 
				
			||||||
  int mPower;                          ///< the transmit power in dB
 | 
					  int mPower;                          ///< the transmit power in dB
 | 
				
			||||||
  unsigned mTSC;                       ///< the midamble sequence code
 | 
					 | 
				
			||||||
  double mEnergyThreshold;             ///< threshold to determine if received data is potentially a GSM burst
 | 
					  double mEnergyThreshold;             ///< threshold to determine if received data is potentially a GSM burst
 | 
				
			||||||
  GSM::Time prevFalseDetectionTime;    ///< last timestamp of a false energy detection
 | 
					  GSM::Time prevFalseDetectionTime;    ///< last timestamp of a false energy detection
 | 
				
			||||||
  int fillerModulus[8];                ///< modulus values of all timeslots, in frames
 | 
					 | 
				
			||||||
  signalVector *fillerTable[102][8];   ///< table of modulated filler waveforms for all timeslots
 | 
					 | 
				
			||||||
  unsigned mMaxExpectedDelay;            ///< maximum expected time-of-arrival offset in GSM symbols
 | 
					  unsigned mMaxExpectedDelay;            ///< maximum expected time-of-arrival offset in GSM symbols
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  GSM::Time    channelEstimateTime[8]; ///< last timestamp of each timeslot's channel estimate
 | 
					  GSM::Time    channelEstimateTime[8]; ///< last timestamp of each timeslot's channel estimate
 | 
				
			||||||
@@ -148,6 +145,8 @@ private:
 | 
				
			|||||||
  float        chanRespOffset[8];      ///< most recent timing offset, e.g. TOA, of all timeslots
 | 
					  float        chanRespOffset[8];      ///< most recent timing offset, e.g. TOA, of all timeslots
 | 
				
			||||||
  complex      chanRespAmplitude[8];   ///< most recent channel amplitude of all timeslots
 | 
					  complex      chanRespAmplitude[8];   ///< most recent channel amplitude of all timeslots
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static int mTSC;                     ///< the midamble sequence code
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Transceiver constructor 
 | 
					  /** Transceiver constructor 
 | 
				
			||||||
@@ -157,11 +156,10 @@ public:
 | 
				
			|||||||
      @param wTransmitLatency initial setting of transmit latency
 | 
					      @param wTransmitLatency initial setting of transmit latency
 | 
				
			||||||
      @param radioInterface associated radioInterface object
 | 
					      @param radioInterface associated radioInterface object
 | 
				
			||||||
  */
 | 
					  */
 | 
				
			||||||
  Transceiver(int wBasePort,
 | 
					  Transceiver(int wBasePort, const char *TRXAddress,
 | 
				
			||||||
	      const char *TRXAddress,
 | 
						      DriveLoop *wDriveLoop, RadioInterface *wRadioInterface,
 | 
				
			||||||
	      int wSamplesPerSymbol,
 | 
						      int wSamplesPerSymbol = SAMPSPERSYM,
 | 
				
			||||||
	      GSM::Time wTransmitLatency,
 | 
						      int wChannel = 0, bool wPrimary = true);
 | 
				
			||||||
	      RadioInterface *wRadioInterface);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Destructor */
 | 
					  /** Destructor */
 | 
				
			||||||
  ~Transceiver();
 | 
					  ~Transceiver();
 | 
				
			||||||
@@ -169,11 +167,8 @@ public:
 | 
				
			|||||||
  /** start the Transceiver */
 | 
					  /** start the Transceiver */
 | 
				
			||||||
  void start();
 | 
					  void start();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** attach the radioInterface receive FIFO */
 | 
					  /** shutdown (teardown threads) the Transceiver */
 | 
				
			||||||
  void receiveFIFO(VectorFIFO *wFIFO) { mReceiveFIFO = wFIFO;}
 | 
					  void shutdown();
 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** attach the radioInterface transmit FIFO */
 | 
					 | 
				
			||||||
  void transmitFIFO(VectorFIFO *wFIFO) { mTransmitFIFO = wFIFO;}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
protected:
 | 
					protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -192,25 +187,17 @@ protected:
 | 
				
			|||||||
  */
 | 
					  */
 | 
				
			||||||
  bool driveTransmitPriorityQueue();
 | 
					  bool driveTransmitPriorityQueue();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  friend void *FIFOServiceLoopAdapter(Transceiver *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  friend void *ControlServiceLoopAdapter(Transceiver *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  friend void *TransmitPriorityQueueServiceLoopAdapter(Transceiver *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  void reset();
 | 
					  void reset();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** return transceiver on/off status */ 
 | 
				
			||||||
 | 
					  bool on() { return mOn; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** return control loop operational status */ 
 | 
				
			||||||
 | 
					  bool running() { return mRunning; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** return the drive loop pointer */
 | 
				
			||||||
 | 
					  DriveLoop *getDriveLoop() { return mDriveLoop; }
 | 
				
			||||||
 | 
					  
 | 
				
			||||||
  /** set priority on current thread */
 | 
					  /** set priority on current thread */
 | 
				
			||||||
  void setPriority() { mRadioInterface->setPriority(); }
 | 
					  void setPriority() { mRadioInterface->setPriority(); }
 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					 | 
				
			||||||
/** FIFO thread loop */
 | 
					 | 
				
			||||||
void *FIFOServiceLoopAdapter(Transceiver *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** control message handler thread loop */
 | 
					 | 
				
			||||||
void *ControlServiceLoopAdapter(Transceiver *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/** transmit queueing thread loop */
 | 
					 | 
				
			||||||
void *TransmitPriorityQueueServiceLoopAdapter(Transceiver *);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -3,6 +3,7 @@
 | 
				
			|||||||
 * Written by Thomas Tsou <ttsou@vt.edu>
 | 
					 * Written by Thomas Tsou <ttsou@vt.edu>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright 2010,2011 Free Software Foundation, Inc.
 | 
					 * Copyright 2010,2011 Free Software Foundation, Inc.
 | 
				
			||||||
 | 
					 * Copyright 2013 Alexander Chemeris <Alexander.Chemeris@fairwaves.ru>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
@@ -27,36 +28,104 @@
 | 
				
			|||||||
#include <uhd/usrp/multi_usrp.hpp>
 | 
					#include <uhd/usrp/multi_usrp.hpp>
 | 
				
			||||||
#include <uhd/utils/thread_priority.hpp>
 | 
					#include <uhd/utils/thread_priority.hpp>
 | 
				
			||||||
#include <uhd/utils/msg.hpp>
 | 
					#include <uhd/utils/msg.hpp>
 | 
				
			||||||
 | 
					#include "RTMD.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef HAVE_CONFIG_H
 | 
					#ifdef HAVE_CONFIG_H
 | 
				
			||||||
#include "config.h"
 | 
					#include "config.h"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define U1_DEFAULT_CLK_RT 64e6
 | 
					#define NUM_TX_CHANS      2
 | 
				
			||||||
#define U2_DEFAULT_CLK_RT 100e6 
 | 
					#define NUM_RX_CHANS      NUM_TX_CHANS
 | 
				
			||||||
 | 
					#define TX_CHAN_OFFSET    2e6
 | 
				
			||||||
 | 
					#define B100_CLK_RT       52e6 
 | 
				
			||||||
 | 
					#define USRP2_BASE_RT     400e3
 | 
				
			||||||
 | 
					#define TX_AMPL           0.3;
 | 
				
			||||||
 | 
					#define SAMPLE_BUF_SZ     (1 << 20)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					enum uhd_dev_type {
 | 
				
			||||||
 | 
						USRP1,
 | 
				
			||||||
 | 
						USRP2,
 | 
				
			||||||
 | 
						B100,
 | 
				
			||||||
 | 
						UMTRX,
 | 
				
			||||||
 | 
						NUM_USRP_TYPES,
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct uhd_dev_offset {
 | 
				
			||||||
 | 
						enum uhd_dev_type type;
 | 
				
			||||||
 | 
						int sps;
 | 
				
			||||||
 | 
						double offset;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static TIMESTAMP init_rd_ts = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
    master_clk_rt     - Master clock frequency - ignored if host resampling is
 | 
					 * Tx / Rx sample offset values. In a perfect world, there is no group delay
 | 
				
			||||||
                        enabled
 | 
					 * though analog components, and behaviour through digital filters exactly
 | 
				
			||||||
 | 
					 * matches calculated values. In reality, there are unaccounted factors,
 | 
				
			||||||
    rx_smpl_offset    - Timing correction in seconds between receive and
 | 
					 * which are captured in these empirically measured (using a loopback test)
 | 
				
			||||||
                        transmit timestamps. This value corrects for delays on
 | 
					 * timing correction values.
 | 
				
			||||||
                        on the RF side of the timestamping point of the device.
 | 
					 *
 | 
				
			||||||
                        This value is generally empirically measured.
 | 
					 * Notes:
 | 
				
			||||||
 | 
					 *   USRP1 with timestamps is not supported by UHD.
 | 
				
			||||||
    smpl_buf_sz       - The receive sample buffer size in bytes.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    tx_ampl           - Transmit amplitude must be between 0 and 1.0
 | 
					 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
const double master_clk_rt = 52e6;
 | 
					static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 3] = {
 | 
				
			||||||
const size_t smpl_buf_sz = (1 << 20);
 | 
						{ USRP1, 1, 0.0 },
 | 
				
			||||||
const float tx_ampl = .3;
 | 
						{ USRP1, 2, 0.0 },
 | 
				
			||||||
 | 
						{ USRP1, 4, 0.0 },
 | 
				
			||||||
 | 
						{ USRP2, 1, 5.4394e-5 },
 | 
				
			||||||
 | 
						{ USRP2, 2, 0.0 },
 | 
				
			||||||
 | 
						{ USRP2, 4, 0.0 },
 | 
				
			||||||
 | 
						{ B100,  1, 9.4778e-5 },
 | 
				
			||||||
 | 
						{ B100,  2, 5.1100e-5 },
 | 
				
			||||||
 | 
						{ B100,  4, 2.9418e-5 },
 | 
				
			||||||
 | 
						{ UMTRX, 1, 9.1738e-5 },
 | 
				
			||||||
 | 
						{ UMTRX, 2, 0.0 },
 | 
				
			||||||
 | 
						{ UMTRX, 4, 0.0 },
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef RESAMPLE
 | 
					static double get_dev_offset(enum uhd_dev_type type, int sps)
 | 
				
			||||||
const double rx_smpl_offset = .00005;
 | 
					{
 | 
				
			||||||
#else
 | 
						if (type == USRP1) {
 | 
				
			||||||
const double rx_smpl_offset = .0000869;
 | 
							LOG(ERR) << "Invalid device type";
 | 
				
			||||||
#endif
 | 
							return 0.0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (sps) {
 | 
				
			||||||
 | 
						case 1:
 | 
				
			||||||
 | 
							return uhd_offsets[3 * type + 0].offset;
 | 
				
			||||||
 | 
						case 2:
 | 
				
			||||||
 | 
							return uhd_offsets[3 * type + 1].offset;
 | 
				
			||||||
 | 
						case 4:
 | 
				
			||||||
 | 
							return uhd_offsets[3 * type + 2].offset;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOG(ERR) << "Unsupported samples-per-symbols: " << sps;
 | 
				
			||||||
 | 
						return 0.0;	
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Select sample rate based on device type and requested samples-per-symbol.
 | 
				
			||||||
 | 
					 * The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
 | 
				
			||||||
 | 
					 * usable channel spacing of 400 kHz.
 | 
				
			||||||
 | 
					 */ 
 | 
				
			||||||
 | 
					static double select_rate(uhd_dev_type type, int sps)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						if ((sps != 4) && (sps != 2) && (sps != 1))
 | 
				
			||||||
 | 
							return -9999.99;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						switch (type) {
 | 
				
			||||||
 | 
						case USRP2:
 | 
				
			||||||
 | 
							return USRP2_BASE_RT * sps;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case B100:
 | 
				
			||||||
 | 
						case UMTRX:
 | 
				
			||||||
 | 
							return GSMRATE * sps;
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOG(ALERT) << "Unknown device type " << type;
 | 
				
			||||||
 | 
						return -9999.99;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** Timestamp conversion
 | 
					/** Timestamp conversion
 | 
				
			||||||
    @param timestamp a UHD or OpenBTS timestamp
 | 
					    @param timestamp a UHD or OpenBTS timestamp
 | 
				
			||||||
@@ -139,6 +208,15 @@ private:
 | 
				
			|||||||
	size_t data_end;
 | 
						size_t data_end;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/** transmit queueing thread loop */
 | 
				
			||||||
 | 
					class UHDAsyncEventThread : public Thread {
 | 
				
			||||||
 | 
					public:
 | 
				
			||||||
 | 
						UHDAsyncEventThread() : Thread("UHDAsyncEventThread") {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					protected:
 | 
				
			||||||
 | 
						virtual void runThread();
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
    uhd_device - UHD implementation of the Device interface. Timestamped samples
 | 
					    uhd_device - UHD implementation of the Device interface. Timestamped samples
 | 
				
			||||||
                are sent to and received from the device. An intermediate buffer
 | 
					                are sent to and received from the device. An intermediate buffer
 | 
				
			||||||
@@ -148,44 +226,48 @@ private:
 | 
				
			|||||||
*/
 | 
					*/
 | 
				
			||||||
class uhd_device : public RadioDevice {
 | 
					class uhd_device : public RadioDevice {
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
	uhd_device(double rate, bool skip_rx);
 | 
						uhd_device(int sps, bool skip_rx);
 | 
				
			||||||
	~uhd_device();
 | 
						~uhd_device();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool open(const std::string &args);
 | 
						int open(const std::string &args);
 | 
				
			||||||
	bool start();
 | 
						bool start();
 | 
				
			||||||
	bool stop();
 | 
						bool stop();
 | 
				
			||||||
	void restart(uhd::time_spec_t ts);
 | 
						void restart(uhd::time_spec_t ts);
 | 
				
			||||||
	void setPriority();
 | 
						void setPriority();
 | 
				
			||||||
	enum busType getBus() { return bus; }
 | 
						enum TxWindowType getWindowType() { return tx_window; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int readSamples(short *buf, int len, bool *overrun, 
 | 
						int readSamples(short **buf, int chans, int len, TIMESTAMP timestamp,
 | 
				
			||||||
			TIMESTAMP timestamp, bool *underrun, unsigned *RSSI);
 | 
								bool *overrun, bool *underrun, unsigned *RSSI);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	int writeSamples(short *buf, int len, bool *underrun, 
 | 
						int writeSamples(short **buf, int chans, int len, TIMESTAMP timestamp,
 | 
				
			||||||
			 TIMESTAMP timestamp, bool isControl);
 | 
								 bool *underrun, bool isControl);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool updateAlignment(TIMESTAMP timestamp);
 | 
						bool updateAlignment(TIMESTAMP timestamp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool setTxFreq(double wFreq);
 | 
						bool setTxFreq(double wFreq, int chan);
 | 
				
			||||||
	bool setRxFreq(double wFreq);
 | 
						bool setRxFreq(double wFreq, int chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	inline TIMESTAMP initialWriteTimestamp() { return 0; }
 | 
						inline TIMESTAMP initialWriteTimestamp() { return init_rd_ts; }
 | 
				
			||||||
	inline TIMESTAMP initialReadTimestamp() { return 0; }
 | 
						inline TIMESTAMP initialReadTimestamp() { return init_rd_ts; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	inline double fullScaleInputValue() { return 32000 * tx_ampl; }
 | 
						inline double fullScaleInputValue() { return 32000 * TX_AMPL; }
 | 
				
			||||||
	inline double fullScaleOutputValue() { return 32000; }
 | 
						inline double fullScaleOutputValue() { return 32000; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	double setRxGain(double db);
 | 
						double setRxGain(double db, int chan);
 | 
				
			||||||
	double getRxGain(void) { return rx_gain; }
 | 
						double getRxGain(int chan) { return !chan ? rx_gain[0] : tx_gain[1]; }
 | 
				
			||||||
	double maxRxGain(void) { return rx_gain_max; }
 | 
						double maxRxGain(void) { return rx_gain_max; }
 | 
				
			||||||
	double minRxGain(void) { return rx_gain_min; }
 | 
						double minRxGain(void) { return rx_gain_min; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	double setTxGain(double db);
 | 
						double setTxGain(double db, int chan);
 | 
				
			||||||
	double maxTxGain(void) { return tx_gain_max; }
 | 
						double maxTxGain(void) { return tx_gain_max; }
 | 
				
			||||||
	double minTxGain(void) { return tx_gain_min; }
 | 
						double minTxGain(void) { return tx_gain_min; }
 | 
				
			||||||
 | 
						void setTxAntenna(std::string &name);
 | 
				
			||||||
 | 
						void setRxAntenna(std::string &name);
 | 
				
			||||||
 | 
						std::string getRxAntenna();
 | 
				
			||||||
 | 
						std::string getTxAntenna();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	double getTxFreq() { return tx_freq; }
 | 
						double getTxFreq(int chan) { return !chan ? tx_freq[0] : tx_freq[1]; }
 | 
				
			||||||
	double getRxFreq() { return rx_freq; }
 | 
						double getRxFreq(int chan) { return !chan ? rx_freq[0] : rx_freq[1]; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	inline double getSampleRate() { return actual_smpl_rt; }
 | 
						inline double getSampleRate() { return actual_smpl_rt; }
 | 
				
			||||||
	inline double numberRead() { return rx_pkt_cnt; }
 | 
						inline double numberRead() { return rx_pkt_cnt; }
 | 
				
			||||||
@@ -195,6 +277,7 @@ public:
 | 
				
			|||||||
	    @return true if message received or false on timeout or error
 | 
						    @return true if message received or false on timeout or error
 | 
				
			||||||
	*/
 | 
						*/
 | 
				
			||||||
	bool recv_async_msg();
 | 
						bool recv_async_msg();
 | 
				
			||||||
 | 
						bool running() { return started; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	enum err_code {
 | 
						enum err_code {
 | 
				
			||||||
		ERROR_TIMING = -1,
 | 
							ERROR_TIMING = -1,
 | 
				
			||||||
@@ -204,14 +287,18 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
private:
 | 
					private:
 | 
				
			||||||
	uhd::usrp::multi_usrp::sptr usrp_dev;
 | 
						uhd::usrp::multi_usrp::sptr usrp_dev;
 | 
				
			||||||
	enum busType bus;
 | 
						uhd::tx_streamer::sptr tx_stream;
 | 
				
			||||||
 | 
						uhd::rx_streamer::sptr rx_stream;
 | 
				
			||||||
 | 
						enum TxWindowType tx_window;
 | 
				
			||||||
 | 
						enum uhd_dev_type dev_type;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						int sps;
 | 
				
			||||||
	double desired_smpl_rt, actual_smpl_rt;
 | 
						double desired_smpl_rt, actual_smpl_rt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	double tx_gain, tx_gain_min, tx_gain_max;
 | 
						double tx_gain[NUM_TX_CHANS], tx_gain_min, tx_gain_max;
 | 
				
			||||||
	double rx_gain, rx_gain_min, rx_gain_max;
 | 
						double rx_gain[NUM_RX_CHANS], rx_gain_min, rx_gain_max;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	double tx_freq, rx_freq;
 | 
						double tx_freq[NUM_TX_CHANS], rx_freq[NUM_RX_CHANS];
 | 
				
			||||||
	size_t tx_spp, rx_spp;
 | 
						size_t tx_spp, rx_spp;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bool started;
 | 
						bool started;
 | 
				
			||||||
@@ -223,11 +310,12 @@ private:
 | 
				
			|||||||
	uhd::time_spec_t prev_ts;
 | 
						uhd::time_spec_t prev_ts;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	TIMESTAMP ts_offset;
 | 
						TIMESTAMP ts_offset;
 | 
				
			||||||
	smpl_buf *rx_smpl_buf;
 | 
						smpl_buf *rx_smpl_buf[NUM_RX_CHANS];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	void init_gains();
 | 
						void init_gains();
 | 
				
			||||||
	void set_ref_clk(bool ext_clk);
 | 
						void set_ref_clk(bool ext_clk);
 | 
				
			||||||
	double set_rates(double rate);
 | 
						int set_master_clk(double rate);
 | 
				
			||||||
 | 
						int set_rates(double rate);
 | 
				
			||||||
	bool parse_dev_type();
 | 
						bool parse_dev_type();
 | 
				
			||||||
	bool flush_recv(size_t num_pkts);
 | 
						bool flush_recv(size_t num_pkts);
 | 
				
			||||||
	int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
 | 
						int check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls);
 | 
				
			||||||
@@ -235,14 +323,14 @@ private:
 | 
				
			|||||||
	std::string str_code(uhd::rx_metadata_t metadata);
 | 
						std::string str_code(uhd::rx_metadata_t metadata);
 | 
				
			||||||
	std::string str_code(uhd::async_metadata_t metadata);
 | 
						std::string str_code(uhd::async_metadata_t metadata);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	Thread async_event_thrd;
 | 
						UHDAsyncEventThread async_event_thrd;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void *async_event_loop(uhd_device *dev)
 | 
					void UHDAsyncEventThread::runThread()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	while (1) {
 | 
						uhd_device *dev = (uhd_device *)mThreadData;
 | 
				
			||||||
 | 
						while (isThreadRunning()) {
 | 
				
			||||||
		dev->recv_async_msg();
 | 
							dev->recv_async_msg();
 | 
				
			||||||
		pthread_testcancel();
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -268,23 +356,34 @@ void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uhd_device::uhd_device(double rate, bool skip_rx)
 | 
					uhd_device::uhd_device(int sps, bool skip_rx)
 | 
				
			||||||
	: desired_smpl_rt(rate), actual_smpl_rt(0),
 | 
						: tx_gain_min(0.0), tx_gain_max(0.0),
 | 
				
			||||||
	  tx_gain(0.0), tx_gain_min(0.0), tx_gain_max(0.0),
 | 
						  rx_gain_min(0.0), rx_gain_max(0.0),
 | 
				
			||||||
	  rx_gain(0.0), rx_gain_min(0.0), rx_gain_max(0.0),
 | 
						  tx_spp(0), rx_spp(0), started(false), aligned(false),
 | 
				
			||||||
	  tx_freq(0.0), rx_freq(0.0), tx_spp(0), rx_spp(0),
 | 
						  rx_pkt_cnt(0), drop_cnt(0), prev_ts(0,0), ts_offset(0)
 | 
				
			||||||
	  started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
 | 
					 | 
				
			||||||
	  prev_ts(0,0), ts_offset(0), rx_smpl_buf(NULL)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						this->sps = sps;
 | 
				
			||||||
	this->skip_rx = skip_rx;
 | 
						this->skip_rx = skip_rx;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (int i = 0; i < NUM_TX_CHANS; i++) {
 | 
				
			||||||
 | 
							tx_freq[i] = 0.0f;
 | 
				
			||||||
 | 
							tx_gain[i] = 0.0f;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (int i = 0; i < NUM_RX_CHANS; i++) {
 | 
				
			||||||
 | 
							rx_smpl_buf[i] = NULL;
 | 
				
			||||||
 | 
							rx_freq[i] = 0.0f;
 | 
				
			||||||
 | 
							rx_gain[i] = 0.0f;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
uhd_device::~uhd_device()
 | 
					uhd_device::~uhd_device()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	stop();
 | 
						stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (rx_smpl_buf)
 | 
						for (int i = 0; i < NUM_RX_CHANS; i++) {
 | 
				
			||||||
		delete rx_smpl_buf;
 | 
							delete rx_smpl_buf[i];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void uhd_device::init_gains()
 | 
					void uhd_device::init_gains()
 | 
				
			||||||
@@ -299,95 +398,135 @@ void uhd_device::init_gains()
 | 
				
			|||||||
	rx_gain_min = range.start();
 | 
						rx_gain_min = range.start();
 | 
				
			||||||
	rx_gain_max = range.stop();
 | 
						rx_gain_max = range.stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2);
 | 
						for (int i = 0; i < NUM_TX_CHANS; i++) {
 | 
				
			||||||
	usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2);
 | 
							usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2, i);
 | 
				
			||||||
 | 
							tx_gain[i] = usrp_dev->get_tx_gain(i);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	tx_gain = usrp_dev->get_tx_gain();
 | 
						for (int i = 0; i < NUM_RX_CHANS; i++) {
 | 
				
			||||||
	rx_gain = usrp_dev->get_rx_gain();
 | 
							usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2, i);
 | 
				
			||||||
 | 
							rx_gain[i] = usrp_dev->get_rx_gain(i);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return;
 | 
						return;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void uhd_device::set_ref_clk(bool ext_clk)
 | 
					void uhd_device::set_ref_clk(bool ext_clk)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uhd::clock_config_t clk_cfg;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	clk_cfg.pps_source = uhd::clock_config_t::PPS_SMA;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (ext_clk)
 | 
						if (ext_clk)
 | 
				
			||||||
		clk_cfg.ref_source = uhd::clock_config_t::REF_SMA;
 | 
							usrp_dev->set_clock_source("external");
 | 
				
			||||||
	else
 | 
					 | 
				
			||||||
		clk_cfg.ref_source = uhd::clock_config_t::REF_INT;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	usrp_dev->set_clock_config(clk_cfg);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return;
 | 
						return;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
double uhd_device::set_rates(double rate)
 | 
					int uhd_device::set_master_clk(double clk_rate)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	double actual_rt, actual_clk_rt;
 | 
						double actual_clk_rt;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifndef RESAMPLE
 | 
						try {
 | 
				
			||||||
	// Make sure we can set the master clock rate on this device
 | 
							usrp_dev->set_master_clock_rate(clk_rate);
 | 
				
			||||||
		actual_clk_rt = usrp_dev->get_master_clock_rate();
 | 
							actual_clk_rt = usrp_dev->get_master_clock_rate();
 | 
				
			||||||
	if (actual_clk_rt > U1_DEFAULT_CLK_RT) {
 | 
						} catch (const std::exception &ex) {
 | 
				
			||||||
		LOG(ALERT) << "Cannot set clock rate on this device";
 | 
							LOG(ALERT) << "UHD clock rate setting failed: " << clk_rate;
 | 
				
			||||||
		LOG(ALERT) << "Please compile with host resampling support";
 | 
							LOG(ALERT) << ex.what();
 | 
				
			||||||
		return -1.0;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Set master clock rate
 | 
						if (actual_clk_rt != clk_rate) {
 | 
				
			||||||
	usrp_dev->set_master_clock_rate(master_clk_rt);
 | 
					 | 
				
			||||||
	actual_clk_rt = usrp_dev->get_master_clock_rate();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (actual_clk_rt != master_clk_rt) {
 | 
					 | 
				
			||||||
		LOG(ALERT) << "Failed to set master clock rate";
 | 
							LOG(ALERT) << "Failed to set master clock rate";
 | 
				
			||||||
 | 
							LOG(ALERT) << "Requested clock rate " << clk_rate;
 | 
				
			||||||
		LOG(ALERT) << "Actual clock rate " << actual_clk_rt;
 | 
							LOG(ALERT) << "Actual clock rate " << actual_clk_rt;
 | 
				
			||||||
		return -1.0;
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int uhd_device::set_rates(double rate)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						double offset_limit = 10.0;
 | 
				
			||||||
 | 
						double tx_offset, rx_offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// B100 is the only device where we set FPGA clocking
 | 
				
			||||||
 | 
						if (dev_type == B100) {
 | 
				
			||||||
 | 
							if (set_master_clk(B100_CLK_RT) < 0)
 | 
				
			||||||
 | 
								return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Set sample rates
 | 
						// Set sample rates
 | 
				
			||||||
 | 
						try {
 | 
				
			||||||
		usrp_dev->set_tx_rate(rate);
 | 
							usrp_dev->set_tx_rate(rate);
 | 
				
			||||||
		usrp_dev->set_rx_rate(rate);
 | 
							usrp_dev->set_rx_rate(rate);
 | 
				
			||||||
	actual_rt = usrp_dev->get_tx_rate();
 | 
						} catch (const std::exception &ex) {
 | 
				
			||||||
 | 
							LOG(ALERT) << "UHD rate setting failed: " << rate;
 | 
				
			||||||
 | 
							LOG(ALERT) << ex.what();
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						actual_smpl_rt = usrp_dev->get_tx_rate();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (actual_rt != rate) {
 | 
						tx_offset = actual_smpl_rt - rate;
 | 
				
			||||||
 | 
						rx_offset = usrp_dev->get_rx_rate() - rate;
 | 
				
			||||||
 | 
						if ((tx_offset > offset_limit) || (rx_offset > offset_limit)) {
 | 
				
			||||||
		LOG(ALERT) << "Actual sample rate differs from desired rate";
 | 
							LOG(ALERT) << "Actual sample rate differs from desired rate";
 | 
				
			||||||
		return -1.0;
 | 
							LOG(ALERT) << "Tx/Rx (" << actual_smpl_rt << "/"
 | 
				
			||||||
	}
 | 
								   << usrp_dev->get_rx_rate() << ")";
 | 
				
			||||||
	if (usrp_dev->get_rx_rate() != actual_rt) {
 | 
							return -1;
 | 
				
			||||||
		LOG(ALERT) << "Transmit and receive sample rates do not match";
 | 
					 | 
				
			||||||
		return -1.0;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return actual_rt;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
double uhd_device::setTxGain(double db)
 | 
					double uhd_device::setTxGain(double db, int chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	usrp_dev->set_tx_gain(db);
 | 
						if (chan >= NUM_TX_CHANS) {
 | 
				
			||||||
	tx_gain = usrp_dev->get_tx_gain();
 | 
							LOG(ALERT) << "Attempting to set gain on non-existent channel";
 | 
				
			||||||
 | 
							return 0.0f;
 | 
				
			||||||
	LOG(INFO) << "Set TX gain to " << tx_gain << "dB";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return tx_gain;
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
double uhd_device::setRxGain(double db)
 | 
						usrp_dev->set_tx_gain(db, chan);
 | 
				
			||||||
 | 
						tx_gain[chan] = usrp_dev->get_tx_gain(chan);
 | 
				
			||||||
 | 
						LOG(INFO) << "Set TX gain to " << tx_gain[chan] << "dB";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return tx_gain[chan];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					double uhd_device::setRxGain(double db, int chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	usrp_dev->set_rx_gain(db);
 | 
						if (chan >= NUM_RX_CHANS) {
 | 
				
			||||||
	rx_gain = usrp_dev->get_rx_gain();
 | 
							LOG(ALERT) << "Attempting to read gain non-existent channel";
 | 
				
			||||||
 | 
							return 0.0f;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	LOG(INFO) << "Set RX gain to " << rx_gain << "dB";
 | 
						usrp_dev->set_rx_gain(db, chan);
 | 
				
			||||||
 | 
						rx_gain[chan] = usrp_dev->get_rx_gain(chan);
 | 
				
			||||||
 | 
						LOG(INFO) << "Set RX gain to " << rx_gain[chan] << "dB";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return rx_gain;
 | 
						return rx_gain[chan];
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void uhd_device::setTxAntenna(std::string &name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						usrp_dev->set_tx_antenna(name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void uhd_device::setRxAntenna(std::string &name)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						usrp_dev->set_rx_antenna(name);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string uhd_device::getTxAntenna()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return usrp_dev->get_tx_antenna();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					std::string uhd_device::getRxAntenna()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						return usrp_dev->get_rx_antenna();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
    Parse the UHD device tree and mboard name to find out what device we're
 | 
					    Parse the UHD device tree and mboard name to find out what device we're
 | 
				
			||||||
    dealing with. We need the bus type so that the transceiver knows how to
 | 
					    dealing with. We need the window type so that the transceiver knows how to
 | 
				
			||||||
    deal with the transport latency. Reject the USRP1 because UHD doesn't
 | 
					    deal with the transport latency. Reject the USRP1 because UHD doesn't
 | 
				
			||||||
    support timestamped samples with it.
 | 
					    support timestamped samples with it.
 | 
				
			||||||
 */
 | 
					 */
 | 
				
			||||||
@@ -395,34 +534,46 @@ bool uhd_device::parse_dev_type()
 | 
				
			|||||||
{
 | 
					{
 | 
				
			||||||
	std::string mboard_str, dev_str;
 | 
						std::string mboard_str, dev_str;
 | 
				
			||||||
	uhd::property_tree::sptr prop_tree;
 | 
						uhd::property_tree::sptr prop_tree;
 | 
				
			||||||
	size_t usrp1_str, usrp2_str, b100_str1, b100_str2;
 | 
						size_t usrp1_str, usrp2_str, b100_str, umtrx_str;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	prop_tree = usrp_dev->get_device()->get_tree();
 | 
						prop_tree = usrp_dev->get_device()->get_tree();
 | 
				
			||||||
	dev_str = prop_tree->access<std::string>("/name").get();
 | 
						dev_str = prop_tree->access<std::string>("/name").get();
 | 
				
			||||||
	mboard_str = usrp_dev->get_mboard_name();
 | 
						mboard_str = usrp_dev->get_mboard_name();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	usrp1_str = dev_str.find("USRP1");
 | 
						usrp1_str = dev_str.find("USRP1");
 | 
				
			||||||
	b100_str1 = dev_str.find("B-Series");
 | 
						usrp2_str = dev_str.find("USRP2");
 | 
				
			||||||
	b100_str2 = mboard_str.find("B100");
 | 
						umtrx_str = dev_str.find("UmTRX");
 | 
				
			||||||
 | 
						b100_str = mboard_str.find("B100");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (usrp1_str != std::string::npos) {
 | 
						if (usrp1_str != std::string::npos) {
 | 
				
			||||||
		LOG(ALERT) << "USRP1 is not supported using the UHD driver";
 | 
							LOG(ALERT) << "USRP1 is not supported using the UHD driver";
 | 
				
			||||||
		LOG(ALERT) << "Please compile with GNU Radio libusrp support";
 | 
							LOG(ALERT) << "Please compile with GNU Radio libusrp support";
 | 
				
			||||||
 | 
							dev_type = USRP1;
 | 
				
			||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if ((b100_str1 != std::string::npos) || (b100_str2 != std::string::npos)) {
 | 
						if (b100_str != std::string::npos) {
 | 
				
			||||||
		bus = USB;
 | 
							tx_window = TX_WINDOW_USRP1;
 | 
				
			||||||
		LOG(INFO) << "Using USB bus for " << dev_str;
 | 
							LOG(INFO) << "Using USRP1 type transmit window for "
 | 
				
			||||||
 | 
								  << dev_str << " " << mboard_str;
 | 
				
			||||||
 | 
							dev_type = B100;
 | 
				
			||||||
 | 
							return true;
 | 
				
			||||||
 | 
						} else if (usrp2_str != std::string::npos) {
 | 
				
			||||||
 | 
							dev_type = USRP2;
 | 
				
			||||||
 | 
						} else if (umtrx_str != std::string::npos) {
 | 
				
			||||||
 | 
							dev_type = UMTRX;
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		bus = NET;
 | 
							LOG(ALERT) << "Unknown UHD device type";
 | 
				
			||||||
		LOG(INFO) << "Using network bus for " << dev_str;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						tx_window = TX_WINDOW_FIXED;
 | 
				
			||||||
 | 
						LOG(INFO) << "Using fixed transmit window for "
 | 
				
			||||||
 | 
							  << dev_str << " " << mboard_str;
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool uhd_device::open(const std::string &args)
 | 
					int uhd_device::open(const std::string &args)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	// Register msg handler
 | 
						// Register msg handler
 | 
				
			||||||
	uhd::msg::register_handler(&uhd_msg_handler);
 | 
						uhd::msg::register_handler(&uhd_msg_handler);
 | 
				
			||||||
@@ -432,7 +583,7 @@ bool uhd_device::open(const std::string &args)
 | 
				
			|||||||
	uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
 | 
						uhd::device_addrs_t dev_addrs = uhd::device::find(addr);
 | 
				
			||||||
	if (dev_addrs.size() == 0) {
 | 
						if (dev_addrs.size() == 0) {
 | 
				
			||||||
		LOG(ALERT) << "No UHD devices found with address '" << args << "'";
 | 
							LOG(ALERT) << "No UHD devices found with address '" << args << "'";
 | 
				
			||||||
		return false;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Use the first found device
 | 
						// Use the first found device
 | 
				
			||||||
@@ -441,32 +592,51 @@ bool uhd_device::open(const std::string &args)
 | 
				
			|||||||
		usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
 | 
							usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
 | 
				
			||||||
	} catch(...) {
 | 
						} catch(...) {
 | 
				
			||||||
		LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
 | 
							LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
 | 
				
			||||||
		return false;
 | 
							return -1;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Check for a valid device type and set bus type
 | 
						// Check for a valid device type and set bus type
 | 
				
			||||||
	if (!parse_dev_type())
 | 
						if (!parse_dev_type())
 | 
				
			||||||
		return false;
 | 
							return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef EXTREF
 | 
					#ifdef EXTREF
 | 
				
			||||||
	set_ref_clk(true);
 | 
						set_ref_clk(true);
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
						if (NUM_TX_CHANS == 2) {
 | 
				
			||||||
	// Number of samples per over-the-wire packet
 | 
							uhd::usrp::subdev_spec_t subdev_spec("A:0 B:0");
 | 
				
			||||||
	tx_spp = usrp_dev->get_device()->get_max_send_samps_per_packet();
 | 
							usrp_dev->set_tx_subdev_spec(subdev_spec);
 | 
				
			||||||
	rx_spp = usrp_dev->get_device()->get_max_recv_samps_per_packet();
 | 
							usrp_dev->set_rx_subdev_spec(subdev_spec);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Set rates
 | 
						// Set rates
 | 
				
			||||||
	actual_smpl_rt = set_rates(desired_smpl_rt);
 | 
						desired_smpl_rt = select_rate(dev_type, sps);
 | 
				
			||||||
	if (actual_smpl_rt < 0)
 | 
						if (set_rates(desired_smpl_rt) < 0)
 | 
				
			||||||
		return false;
 | 
							return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Create TX and RX streamers
 | 
				
			||||||
 | 
						uhd::stream_args_t stream_args("sc16");
 | 
				
			||||||
 | 
						for (int i = 0; i < NUM_TX_CHANS; i++)
 | 
				
			||||||
 | 
							stream_args.channels.push_back(i);
 | 
				
			||||||
 | 
						tx_stream = usrp_dev->get_tx_stream(stream_args);
 | 
				
			||||||
 | 
						rx_stream = usrp_dev->get_rx_stream(stream_args);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Number of samples per over-the-wire packet
 | 
				
			||||||
 | 
						tx_spp = tx_stream->get_max_num_samps();
 | 
				
			||||||
 | 
						rx_spp = rx_stream->get_max_num_samps();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Create receive buffer
 | 
						// Create receive buffer
 | 
				
			||||||
	size_t buf_len = smpl_buf_sz / sizeof(uint32_t);
 | 
						size_t buf_len = SAMPLE_BUF_SZ / sizeof(uint32_t);
 | 
				
			||||||
	rx_smpl_buf = new smpl_buf(buf_len, actual_smpl_rt);
 | 
						for (int i = 0; i < NUM_RX_CHANS; i++)
 | 
				
			||||||
 | 
							rx_smpl_buf[i] = new smpl_buf(buf_len, actual_smpl_rt);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Set receive chain sample offset 
 | 
						// Set receive chain sample offset 
 | 
				
			||||||
	ts_offset = (TIMESTAMP)(rx_smpl_offset * actual_smpl_rt);
 | 
						double offset = get_dev_offset(dev_type, sps);
 | 
				
			||||||
 | 
						if (offset == 0.0) {
 | 
				
			||||||
 | 
							LOG(ERR) << "Unsupported configuration, no correction applied";
 | 
				
			||||||
 | 
							ts_offset = 0;
 | 
				
			||||||
 | 
						} else  {
 | 
				
			||||||
 | 
							ts_offset = (TIMESTAMP) (offset * actual_smpl_rt);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Initialize and shadow gain values 
 | 
						// Initialize and shadow gain values 
 | 
				
			||||||
	init_gains();
 | 
						init_gains();
 | 
				
			||||||
@@ -474,28 +644,30 @@ bool uhd_device::open(const std::string &args)
 | 
				
			|||||||
	// Print configuration
 | 
						// Print configuration
 | 
				
			||||||
	LOG(INFO) << "\n" << usrp_dev->get_pp_string();
 | 
						LOG(INFO) << "\n" << usrp_dev->get_pp_string();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true;
 | 
						if (dev_type == USRP2)
 | 
				
			||||||
 | 
							return RESAMP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return NORMAL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool uhd_device::flush_recv(size_t num_pkts)
 | 
					bool uhd_device::flush_recv(size_t num_pkts)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uhd::rx_metadata_t md;
 | 
						uhd::rx_metadata_t md;
 | 
				
			||||||
	size_t num_smpls;
 | 
						size_t num_smpls;
 | 
				
			||||||
	uint32_t buff[rx_spp];
 | 
					 | 
				
			||||||
	float timeout;
 | 
						float timeout;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Use .01 sec instead of the default .1 sec
 | 
						// Use .01 sec instead of the default .1 sec
 | 
				
			||||||
	timeout = .01;
 | 
						timeout = .01;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for (size_t i = 0; i < num_pkts; i++) {
 | 
						std::vector<std::vector<std::complex<short> > > bufs(
 | 
				
			||||||
		num_smpls = usrp_dev->get_device()->recv(
 | 
							NUM_RX_CHANS, std::vector<std::complex<short> >(rx_spp));
 | 
				
			||||||
					buff,
 | 
						std::vector<std::complex<short> *> buf_ptrs;
 | 
				
			||||||
					rx_spp,
 | 
						for (int i = 0; i < bufs.size(); i++)
 | 
				
			||||||
					md,
 | 
							buf_ptrs.push_back(&bufs[i].front());
 | 
				
			||||||
					uhd::io_type_t::COMPLEX_INT16,
 | 
					 | 
				
			||||||
					uhd::device::RECV_MODE_ONE_PACKET,
 | 
					 | 
				
			||||||
					timeout);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (size_t i = 0; i < num_pkts; i++) {
 | 
				
			||||||
 | 
							num_smpls = rx_stream->recv(buf_ptrs, rx_spp, md,
 | 
				
			||||||
 | 
										    timeout, true);
 | 
				
			||||||
		if (!num_smpls) {
 | 
							if (!num_smpls) {
 | 
				
			||||||
			switch (md.error_code) {
 | 
								switch (md.error_code) {
 | 
				
			||||||
			case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
 | 
								case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
 | 
				
			||||||
@@ -511,17 +683,15 @@ bool uhd_device::flush_recv(size_t num_pkts)
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
void uhd_device::restart(uhd::time_spec_t ts)
 | 
					void uhd_device::restart(uhd::time_spec_t ts)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
 | 
					 | 
				
			||||||
	usrp_dev->issue_stream_cmd(cmd);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	flush_recv(50);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	usrp_dev->set_time_now(ts);
 | 
						usrp_dev->set_time_now(ts);
 | 
				
			||||||
	aligned = false;
 | 
						aligned = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
 | 
						uhd::stream_cmd_t cmd = uhd::stream_cmd_t::STREAM_MODE_START_CONTINUOUS;
 | 
				
			||||||
	cmd.stream_now = true;
 | 
						cmd.time_spec = uhd::time_spec_t(0.1);
 | 
				
			||||||
 | 
						cmd.stream_now = false;
 | 
				
			||||||
	usrp_dev->issue_stream_cmd(cmd);
 | 
						usrp_dev->issue_stream_cmd(cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uhd::rx_metadata_t md;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool uhd_device::start()
 | 
					bool uhd_device::start()
 | 
				
			||||||
@@ -533,10 +703,11 @@ bool uhd_device::start()
 | 
				
			|||||||
		return false;
 | 
							return false;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						started = true;
 | 
				
			||||||
	setPriority();
 | 
						setPriority();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Start asynchronous event (underrun check) loop
 | 
						// Start asynchronous event (underrun check) loop
 | 
				
			||||||
	async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
 | 
						async_event_thrd.startThread((void*)this);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Start streaming
 | 
						// Start streaming
 | 
				
			||||||
	restart(uhd::time_spec_t(0.0));
 | 
						restart(uhd::time_spec_t(0.0));
 | 
				
			||||||
@@ -545,18 +716,22 @@ bool uhd_device::start()
 | 
				
			|||||||
	double time_now = usrp_dev->get_time_now().get_real_secs();
 | 
						double time_now = usrp_dev->get_time_now().get_real_secs();
 | 
				
			||||||
	LOG(INFO) << "The current time is " << time_now << " seconds";
 | 
						LOG(INFO) << "The current time is " << time_now << " seconds";
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	started = true;
 | 
					 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool uhd_device::stop()
 | 
					bool uhd_device::stop()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						if (!started)
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						async_event_thrd.stopThread();
 | 
				
			||||||
 | 
						started = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uhd::stream_cmd_t stream_cmd = 
 | 
						uhd::stream_cmd_t stream_cmd = 
 | 
				
			||||||
		uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
 | 
							uhd::stream_cmd_t::STREAM_MODE_STOP_CONTINUOUS;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	usrp_dev->issue_stream_cmd(stream_cmd);
 | 
						usrp_dev->issue_stream_cmd(stream_cmd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	started = false;
 | 
					 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -607,40 +782,55 @@ int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
 | 
				
			|||||||
	return 0;
 | 
						return 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int uhd_device::readSamples(short *buf, int len, bool *overrun,
 | 
					int uhd_device::readSamples(short **buf, int chans, int len,
 | 
				
			||||||
			TIMESTAMP timestamp, bool *underrun, unsigned *RSSI)
 | 
								    TIMESTAMP timestamp, bool *overrun,
 | 
				
			||||||
 | 
								    bool *underrun, unsigned *RSSI)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	ssize_t rc;
 | 
						ssize_t rc;
 | 
				
			||||||
	uhd::time_spec_t ts;
 | 
						uhd::time_spec_t ts;
 | 
				
			||||||
	uhd::rx_metadata_t metadata;
 | 
						uhd::rx_metadata_t metadata;
 | 
				
			||||||
	uint32_t pkt_buf[rx_spp];
 | 
						RTMD_SET("readSamples");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if (skip_rx)
 | 
						if (skip_rx) {
 | 
				
			||||||
 | 
							LOG(INFO) << "Skipping Rx";
 | 
				
			||||||
 | 
							RTMD_VAL("readSamples", -1);
 | 
				
			||||||
 | 
							RTMD_CLEAR("readSamples");
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (chans != NUM_RX_CHANS) {
 | 
				
			||||||
 | 
							LOG(ERR) << "Number of requested channels does not match build";
 | 
				
			||||||
 | 
							RTMD_VAL("readSamples", -2);
 | 
				
			||||||
 | 
							RTMD_CLEAR("readSamples");
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Shift read time with respect to transmit clock
 | 
						// Shift read time with respect to transmit clock
 | 
				
			||||||
	timestamp += ts_offset;
 | 
						timestamp += ts_offset;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	ts = convert_time(timestamp, actual_smpl_rt);
 | 
						ts = convert_time(timestamp, actual_smpl_rt);
 | 
				
			||||||
	LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
 | 
						LOG(DEBUG) << "Requested UHD timestamp = " << ts.get_real_secs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Check that timestamp is valid
 | 
						// Check that timestamp is valid
 | 
				
			||||||
	rc = rx_smpl_buf->avail_smpls(timestamp);
 | 
						rc = rx_smpl_buf[0]->avail_smpls(timestamp);
 | 
				
			||||||
	if (rc < 0) {
 | 
						if (rc < 0) {
 | 
				
			||||||
		LOG(ERR) << rx_smpl_buf->str_code(rc);
 | 
							LOG(ERR) << rx_smpl_buf[0]->str_code(rc);
 | 
				
			||||||
		LOG(ERR) << rx_smpl_buf->str_status();
 | 
							LOG(ERR) << rx_smpl_buf[0]->str_status();
 | 
				
			||||||
 | 
							RTMD_VAL("readSamples", -3);
 | 
				
			||||||
 | 
							RTMD_CLEAR("readSamples");
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Receive samples from the usrp until we have enough
 | 
						std::vector<std::vector<std::complex<short> > > bufs(
 | 
				
			||||||
	while (rx_smpl_buf->avail_smpls(timestamp) < len) {
 | 
							NUM_RX_CHANS, std::vector<std::complex<short> >(rx_spp));
 | 
				
			||||||
		size_t num_smpls = usrp_dev->get_device()->recv(
 | 
						std::vector<std::complex<short> *> buf_ptrs;
 | 
				
			||||||
					(void*)pkt_buf,
 | 
						for (int i = 0; i < bufs.size(); i++)
 | 
				
			||||||
					rx_spp,
 | 
							buf_ptrs.push_back(&bufs[i].front());
 | 
				
			||||||
					metadata,
 | 
					 | 
				
			||||||
					uhd::io_type_t::COMPLEX_INT16,
 | 
					 | 
				
			||||||
					uhd::device::RECV_MODE_ONE_PACKET);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// Receive samples from the usrp until we have enough
 | 
				
			||||||
 | 
						while (rx_smpl_buf[0]->avail_smpls(timestamp) < len) {
 | 
				
			||||||
 | 
							size_t num_smpls = rx_stream->recv(buf_ptrs, rx_spp,
 | 
				
			||||||
 | 
											   metadata, 0.1, true);
 | 
				
			||||||
		rx_pkt_cnt++;
 | 
							rx_pkt_cnt++;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Check for errors 
 | 
							// Check for errors 
 | 
				
			||||||
@@ -653,40 +843,55 @@ int uhd_device::readSamples(short *buf, int len, bool *overrun,
 | 
				
			|||||||
		case ERROR_TIMING:
 | 
							case ERROR_TIMING:
 | 
				
			||||||
			restart(prev_ts);
 | 
								restart(prev_ts);
 | 
				
			||||||
		case ERROR_UNHANDLED:
 | 
							case ERROR_UNHANDLED:
 | 
				
			||||||
 | 
								LOG(ALERT) << "UHD: Unhandled error";
 | 
				
			||||||
			continue;
 | 
								continue;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
		ts = metadata.time_spec;
 | 
							ts = metadata.time_spec;
 | 
				
			||||||
		LOG(DEBUG) << "Received timestamp = " << ts.get_real_secs();
 | 
							LOG(DEBUG) << "Received " << num_smpls << " samples "
 | 
				
			||||||
 | 
								   << "with timestamp = " << ts.get_real_secs();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		rc = rx_smpl_buf->write(pkt_buf,
 | 
							for (int i = 0; i < NUM_RX_CHANS; i++) {
 | 
				
			||||||
 | 
								rc = rx_smpl_buf[i]->write((short *) &bufs[i].front(),
 | 
				
			||||||
						   num_smpls,
 | 
											   num_smpls,
 | 
				
			||||||
						   metadata.time_spec);
 | 
											   metadata.time_spec);
 | 
				
			||||||
                        
 | 
					                        
 | 
				
			||||||
			// Continue on local overrun, exit on other errors
 | 
								// Continue on local overrun, exit on other errors
 | 
				
			||||||
			if ((rc < 0)) {
 | 
								if ((rc < 0)) {
 | 
				
			||||||
			LOG(ERR) << rx_smpl_buf->str_code(rc);
 | 
									LOG(ERR) << rx_smpl_buf[i]->str_code(rc);
 | 
				
			||||||
			LOG(ERR) << rx_smpl_buf->str_status();
 | 
									LOG(ERR) << rx_smpl_buf[i]->str_status();
 | 
				
			||||||
			if (rc != smpl_buf::ERROR_OVERFLOW)
 | 
									if (rc != smpl_buf::ERROR_OVERFLOW) {
 | 
				
			||||||
 | 
										RTMD_VAL("readSamples", -4);
 | 
				
			||||||
 | 
										RTMD_CLEAR("readSamples");
 | 
				
			||||||
					return 0;
 | 
										return 0;
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// We have enough samples
 | 
						// We have enough samples
 | 
				
			||||||
	rc = rx_smpl_buf->read(buf, len, timestamp);
 | 
						for (int i = 0; i < NUM_RX_CHANS; i++) {
 | 
				
			||||||
 | 
							rc = rx_smpl_buf[i]->read(buf[i], len, timestamp);
 | 
				
			||||||
		if ((rc < 0) || (rc != len)) {
 | 
							if ((rc < 0) || (rc != len)) {
 | 
				
			||||||
		LOG(ERR) << rx_smpl_buf->str_code(rc);
 | 
								LOG(ERR) << rx_smpl_buf[i]->str_code(rc);
 | 
				
			||||||
		LOG(ERR) << rx_smpl_buf->str_status();
 | 
								LOG(ERR) << rx_smpl_buf[i]->str_status();
 | 
				
			||||||
 | 
								RTMD_VAL("readSamples", -5);
 | 
				
			||||||
 | 
								RTMD_CLEAR("readSamples");
 | 
				
			||||||
			return 0;
 | 
								return 0;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RTMD_VAL("readSamples", len);
 | 
				
			||||||
 | 
						RTMD_CLEAR("readSamples");
 | 
				
			||||||
	return len;
 | 
						return len;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int uhd_device::writeSamples(short *buf, int len, bool *underrun,
 | 
					int uhd_device::writeSamples(short **buf, int chans, int len,
 | 
				
			||||||
			unsigned long long timestamp,bool isControl)
 | 
								     TIMESTAMP timestamp, bool *underrun,
 | 
				
			||||||
 | 
								     bool isControl)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						RTMD_SET("writeSamples");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	uhd::tx_metadata_t metadata;
 | 
						uhd::tx_metadata_t metadata;
 | 
				
			||||||
	metadata.has_time_spec = true;
 | 
						metadata.has_time_spec = true;
 | 
				
			||||||
	metadata.start_of_burst = false;
 | 
						metadata.start_of_burst = false;
 | 
				
			||||||
@@ -696,6 +901,8 @@ int uhd_device::writeSamples(short *buf, int len, bool *underrun,
 | 
				
			|||||||
	// No control packets
 | 
						// No control packets
 | 
				
			||||||
	if (isControl) {
 | 
						if (isControl) {
 | 
				
			||||||
		LOG(ERR) << "Control packets not supported";
 | 
							LOG(ERR) << "Control packets not supported";
 | 
				
			||||||
 | 
							RTMD_VAL("writeSamples", -1);
 | 
				
			||||||
 | 
							RTMD_CLEAR("writeSamples");
 | 
				
			||||||
		return 0;
 | 
							return 0;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -709,6 +916,8 @@ int uhd_device::writeSamples(short *buf, int len, bool *underrun,
 | 
				
			|||||||
			metadata.end_of_burst = true;
 | 
								metadata.end_of_burst = true;
 | 
				
			||||||
		} else if (drop_cnt < 30) {
 | 
							} else if (drop_cnt < 30) {
 | 
				
			||||||
			LOG(DEBUG) << "Aligning transmitter: packet advance";
 | 
								LOG(DEBUG) << "Aligning transmitter: packet advance";
 | 
				
			||||||
 | 
								RTMD_VAL("writeSamples", -2);
 | 
				
			||||||
 | 
								RTMD_CLEAR("writeSamples");
 | 
				
			||||||
			return len;
 | 
								return len;
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			LOG(DEBUG) << "Aligning transmitter: start burst";
 | 
								LOG(DEBUG) << "Aligning transmitter: start burst";
 | 
				
			||||||
@@ -718,12 +927,11 @@ int uhd_device::writeSamples(short *buf, int len, bool *underrun,
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	size_t num_smpls = usrp_dev->get_device()->send(buf,
 | 
						std::vector<short *> bufs(NUM_TX_CHANS);
 | 
				
			||||||
					len,
 | 
						for (int i = 0; i < NUM_TX_CHANS; i++)
 | 
				
			||||||
					metadata,
 | 
							bufs[i] = buf[i];
 | 
				
			||||||
					uhd::io_type_t::COMPLEX_INT16,
 | 
					 | 
				
			||||||
					uhd::device::SEND_MODE_FULL_BUFF);
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						size_t num_smpls = tx_stream->send(bufs, len, metadata);
 | 
				
			||||||
	if (num_smpls != (unsigned) len) {
 | 
						if (num_smpls != (unsigned) len) {
 | 
				
			||||||
		LOG(ALERT) << "UHD: Device send timed out";
 | 
							LOG(ALERT) << "UHD: Device send timed out";
 | 
				
			||||||
		LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
 | 
							LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
 | 
				
			||||||
@@ -731,29 +939,40 @@ int uhd_device::writeSamples(short *buf, int len, bool *underrun,
 | 
				
			|||||||
		exit(-1);
 | 
							exit(-1);
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RTMD_VAL("writeSamples", num_smpls);
 | 
				
			||||||
 | 
						RTMD_CLEAR("writeSamples");
 | 
				
			||||||
	return num_smpls;
 | 
						return num_smpls;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool uhd_device::updateAlignment(TIMESTAMP timestamp)
 | 
					bool uhd_device::updateAlignment(TIMESTAMP timestamp)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	aligned = false;
 | 
					 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool uhd_device::setTxFreq(double wFreq)
 | 
					bool uhd_device::setTxFreq(double wFreq, int chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq);
 | 
						if (chan >= NUM_TX_CHANS) {
 | 
				
			||||||
 | 
							LOG(ALERT) << "Attempting to tune non-existent channel";
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uhd::tune_result_t tr = usrp_dev->set_tx_freq(wFreq, chan);
 | 
				
			||||||
	LOG(INFO) << "\n" << tr.to_pp_string();
 | 
						LOG(INFO) << "\n" << tr.to_pp_string();
 | 
				
			||||||
	tx_freq = usrp_dev->get_tx_freq();
 | 
						tx_freq[chan] = usrp_dev->get_tx_freq(chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool uhd_device::setRxFreq(double wFreq)
 | 
					bool uhd_device::setRxFreq(double wFreq, int chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq);
 | 
						if (chan >= NUM_RX_CHANS) {
 | 
				
			||||||
 | 
							LOG(ALERT) << "Attempting to tune non-existent channel";
 | 
				
			||||||
 | 
							return false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uhd::tune_result_t tr = usrp_dev->set_rx_freq(wFreq, chan);
 | 
				
			||||||
	LOG(INFO) << "\n" << tr.to_pp_string();
 | 
						LOG(INFO) << "\n" << tr.to_pp_string();
 | 
				
			||||||
	rx_freq = usrp_dev->get_rx_freq();
 | 
						rx_freq[chan] = usrp_dev->get_rx_freq(chan);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return true;
 | 
						return true;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -888,7 +1107,7 @@ ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
 | 
				
			|||||||
		num_smpls = len;
 | 
							num_smpls = len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Starting index
 | 
						// Starting index
 | 
				
			||||||
	size_t read_start = data_start + (timestamp - time_start);
 | 
						size_t read_start = (data_start + (timestamp - time_start)) % buf_len;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Read it
 | 
						// Read it
 | 
				
			||||||
	if (read_start + num_smpls < buf_len) {
 | 
						if (read_start + num_smpls < buf_len) {
 | 
				
			||||||
@@ -986,7 +1205,7 @@ std::string smpl_buf::str_code(ssize_t code)
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RadioDevice *RadioDevice::make(double smpl_rt, bool skip_rx)
 | 
					RadioDevice *RadioDevice::make(int sps, bool skip_rx)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return new uhd_device(smpl_rt, skip_rx);
 | 
						return new uhd_device(sps, skip_rx);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -59,11 +59,11 @@ const dboardConfigType dboardConfig = TXA_RXB;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
const double USRPDevice::masterClockRate = 52.0e6;
 | 
					const double USRPDevice::masterClockRate = 52.0e6;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
USRPDevice::USRPDevice (double _desiredSampleRate, bool skipRx)
 | 
					USRPDevice::USRPDevice(int sps, bool skipRx)
 | 
				
			||||||
  : skipRx(skipRx)
 | 
					  : skipRx(skipRx)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  LOG(INFO) << "creating USRP device...";
 | 
					  LOG(INFO) << "creating USRP device...";
 | 
				
			||||||
  decimRate = (unsigned int) round(masterClockRate/_desiredSampleRate);
 | 
					  decimRate = (unsigned int) round(masterClockRate/((GSMRATE) * (double) sps));
 | 
				
			||||||
  actualSampleRate = masterClockRate/decimRate;
 | 
					  actualSampleRate = masterClockRate/decimRate;
 | 
				
			||||||
  rxGain = 0;
 | 
					  rxGain = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -75,7 +75,7 @@ USRPDevice::USRPDevice (double _desiredSampleRate, bool skipRx)
 | 
				
			|||||||
#endif
 | 
					#endif
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool USRPDevice::open(const std::string &)
 | 
					int USRPDevice::open(const std::string &)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  writeLock.unlock();
 | 
					  writeLock.unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -97,7 +97,7 @@ bool USRPDevice::open(const std::string &)
 | 
				
			|||||||
  catch(...) {
 | 
					  catch(...) {
 | 
				
			||||||
    LOG(ALERT) << "make failed on Rx";
 | 
					    LOG(ALERT) << "make failed on Rx";
 | 
				
			||||||
    m_uRx.reset();
 | 
					    m_uRx.reset();
 | 
				
			||||||
    return false;
 | 
					    return -1;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (m_uRx->fpga_master_clock_freq() != masterClockRate)
 | 
					  if (m_uRx->fpga_master_clock_freq() != masterClockRate)
 | 
				
			||||||
@@ -105,7 +105,7 @@ bool USRPDevice::open(const std::string &)
 | 
				
			|||||||
    LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
 | 
					    LOG(ALERT) << "WRONG FPGA clock freq = " << m_uRx->fpga_master_clock_freq()
 | 
				
			||||||
               << ", desired clock freq = " << masterClockRate;
 | 
					               << ", desired clock freq = " << masterClockRate;
 | 
				
			||||||
    m_uRx.reset();
 | 
					    m_uRx.reset();
 | 
				
			||||||
    return false;
 | 
					    return -1;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -120,7 +120,7 @@ bool USRPDevice::open(const std::string &)
 | 
				
			|||||||
  catch(...) {
 | 
					  catch(...) {
 | 
				
			||||||
    LOG(ALERT) << "make failed on Tx";
 | 
					    LOG(ALERT) << "make failed on Tx";
 | 
				
			||||||
    m_uTx.reset();
 | 
					    m_uTx.reset();
 | 
				
			||||||
    return false;
 | 
					    return -1;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (m_uTx->fpga_master_clock_freq() != masterClockRate)
 | 
					  if (m_uTx->fpga_master_clock_freq() != masterClockRate)
 | 
				
			||||||
@@ -128,7 +128,7 @@ bool USRPDevice::open(const std::string &)
 | 
				
			|||||||
    LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
 | 
					    LOG(ALERT) << "WRONG FPGA clock freq = " << m_uTx->fpga_master_clock_freq()
 | 
				
			||||||
               << ", desired clock freq = " << masterClockRate;
 | 
					               << ", desired clock freq = " << masterClockRate;
 | 
				
			||||||
    m_uTx.reset();
 | 
					    m_uTx.reset();
 | 
				
			||||||
    return false;
 | 
					    return -1;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!skipRx) m_uRx->stop();
 | 
					  if (!skipRx) m_uRx->stop();
 | 
				
			||||||
@@ -165,7 +165,7 @@ bool USRPDevice::open(const std::string &)
 | 
				
			|||||||
  samplesWritten = 0;
 | 
					  samplesWritten = 0;
 | 
				
			||||||
  started = false;
 | 
					  started = false;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  return true;
 | 
					  return NORMAL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -556,7 +556,7 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
 | 
				
			|||||||
bool USRPDevice::setRxFreq(double wFreq) { return true;};
 | 
					bool USRPDevice::setRxFreq(double wFreq) { return true;};
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RadioDevice *RadioDevice::make(double desiredSampleRate, bool skipRx)
 | 
					RadioDevice *RadioDevice::make(int sps, bool skipRx)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	return new USRPDevice(desiredSampleRate, skipRx);
 | 
						return new USRPDevice(sps, skipRx);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -112,10 +112,10 @@ private:
 | 
				
			|||||||
 public:
 | 
					 public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Object constructor */
 | 
					  /** Object constructor */
 | 
				
			||||||
  USRPDevice (double _desiredSampleRate, bool skipRx);
 | 
					  USRPDevice(int sps, bool skipRx);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Instantiate the USRP */
 | 
					  /** Instantiate the USRP */
 | 
				
			||||||
  bool open(const std::string &);
 | 
					  int open(const std::string &);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Start the USRP */
 | 
					  /** Start the USRP */
 | 
				
			||||||
  bool start();
 | 
					  bool start();
 | 
				
			||||||
@@ -126,8 +126,7 @@ private:
 | 
				
			|||||||
  /** Set priority not supported */
 | 
					  /** Set priority not supported */
 | 
				
			||||||
  void setPriority() { return; }
 | 
					  void setPriority() { return; }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Only USB bus supported */
 | 
					  enum TxWindowType getWindowType() { return TX_WINDOW_USRP1; }
 | 
				
			||||||
  busType getBus() { return USB; }
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
	Read samples from the USRP.
 | 
						Read samples from the USRP.
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -41,7 +41,7 @@ int main(int argc, char *argv[]) {
 | 
				
			|||||||
  else gLogInit("DEBUG");
 | 
					  else gLogInit("DEBUG");
 | 
				
			||||||
  //if (argc>2) gSetLogFile(argv[2]);
 | 
					  //if (argc>2) gSetLogFile(argv[2]);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  RadioDevice *usrp = RadioDevice::make(52.0e6/192.0);
 | 
					  RadioDevice *usrp = RadioDevice::make(52.0e6/192.0, 1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  usrp->open("");
 | 
					  usrp->open("");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										514
									
								
								Transceiver52M/cycle.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										514
									
								
								Transceiver52M/cycle.h
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,514 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright (c) 2003, 2007-8 Matteo Frigo
 | 
				
			||||||
 | 
					 * Copyright (c) 2003, 2007-8 Massachusetts Institute of Technology
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * Permission is hereby granted, free of charge, to any person obtaining
 | 
				
			||||||
 | 
					 * a copy of this software and associated documentation files (the
 | 
				
			||||||
 | 
					 * "Software"), to deal in the Software without restriction, including
 | 
				
			||||||
 | 
					 * without limitation the rights to use, copy, modify, merge, publish,
 | 
				
			||||||
 | 
					 * distribute, sublicense, and/or sell copies of the Software, and to
 | 
				
			||||||
 | 
					 * permit persons to whom the Software is furnished to do so, subject to
 | 
				
			||||||
 | 
					 * the following conditions:
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * The above copyright notice and this permission notice shall be
 | 
				
			||||||
 | 
					 * included in all copies or substantial portions of the Software.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 | 
				
			||||||
 | 
					 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 | 
				
			||||||
 | 
					 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 | 
				
			||||||
 | 
					 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 | 
				
			||||||
 | 
					 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 | 
				
			||||||
 | 
					 * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 | 
				
			||||||
 | 
					 * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* machine-dependent cycle counters code. Needs to be inlined. */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***************************************************************************/
 | 
				
			||||||
 | 
					/* To use the cycle counters in your code, simply #include "cycle.h" (this
 | 
				
			||||||
 | 
					   file), and then use the functions/macros:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                 ticks getticks(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   ticks is an opaque typedef defined below, representing the current time.
 | 
				
			||||||
 | 
					   You extract the elapsed time between two calls to gettick() via:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                 double elapsed(ticks t1, ticks t0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   which returns a double-precision variable in arbitrary units.  You
 | 
				
			||||||
 | 
					   are not expected to convert this into human units like seconds; it
 | 
				
			||||||
 | 
					   is intended only for *comparisons* of time intervals.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   (In order to use some of the OS-dependent timer routines like
 | 
				
			||||||
 | 
					   Solaris' gethrtime, you need to paste the autoconf snippet below
 | 
				
			||||||
 | 
					   into your configure.ac file and #include "config.h" before cycle.h,
 | 
				
			||||||
 | 
					   or define the relevant macros manually if you are not using autoconf.)
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***************************************************************************/
 | 
				
			||||||
 | 
					/* This file uses macros like HAVE_GETHRTIME that are assumed to be
 | 
				
			||||||
 | 
					   defined according to whether the corresponding function/type/header
 | 
				
			||||||
 | 
					   is available on your system.  The necessary macros are most
 | 
				
			||||||
 | 
					   conveniently defined if you are using GNU autoconf, via the tests:
 | 
				
			||||||
 | 
					   
 | 
				
			||||||
 | 
					   dnl ---------------------------------------------------------------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   AC_C_INLINE
 | 
				
			||||||
 | 
					   AC_HEADER_TIME
 | 
				
			||||||
 | 
					   AC_CHECK_HEADERS([sys/time.h c_asm.h intrinsics.h mach/mach_time.h])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   AC_CHECK_TYPE([hrtime_t],[AC_DEFINE(HAVE_HRTIME_T, 1, [Define to 1 if hrtime_t is defined in <sys/time.h>])],,[#if HAVE_SYS_TIME_H
 | 
				
			||||||
 | 
					#include <sys/time.h>
 | 
				
			||||||
 | 
					#endif])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   AC_CHECK_FUNCS([gethrtime read_real_time time_base_to_time clock_gettime mach_absolute_time])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   dnl Cray UNICOS _rtc() (real-time clock) intrinsic
 | 
				
			||||||
 | 
					   AC_MSG_CHECKING([for _rtc intrinsic])
 | 
				
			||||||
 | 
					   rtc_ok=yes
 | 
				
			||||||
 | 
					   AC_TRY_LINK([#ifdef HAVE_INTRINSICS_H
 | 
				
			||||||
 | 
					#include <intrinsics.h>
 | 
				
			||||||
 | 
					#endif], [_rtc()], [AC_DEFINE(HAVE__RTC,1,[Define if you have the UNICOS _rtc() intrinsic.])], [rtc_ok=no])
 | 
				
			||||||
 | 
					   AC_MSG_RESULT($rtc_ok)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					   dnl ---------------------------------------------------------------------
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/***************************************************************************/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#if TIME_WITH_SYS_TIME
 | 
				
			||||||
 | 
					# include <sys/time.h>
 | 
				
			||||||
 | 
					# include <time.h>
 | 
				
			||||||
 | 
					#else
 | 
				
			||||||
 | 
					# if HAVE_SYS_TIME_H
 | 
				
			||||||
 | 
					#  include <sys/time.h>
 | 
				
			||||||
 | 
					# else
 | 
				
			||||||
 | 
					#  include <time.h>
 | 
				
			||||||
 | 
					# endif
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define INLINE_ELAPSED(INL) static INL double elapsed(ticks t1, ticks t0) \
 | 
				
			||||||
 | 
					{									  \
 | 
				
			||||||
 | 
					     return (double)t1 - (double)t0;					  \
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*----------------------------------------------------------------*/
 | 
				
			||||||
 | 
					/* Solaris */
 | 
				
			||||||
 | 
					#if defined(HAVE_GETHRTIME) && defined(HAVE_HRTIME_T) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					typedef hrtime_t ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define getticks gethrtime
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INLINE_ELAPSED(inline)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*----------------------------------------------------------------*/
 | 
				
			||||||
 | 
					/* AIX v. 4+ routines to read the real-time clock or time-base register */
 | 
				
			||||||
 | 
					#if defined(HAVE_READ_REAL_TIME) && defined(HAVE_TIME_BASE_TO_TIME) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					typedef timebasestruct_t ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __inline ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     ticks t;
 | 
				
			||||||
 | 
					     read_real_time(&t, TIMEBASE_SZ);
 | 
				
			||||||
 | 
					     return t;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __inline double elapsed(ticks t1, ticks t0) /* time in nanoseconds */
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     time_base_to_time(&t1, TIMEBASE_SZ);
 | 
				
			||||||
 | 
					     time_base_to_time(&t0, TIMEBASE_SZ);
 | 
				
			||||||
 | 
					     return (((double)t1.tb_high - (double)t0.tb_high) * 1.0e9 + 
 | 
				
			||||||
 | 
						     ((double)t1.tb_low - (double)t0.tb_low));
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*----------------------------------------------------------------*/
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * PowerPC ``cycle'' counter using the time base register.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#if ((((defined(__GNUC__) && (defined(__powerpc__) || defined(__ppc__))) || (defined(__MWERKS__) && defined(macintosh)))) || (defined(__IBM_GCC_ASM) && (defined(__powerpc__) || defined(__ppc__))))  && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					typedef unsigned long long ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __inline__ ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     unsigned int tbl, tbu0, tbu1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     do {
 | 
				
			||||||
 | 
						  __asm__ __volatile__ ("mftbu %0" : "=r"(tbu0));
 | 
				
			||||||
 | 
						  __asm__ __volatile__ ("mftb %0" : "=r"(tbl));
 | 
				
			||||||
 | 
						  __asm__ __volatile__ ("mftbu %0" : "=r"(tbu1));
 | 
				
			||||||
 | 
					     } while (tbu0 != tbu1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     return (((unsigned long long)tbu0) << 32) | tbl;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INLINE_ELAPSED(__inline__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* MacOS/Mach (Darwin) time-base register interface (unlike UpTime,
 | 
				
			||||||
 | 
					   from Carbon, requires no additional libraries to be linked). */
 | 
				
			||||||
 | 
					#if defined(HAVE_MACH_ABSOLUTE_TIME) && defined(HAVE_MACH_MACH_TIME_H) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					#include <mach/mach_time.h>
 | 
				
			||||||
 | 
					typedef uint64_t ticks;
 | 
				
			||||||
 | 
					#define getticks mach_absolute_time
 | 
				
			||||||
 | 
					INLINE_ELAPSED(__inline__)
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*----------------------------------------------------------------*/
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Pentium cycle counter 
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#if (defined(__GNUC__) || defined(__ICC)) && defined(__i386__)  && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					typedef unsigned long long ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __inline__ ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     ticks ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     __asm__ __volatile__("rdtsc": "=A" (ret));
 | 
				
			||||||
 | 
					     /* no input, nothing else clobbered */
 | 
				
			||||||
 | 
					     return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INLINE_ELAPSED(__inline__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#define TIME_MIN 5000.0   /* unreliable pentium IV cycle counter */
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Visual C++ -- thanks to Morten Nissov for his help with this */
 | 
				
			||||||
 | 
					#if _MSC_VER >= 1200 && _M_IX86 >= 500 && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					#include <windows.h>
 | 
				
			||||||
 | 
					typedef LARGE_INTEGER ticks;
 | 
				
			||||||
 | 
					#define RDTSC __asm __emit 0fh __asm __emit 031h /* hack for VC++ 5.0 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __inline ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     ticks retval;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     __asm {
 | 
				
			||||||
 | 
						  RDTSC
 | 
				
			||||||
 | 
						  mov retval.HighPart, edx
 | 
				
			||||||
 | 
						  mov retval.LowPart, eax
 | 
				
			||||||
 | 
					     }
 | 
				
			||||||
 | 
					     return retval;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __inline double elapsed(ticks t1, ticks t0)
 | 
				
			||||||
 | 
					{  
 | 
				
			||||||
 | 
					     return (double)t1.QuadPart - (double)t0.QuadPart;
 | 
				
			||||||
 | 
					}  
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#define TIME_MIN 5000.0   /* unreliable pentium IV cycle counter */
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*----------------------------------------------------------------*/
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * X86-64 cycle counter
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#if (defined(__GNUC__) || defined(__ICC) || defined(__SUNPRO_C)) && defined(__x86_64__)  && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					typedef unsigned long long ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __inline__ ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     unsigned a, d; 
 | 
				
			||||||
 | 
					     asm volatile("rdtsc" : "=a" (a), "=d" (d)); 
 | 
				
			||||||
 | 
					     return ((ticks)a) | (((ticks)d) << 32); 
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INLINE_ELAPSED(__inline__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* PGI compiler, courtesy Cristiano Calonaci, Andrea Tarsi, & Roberto Gori.
 | 
				
			||||||
 | 
					   NOTE: this code will fail to link unless you use the -Masmkeyword compiler
 | 
				
			||||||
 | 
					   option (grrr). */
 | 
				
			||||||
 | 
					#if defined(__PGI) && defined(__x86_64__) && !defined(HAVE_TICK_COUNTER) 
 | 
				
			||||||
 | 
					typedef unsigned long long ticks;
 | 
				
			||||||
 | 
					static ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					    asm(" rdtsc; shl    $0x20,%rdx; mov    %eax,%eax; or     %rdx,%rax;    ");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					INLINE_ELAPSED(__inline__)
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Visual C++, courtesy of Dirk Michaelis */
 | 
				
			||||||
 | 
					#if _MSC_VER >= 1400 && (defined(_M_AMD64) || defined(_M_X64)) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <intrin.h>
 | 
				
			||||||
 | 
					#pragma intrinsic(__rdtsc)
 | 
				
			||||||
 | 
					typedef unsigned __int64 ticks;
 | 
				
			||||||
 | 
					#define getticks __rdtsc
 | 
				
			||||||
 | 
					INLINE_ELAPSED(__inline)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*----------------------------------------------------------------*/
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * IA64 cycle counter
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* intel's icc/ecc compiler */
 | 
				
			||||||
 | 
					#if (defined(__EDG_VERSION) || defined(__ECC)) && defined(__ia64__) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					typedef unsigned long ticks;
 | 
				
			||||||
 | 
					#include <ia64intrin.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __inline__ ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     return __getReg(_IA64_REG_AR_ITC);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					INLINE_ELAPSED(__inline__)
 | 
				
			||||||
 | 
					 
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* gcc */
 | 
				
			||||||
 | 
					#if defined(__GNUC__) && defined(__ia64__) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					typedef unsigned long ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __inline__ ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     ticks ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     __asm__ __volatile__ ("mov %0=ar.itc" : "=r"(ret));
 | 
				
			||||||
 | 
					     return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INLINE_ELAPSED(__inline__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* HP/UX IA64 compiler, courtesy Teresa L. Johnson: */
 | 
				
			||||||
 | 
					#if defined(__hpux) && defined(__ia64) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					#include <machine/sys/inline.h>
 | 
				
			||||||
 | 
					typedef unsigned long ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     ticks ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     ret = _Asm_mov_from_ar (_AREG_ITC);
 | 
				
			||||||
 | 
					     return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INLINE_ELAPSED(inline)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Microsoft Visual C++ */
 | 
				
			||||||
 | 
					#if defined(_MSC_VER) && defined(_M_IA64) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					typedef unsigned __int64 ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#  ifdef __cplusplus
 | 
				
			||||||
 | 
					extern "C"
 | 
				
			||||||
 | 
					#  endif
 | 
				
			||||||
 | 
					ticks __getReg(int whichReg);
 | 
				
			||||||
 | 
					#pragma intrinsic(__getReg)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __inline ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     volatile ticks temp;
 | 
				
			||||||
 | 
					     temp = __getReg(3116);
 | 
				
			||||||
 | 
					     return temp;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INLINE_ELAPSED(inline)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*----------------------------------------------------------------*/
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * PA-RISC cycle counter 
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					#if defined(__hppa__) || defined(__hppa) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					typedef unsigned long ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#  ifdef __GNUC__
 | 
				
			||||||
 | 
					static __inline__ ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     ticks ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					     __asm__ __volatile__("mfctl 16, %0": "=r" (ret));
 | 
				
			||||||
 | 
					     /* no input, nothing else clobbered */
 | 
				
			||||||
 | 
					     return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#  else
 | 
				
			||||||
 | 
					#  include <machine/inline.h>
 | 
				
			||||||
 | 
					static __inline unsigned long getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     register ticks ret;
 | 
				
			||||||
 | 
					     _MFCTL(16, ret);
 | 
				
			||||||
 | 
					     return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#  endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INLINE_ELAPSED(__inline)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*----------------------------------------------------------------*/
 | 
				
			||||||
 | 
					/* S390, courtesy of James Treacy */
 | 
				
			||||||
 | 
					#if defined(__GNUC__) && defined(__s390__) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					typedef unsigned long long ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __inline__ ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     ticks cycles;
 | 
				
			||||||
 | 
					     __asm__("stck 0(%0)" : : "a" (&(cycles)) : "memory", "cc");
 | 
				
			||||||
 | 
					     return cycles;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INLINE_ELAPSED(__inline__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					/*----------------------------------------------------------------*/
 | 
				
			||||||
 | 
					#if defined(__GNUC__) && defined(__alpha__) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * The 32-bit cycle counter on alpha overflows pretty quickly, 
 | 
				
			||||||
 | 
					 * unfortunately.  A 1GHz machine overflows in 4 seconds.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					typedef unsigned int ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __inline__ ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     unsigned long cc;
 | 
				
			||||||
 | 
					     __asm__ __volatile__ ("rpcc %0" : "=r"(cc));
 | 
				
			||||||
 | 
					     return (cc & 0xFFFFFFFF);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INLINE_ELAPSED(__inline__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*----------------------------------------------------------------*/
 | 
				
			||||||
 | 
					#if defined(__GNUC__) && defined(__sparc_v9__) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					typedef unsigned long ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __inline__ ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     ticks ret;
 | 
				
			||||||
 | 
					     __asm__ __volatile__("rd %%tick, %0" : "=r" (ret));
 | 
				
			||||||
 | 
					     return ret;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INLINE_ELAPSED(__inline__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*----------------------------------------------------------------*/
 | 
				
			||||||
 | 
					#if (defined(__DECC) || defined(__DECCXX)) && defined(__alpha) && defined(HAVE_C_ASM_H) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					#  include <c_asm.h>
 | 
				
			||||||
 | 
					typedef unsigned int ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static __inline ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     unsigned long cc;
 | 
				
			||||||
 | 
					     cc = asm("rpcc %v0");
 | 
				
			||||||
 | 
					     return (cc & 0xFFFFFFFF);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INLINE_ELAPSED(__inline)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					/*----------------------------------------------------------------*/
 | 
				
			||||||
 | 
					/* SGI/Irix */
 | 
				
			||||||
 | 
					#if defined(HAVE_CLOCK_GETTIME) && defined(CLOCK_SGI_CYCLE) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					typedef struct timespec ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     struct timespec t;
 | 
				
			||||||
 | 
					     clock_gettime(CLOCK_SGI_CYCLE, &t);
 | 
				
			||||||
 | 
					     return t;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline double elapsed(ticks t1, ticks t0)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					     return ((double)t1.tv_sec - (double)t0.tv_sec) * 1.0E9 +
 | 
				
			||||||
 | 
						  ((double)t1.tv_nsec - (double)t0.tv_nsec);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*----------------------------------------------------------------*/
 | 
				
			||||||
 | 
					/* Cray UNICOS _rtc() intrinsic function */
 | 
				
			||||||
 | 
					#if defined(HAVE__RTC) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					#ifdef HAVE_INTRINSICS_H
 | 
				
			||||||
 | 
					#  include <intrinsics.h>
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef long long ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define getticks _rtc
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INLINE_ELAPSED(inline)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*----------------------------------------------------------------*/
 | 
				
			||||||
 | 
					/* MIPS ZBus */
 | 
				
			||||||
 | 
					#if HAVE_MIPS_ZBUS_TIMER
 | 
				
			||||||
 | 
					#if defined(__mips__) && !defined(HAVE_TICK_COUNTER)
 | 
				
			||||||
 | 
					#include <sys/mman.h>
 | 
				
			||||||
 | 
					#include <unistd.h>
 | 
				
			||||||
 | 
					#include <fcntl.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					typedef uint64_t ticks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline ticks getticks(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  static uint64_t* addr = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (addr == 0)
 | 
				
			||||||
 | 
					  {
 | 
				
			||||||
 | 
					    uint32_t rq_addr = 0x10030000;
 | 
				
			||||||
 | 
					    int fd;
 | 
				
			||||||
 | 
					    int pgsize;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    pgsize = getpagesize();
 | 
				
			||||||
 | 
					    fd = open ("/dev/mem", O_RDONLY | O_SYNC, 0);
 | 
				
			||||||
 | 
					    if (fd < 0) {
 | 
				
			||||||
 | 
					      perror("open");
 | 
				
			||||||
 | 
					      return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    addr = mmap(0, pgsize, PROT_READ, MAP_SHARED, fd, rq_addr);
 | 
				
			||||||
 | 
					    close(fd);
 | 
				
			||||||
 | 
					    if (addr == (uint64_t *)-1) {
 | 
				
			||||||
 | 
					      perror("mmap");
 | 
				
			||||||
 | 
					      return NULL;
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  return *addr;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					INLINE_ELAPSED(inline)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define HAVE_TICK_COUNTER
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					#endif /* HAVE_MIPS_ZBUS_TIMER */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										125
									
								
								Transceiver52M/multiTRX.cpp
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										125
									
								
								Transceiver52M/multiTRX.cpp
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,125 @@
 | 
				
			|||||||
 | 
					/*
 | 
				
			||||||
 | 
					 * Copyright 2012  Thomas Tsou <ttsou@vt.edu>
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
 | 
					 * the Free Software Foundation, either version 3 of the License, or
 | 
				
			||||||
 | 
					 * (at your option) any later version.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This program is distributed in the hope that it will be useful,
 | 
				
			||||||
 | 
					 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
				
			||||||
 | 
					 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
				
			||||||
 | 
					 * GNU Affero General Public License for more details.
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * You should have received a copy of the GNU Affero General Public License
 | 
				
			||||||
 | 
					 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
				
			||||||
 | 
					 * See the COPYING file in the main directory for details.
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <time.h>
 | 
				
			||||||
 | 
					#include <signal.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include <GSMCommon.h>
 | 
				
			||||||
 | 
					#include <Logger.h>
 | 
				
			||||||
 | 
					#include <Configuration.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#include "Transceiver.h"
 | 
				
			||||||
 | 
					#include "radioDevice.h"
 | 
				
			||||||
 | 
					#include "RTMD.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ConfigurationTable gConfig("/etc/OpenBTS/OpenBTS.db");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					volatile bool gbShutdown = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int Transceiver::mTSC = -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void sigHandler(int signum)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						LOG(NOTICE) << "Received shutdown signal";
 | 
				
			||||||
 | 
						gbShutdown = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static int setupSignals()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						struct sigaction action;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						action.sa_handler = sigHandler;
 | 
				
			||||||
 | 
						sigemptyset(&action.sa_mask);
 | 
				
			||||||
 | 
						action.sa_flags = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (sigaction(SIGINT, &action, NULL) < 0)
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
						if (sigaction(SIGTERM, &action, NULL) < 0)
 | 
				
			||||||
 | 
							return -1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					int main(int argc, char *argv[])
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						int numARFCN = 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (argc > 1) {
 | 
				
			||||||
 | 
							numARFCN = atoi(argv[1]);
 | 
				
			||||||
 | 
							if (numARFCN > CHAN_MAX) {
 | 
				
			||||||
 | 
								LOG(ALERT) << numARFCN  << " channels not supported "
 | 
				
			||||||
 | 
											<< " with with current build";
 | 
				
			||||||
 | 
								exit(-1);
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						gLogInit("transceiver", gConfig.getStr("Log.Level").c_str(), LOG_LOCAL7);
 | 
				
			||||||
 | 
						srandom(time(NULL));
 | 
				
			||||||
 | 
						// Allocate RAM for the real-time log.
 | 
				
			||||||
 | 
						// 4M items = 4*32Mb RAM
 | 
				
			||||||
 | 
						RTMD_INIT(4*1024*1024);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (setupSignals() < 0) {
 | 
				
			||||||
 | 
							LOG(ERR) << "Failed to setup signal handlers, exiting...";
 | 
				
			||||||
 | 
							exit(-1);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RadioDevice *device = RadioDevice::make(SAMPSPERSYM);
 | 
				
			||||||
 | 
						int radioType = device->open("");
 | 
				
			||||||
 | 
						if (radioType < 0) {
 | 
				
			||||||
 | 
							LOG(ALERT) << "Failed to open device, exiting...";
 | 
				
			||||||
 | 
							return EXIT_FAILURE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RadioInterface *radio;
 | 
				
			||||||
 | 
						switch (radioType) {
 | 
				
			||||||
 | 
						case RadioDevice::NORMAL:
 | 
				
			||||||
 | 
							radio = new RadioInterface(device, numARFCN);
 | 
				
			||||||
 | 
							break;
 | 
				
			||||||
 | 
						case RadioDevice::RESAMP:
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							LOG(ALERT) << "Unsupported configuration";
 | 
				
			||||||
 | 
							return EXIT_FAILURE;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DriveLoop *drive = new DriveLoop(5700, "127.0.0.1", radio, numARFCN, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						Transceiver *trx[CHAN_MAX];
 | 
				
			||||||
 | 
						bool primary = true;
 | 
				
			||||||
 | 
						for (int i = 0; i < numARFCN; i++) {
 | 
				
			||||||
 | 
							trx[i] = new Transceiver(5700 + 2 * i, "127.0.0.1",
 | 
				
			||||||
 | 
										 drive, radio, SAMPSPERSYM,
 | 
				
			||||||
 | 
										 i, primary);
 | 
				
			||||||
 | 
							trx[i]->start();
 | 
				
			||||||
 | 
							primary = false;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						while (!gbShutdown)
 | 
				
			||||||
 | 
							sleep(1);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						LOG(NOTICE) << "Shutting down transceivers...";
 | 
				
			||||||
 | 
						RTMD_FLUSH("osmo-trx.rtmd");
 | 
				
			||||||
 | 
						for (int i = 0; i < numARFCN; i++)
 | 
				
			||||||
 | 
							trx[i]->shutdown();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						for (int i = 0; i < numARFCN; i++)
 | 
				
			||||||
 | 
							delete trx[i];
 | 
				
			||||||
 | 
						delete drive;
 | 
				
			||||||
 | 
						delete radio;
 | 
				
			||||||
 | 
						delete device;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -21,6 +21,8 @@
 | 
				
			|||||||
#include "config.h"
 | 
					#include "config.h"
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define GSMRATE       1625e3/6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** a 64-bit virtual timestamp for radio data */
 | 
					/** a 64-bit virtual timestamp for radio data */
 | 
				
			||||||
typedef unsigned long long TIMESTAMP;
 | 
					typedef unsigned long long TIMESTAMP;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -29,12 +31,17 @@ class RadioDevice {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  public:
 | 
					  public:
 | 
				
			||||||
  /* Available transport bus types */
 | 
					  /* Available transport bus types */
 | 
				
			||||||
  enum busType { USB, NET };
 | 
					  enum TxWindowType { TX_WINDOW_USRP1, TX_WINDOW_FIXED };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  static RadioDevice *make(double desiredSampleRate, bool skipRx = false);
 | 
					  /* Radio interface types */
 | 
				
			||||||
 | 
					  enum RadioInterfaceType { NORMAL, RESAMP };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  static RadioDevice *make(int sps, bool skipRx = false);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  virtual ~RadioDevice() {};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Initialize the USRP */
 | 
					  /** Initialize the USRP */
 | 
				
			||||||
  virtual bool open(const std::string &args)=0;
 | 
					  virtual int open(const std::string &args)=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Start the USRP */
 | 
					  /** Start the USRP */
 | 
				
			||||||
  virtual bool start()=0;
 | 
					  virtual bool start()=0;
 | 
				
			||||||
@@ -42,8 +49,8 @@ class RadioDevice {
 | 
				
			|||||||
  /** Stop the USRP */
 | 
					  /** Stop the USRP */
 | 
				
			||||||
  virtual bool stop()=0;
 | 
					  virtual bool stop()=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Get the bus type */
 | 
					  /** Get the Tx window type */
 | 
				
			||||||
  virtual enum busType getBus()=0;
 | 
					  virtual enum TxWindowType getWindowType()=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Enable thread priority */
 | 
					  /** Enable thread priority */
 | 
				
			||||||
  virtual void setPriority()=0;
 | 
					  virtual void setPriority()=0;
 | 
				
			||||||
@@ -58,10 +65,9 @@ class RadioDevice {
 | 
				
			|||||||
	@param RSSI The received signal strength of the read result
 | 
						@param RSSI The received signal strength of the read result
 | 
				
			||||||
	@return The number of samples actually read
 | 
						@return The number of samples actually read
 | 
				
			||||||
  */
 | 
					  */
 | 
				
			||||||
  virtual int readSamples(short *buf, int len, bool *overrun, 
 | 
					  virtual int readSamples(short **buf, int chans, int len, TIMESTAMP timestamp,
 | 
				
			||||||
		   TIMESTAMP timestamp = 0xffffffff,
 | 
					                          bool *overrun = NULL, bool *underrun = NULL,
 | 
				
			||||||
		   bool *underrun = 0,
 | 
					                          unsigned *RSSI = NULL)=0;
 | 
				
			||||||
		   unsigned *RSSI = 0)=0;
 | 
					 | 
				
			||||||
  /**
 | 
					  /**
 | 
				
			||||||
        Write samples to the radio.
 | 
					        Write samples to the radio.
 | 
				
			||||||
        @param buf Contains the data to be written.
 | 
					        @param buf Contains the data to be written.
 | 
				
			||||||
@@ -71,18 +77,17 @@ class RadioDevice {
 | 
				
			|||||||
        @param isControl Set if data is a control packet, e.g. a ping command
 | 
					        @param isControl Set if data is a control packet, e.g. a ping command
 | 
				
			||||||
        @return The number of samples actually written
 | 
					        @return The number of samples actually written
 | 
				
			||||||
  */
 | 
					  */
 | 
				
			||||||
  virtual int writeSamples(short *buf, int len, bool *underrun, 
 | 
					  virtual int writeSamples(short **buf, int chans, int len, TIMESTAMP timestamp,
 | 
				
			||||||
		    TIMESTAMP timestamp,
 | 
					                           bool *underrun = NULL, bool isControl = false)=0;
 | 
				
			||||||
		    bool isControl=false)=0;
 | 
					 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
  /** Update the alignment between the read and write timestamps */
 | 
					  /** Update the alignment between the read and write timestamps */
 | 
				
			||||||
  virtual bool updateAlignment(TIMESTAMP timestamp)=0;
 | 
					  virtual bool updateAlignment(TIMESTAMP timestamp)=0;
 | 
				
			||||||
  
 | 
					  
 | 
				
			||||||
  /** Set the transmitter frequency */
 | 
					  /** Set the transmitter frequency */
 | 
				
			||||||
  virtual bool setTxFreq(double wFreq)=0;
 | 
					  virtual bool setTxFreq(double wFreq, int chan = 0)=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Set the receiver frequency */
 | 
					  /** Set the receiver frequency */
 | 
				
			||||||
  virtual bool setRxFreq(double wFreq)=0;
 | 
					  virtual bool setRxFreq(double wFreq, int chan = 0)=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Returns the starting write Timestamp*/
 | 
					  /** Returns the starting write Timestamp*/
 | 
				
			||||||
  virtual TIMESTAMP initialWriteTimestamp(void)=0;
 | 
					  virtual TIMESTAMP initialWriteTimestamp(void)=0;
 | 
				
			||||||
@@ -97,10 +102,10 @@ class RadioDevice {
 | 
				
			|||||||
  virtual double fullScaleOutputValue()=0;
 | 
					  virtual double fullScaleOutputValue()=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** sets the receive chan gain, returns the gain setting **/
 | 
					  /** sets the receive chan gain, returns the gain setting **/
 | 
				
			||||||
  virtual double setRxGain(double dB)=0;
 | 
					  virtual double setRxGain(double dB, int chan = 0)=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** gets the current receive gain **/
 | 
					  /** gets the current receive gain **/
 | 
				
			||||||
  virtual double getRxGain(void)=0;
 | 
					  virtual double getRxGain(int chan = 0)=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** return maximum Rx Gain **/
 | 
					  /** return maximum Rx Gain **/
 | 
				
			||||||
  virtual double maxRxGain(void) = 0;
 | 
					  virtual double maxRxGain(void) = 0;
 | 
				
			||||||
@@ -109,7 +114,7 @@ class RadioDevice {
 | 
				
			|||||||
  virtual double minRxGain(void) = 0;
 | 
					  virtual double minRxGain(void) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** sets the transmit chan gain, returns the gain setting **/
 | 
					  /** sets the transmit chan gain, returns the gain setting **/
 | 
				
			||||||
  virtual double setTxGain(double dB)=0;
 | 
					  virtual double setTxGain(double dB, int chan = 0)=0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** return maximum Tx Gain **/
 | 
					  /** return maximum Tx Gain **/
 | 
				
			||||||
  virtual double maxTxGain(void) = 0;
 | 
					  virtual double maxTxGain(void) = 0;
 | 
				
			||||||
@@ -117,13 +122,18 @@ class RadioDevice {
 | 
				
			|||||||
  /** return minimum Tx Gain **/
 | 
					  /** return minimum Tx Gain **/
 | 
				
			||||||
  virtual double minTxGain(void) = 0;
 | 
					  virtual double minTxGain(void) = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** set and return antennas selection **/
 | 
				
			||||||
 | 
					  virtual void setTxAntenna(std::string &name) = 0;
 | 
				
			||||||
 | 
					  virtual void setRxAntenna(std::string &name) = 0;
 | 
				
			||||||
 | 
					  virtual std::string getRxAntenna() = 0;
 | 
				
			||||||
 | 
					  virtual std::string getTxAntenna() = 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** Return internal status values */
 | 
					  /** Return internal status values */
 | 
				
			||||||
  virtual double getTxFreq()=0;
 | 
					  virtual double getTxFreq(int chan = 0)=0;
 | 
				
			||||||
  virtual double getRxFreq()=0;
 | 
					  virtual double getRxFreq(int chan = 0)=0;
 | 
				
			||||||
  virtual double getSampleRate()=0;
 | 
					  virtual double getSampleRate()=0;
 | 
				
			||||||
  virtual double numberRead()=0;
 | 
					  virtual double numberRead()=0;
 | 
				
			||||||
  virtual double numberWritten()=0;
 | 
					  virtual double numberWritten()=0;
 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#endif
 | 
					#endif
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,91 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * Radio device I/O interface
 | 
					 | 
				
			||||||
 * Written by Thomas Tsou <ttsou@vt.edu>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Copyright 2011 Free Software Foundation, Inc.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
 * (at your option) any later version.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is distributed in the hope that it will be useful,
 | 
					 | 
				
			||||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 | 
				
			||||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 | 
				
			||||||
 * GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 * See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <radioInterface.h>
 | 
					 | 
				
			||||||
#include <Logger.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Device side buffers */
 | 
					 | 
				
			||||||
static short rx_buf[OUTCHUNK * 2 * 2];
 | 
					 | 
				
			||||||
static short tx_buf[INCHUNK * 2 * 2];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Complex float to short conversion */
 | 
					 | 
				
			||||||
static int float_to_short(short *shrt_out, float *flt_in, int num)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int i;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (i = 0; i < num; i++) {
 | 
					 | 
				
			||||||
		shrt_out[2 * i + 0] = flt_in[2 * i + 0];
 | 
					 | 
				
			||||||
		shrt_out[2 * i + 1] = flt_in[2 * i + 1];
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return i;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Comlpex short to float conversion */
 | 
					 | 
				
			||||||
static int short_to_float(float *flt_out, short *shrt_in, int num)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int i;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (i = 0; i < num; i++) {
 | 
					 | 
				
			||||||
		flt_out[2 * i + 0] = shrt_in[2 * i + 0];
 | 
					 | 
				
			||||||
		flt_out[2 * i + 1] = shrt_in[2 * i + 1];
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return i;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Receive a timestamped chunk from the device */ 
 | 
					 | 
				
			||||||
void RadioInterface::pullBuffer()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	bool local_underrun;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Read samples. Fail if we don't get what we want. */
 | 
					 | 
				
			||||||
	int num_rd = mRadio->readSamples(rx_buf, OUTCHUNK, &overrun,
 | 
					 | 
				
			||||||
					    readTimestamp, &local_underrun);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOG(DEBUG) << "Rx read " << num_rd << " samples from device";
 | 
					 | 
				
			||||||
	assert(num_rd == OUTCHUNK);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	underrun |= local_underrun;
 | 
					 | 
				
			||||||
	readTimestamp += (TIMESTAMP) num_rd;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	short_to_float(rcvBuffer + 2 * rcvCursor, rx_buf, num_rd);
 | 
					 | 
				
			||||||
	rcvCursor += num_rd;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Send timestamped chunk to the device with arbitrary size */ 
 | 
					 | 
				
			||||||
void RadioInterface::pushBuffer()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	if (sendCursor < INCHUNK)
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	float_to_short(tx_buf, sendBuffer, sendCursor);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Write samples. Fail if we don't get what we want. */
 | 
					 | 
				
			||||||
	int num_smpls = mRadio->writeSamples(tx_buf,
 | 
					 | 
				
			||||||
					     sendCursor,
 | 
					 | 
				
			||||||
					     &underrun,
 | 
					 | 
				
			||||||
					     writeTimestamp);
 | 
					 | 
				
			||||||
	assert(num_smpls == sendCursor);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	writeTimestamp += (TIMESTAMP) num_smpls;
 | 
					 | 
				
			||||||
	sendCursor = 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,324 +0,0 @@
 | 
				
			|||||||
/*
 | 
					 | 
				
			||||||
 * Radio device interface with sample rate conversion
 | 
					 | 
				
			||||||
 * Written by Thomas Tsou <ttsou@vt.edu>
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Copyright 2011 Free Software Foundation, Inc.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 | 
				
			||||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
					 | 
				
			||||||
 * the Free Software Foundation, either version 3 of the License, or
 | 
					 | 
				
			||||||
 * (at your option) any later version.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * This program is distributed in the hope that it will be useful,
 | 
					 | 
				
			||||||
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
					 | 
				
			||||||
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
					 | 
				
			||||||
 * GNU Affero General Public License for more details.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * You should have received a copy of the GNU Affero General Public License
 | 
					 | 
				
			||||||
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 | 
					 | 
				
			||||||
 * See the COPYING file in the main directory for details.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#include <radioInterface.h>
 | 
					 | 
				
			||||||
#include <Logger.h>
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* New chunk sizes for resampled rate */
 | 
					 | 
				
			||||||
#ifdef INCHUNK
 | 
					 | 
				
			||||||
  #undef INCHUNK
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
#ifdef OUTCHUNK
 | 
					 | 
				
			||||||
  #undef OUTCHUNK
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Resampling parameters */
 | 
					 | 
				
			||||||
#define INRATE       65 * SAMPSPERSYM
 | 
					 | 
				
			||||||
#define INHISTORY    INRATE * 2
 | 
					 | 
				
			||||||
#define INCHUNK      INRATE * 9
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define OUTRATE      96 * SAMPSPERSYM
 | 
					 | 
				
			||||||
#define OUTHISTORY   OUTRATE * 2
 | 
					 | 
				
			||||||
#define OUTCHUNK     OUTRATE * 9
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Resampler low pass filters */
 | 
					 | 
				
			||||||
signalVector *tx_lpf = 0;
 | 
					 | 
				
			||||||
signalVector *rx_lpf = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Resampler history */
 | 
					 | 
				
			||||||
signalVector *tx_hist = 0;
 | 
					 | 
				
			||||||
signalVector *rx_hist = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Resampler input buffer */
 | 
					 | 
				
			||||||
signalVector *tx_vec = 0;
 | 
					 | 
				
			||||||
signalVector *rx_vec = 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/*
 | 
					 | 
				
			||||||
 * High rate (device facing) buffers
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Transmit side samples are pushed after each burst so accomodate
 | 
					 | 
				
			||||||
 * a resampled burst plus up to a chunk left over from the previous
 | 
					 | 
				
			||||||
 * resampling operation.
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Receive side samples always pulled with a fixed size.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
short tx_buf[INCHUNK * 2 * 4];
 | 
					 | 
				
			||||||
short rx_buf[OUTCHUNK * 2 * 2];
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* 
 | 
					 | 
				
			||||||
 * Utilities and Conversions 
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * Manipulate signal vectors dynamically for two reasons. For one,
 | 
					 | 
				
			||||||
 * it's simpler. And two, it doesn't make any reasonable difference
 | 
					 | 
				
			||||||
 * relative to the high overhead generated by the resampling.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Concatenate signal vectors. Deallocate input vectors. */
 | 
					 | 
				
			||||||
signalVector *concat(signalVector *a, signalVector *b)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	signalVector *vec = new signalVector(*a, *b);
 | 
					 | 
				
			||||||
	delete a;
 | 
					 | 
				
			||||||
	delete b;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return vec;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Segment a signal vector. Deallocate the input vector. */
 | 
					 | 
				
			||||||
signalVector *segment(signalVector *a, int indx, int sz)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	signalVector *vec = new signalVector(sz);
 | 
					 | 
				
			||||||
	a->segmentCopyTo(*vec, indx, sz);
 | 
					 | 
				
			||||||
	delete a;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return vec;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Create a new signal vector from a short array. */
 | 
					 | 
				
			||||||
signalVector *short_to_sigvec(short *smpls, size_t sz)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int i;
 | 
					 | 
				
			||||||
	signalVector *vec = new signalVector(sz);
 | 
					 | 
				
			||||||
	signalVector::iterator itr = vec->begin();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (i = 0; i < sz; i++) {
 | 
					 | 
				
			||||||
		*itr++ = Complex<float>(smpls[2 * i + 0], smpls[2 * i + 1]);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return vec;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Convert and deallocate a signal vector into a short array. */
 | 
					 | 
				
			||||||
int sigvec_to_short(signalVector *vec, short *smpls)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int i;
 | 
					 | 
				
			||||||
	signalVector::iterator itr = vec->begin();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (i = 0; i < vec->size(); i++) {
 | 
					 | 
				
			||||||
		smpls[2 * i + 0] = itr->real();
 | 
					 | 
				
			||||||
		smpls[2 * i + 1] = itr->imag();
 | 
					 | 
				
			||||||
		itr++;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	delete vec;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return i;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Create a new signal vector from a float array. */
 | 
					 | 
				
			||||||
signalVector *float_to_sigvec(float *smpls, int sz)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int i;
 | 
					 | 
				
			||||||
	signalVector *vec = new signalVector(sz);
 | 
					 | 
				
			||||||
	signalVector::iterator itr = vec->begin();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (i = 0; i < sz; i++) {
 | 
					 | 
				
			||||||
		*itr++ = Complex<float>(smpls[2 * i + 0], smpls[2 * i + 1]);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return vec;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Convert and deallocate a signal vector into a float array. */
 | 
					 | 
				
			||||||
int sigvec_to_float(signalVector *vec, float *smpls)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int i;
 | 
					 | 
				
			||||||
	signalVector::iterator itr = vec->begin();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	for (i = 0; i < vec->size(); i++) {
 | 
					 | 
				
			||||||
		smpls[2 * i + 0] = itr->real();
 | 
					 | 
				
			||||||
		smpls[2 * i + 1] = itr->imag();
 | 
					 | 
				
			||||||
		itr++;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	delete vec;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return i;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Initialize resampling signal vectors */
 | 
					 | 
				
			||||||
void init_resampler(signalVector **lpf,
 | 
					 | 
				
			||||||
	   	    signalVector **buf,
 | 
					 | 
				
			||||||
		    signalVector **hist,
 | 
					 | 
				
			||||||
		    int tx)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int P, Q, taps, hist_len;
 | 
					 | 
				
			||||||
	float cutoff_freq;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (tx) {
 | 
					 | 
				
			||||||
		LOG(INFO) << "Initializing Tx resampler";
 | 
					 | 
				
			||||||
		P = OUTRATE;
 | 
					 | 
				
			||||||
		Q = INRATE;
 | 
					 | 
				
			||||||
		taps = 651;
 | 
					 | 
				
			||||||
		hist_len = INHISTORY;
 | 
					 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		LOG(INFO) << "Initializing Rx resampler";
 | 
					 | 
				
			||||||
		P = INRATE;
 | 
					 | 
				
			||||||
		Q = OUTRATE;
 | 
					 | 
				
			||||||
		taps = 961;
 | 
					 | 
				
			||||||
		hist_len = OUTHISTORY;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!*lpf) {
 | 
					 | 
				
			||||||
		cutoff_freq = (P < Q) ? (1.0/(float) Q) : (1.0/(float) P);
 | 
					 | 
				
			||||||
		*lpf = createLPF(cutoff_freq, taps, P);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!*buf) {
 | 
					 | 
				
			||||||
		*buf = new signalVector();
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!*hist);
 | 
					 | 
				
			||||||
		*hist = new signalVector(hist_len);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Resample a signal vector
 | 
					 | 
				
			||||||
 *
 | 
					 | 
				
			||||||
 * The input vector is deallocated and the pointer returned with a vector
 | 
					 | 
				
			||||||
 * of any unconverted samples.
 | 
					 | 
				
			||||||
 */
 | 
					 | 
				
			||||||
signalVector *resmpl_sigvec(signalVector *hist, signalVector **vec,
 | 
					 | 
				
			||||||
			    signalVector *lpf, double in_rate,
 | 
					 | 
				
			||||||
			    double out_rate, int chunk_sz)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	signalVector *resamp_vec;
 | 
					 | 
				
			||||||
	int num_chunks = (*vec)->size() / chunk_sz;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Truncate to a chunk multiple */
 | 
					 | 
				
			||||||
	signalVector trunc_vec(num_chunks * chunk_sz);
 | 
					 | 
				
			||||||
	(*vec)->segmentCopyTo(trunc_vec, 0, num_chunks * chunk_sz);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Update sample buffer with remainder */
 | 
					 | 
				
			||||||
	*vec = segment(*vec, trunc_vec.size(), (*vec)->size() - trunc_vec.size());
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Add history and resample */
 | 
					 | 
				
			||||||
	signalVector input_vec(*hist, trunc_vec);
 | 
					 | 
				
			||||||
	resamp_vec = polyphaseResampleVector(input_vec, in_rate,
 | 
					 | 
				
			||||||
					     out_rate, lpf);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Update history */
 | 
					 | 
				
			||||||
	trunc_vec.segmentCopyTo(*hist, trunc_vec.size() - hist->size(),
 | 
					 | 
				
			||||||
				hist->size());
 | 
					 | 
				
			||||||
	return resamp_vec;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Wrapper for receive-side integer-to-float array resampling */
 | 
					 | 
				
			||||||
 int rx_resmpl_int_flt(float *smpls_out, short *smpls_in, int num_smpls)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int num_resmpld, num_chunks;
 | 
					 | 
				
			||||||
	signalVector *convert_vec, *resamp_vec, *trunc_vec;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!rx_lpf || !rx_vec || !rx_hist)
 | 
					 | 
				
			||||||
		init_resampler(&rx_lpf, &rx_vec, &rx_hist, false);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Convert and add samples to the receive buffer */
 | 
					 | 
				
			||||||
	convert_vec = short_to_sigvec(smpls_in, num_smpls);
 | 
					 | 
				
			||||||
	rx_vec = concat(rx_vec, convert_vec);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	num_chunks = rx_vec->size() / OUTCHUNK;
 | 
					 | 
				
			||||||
	if (num_chunks < 1)
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Resample */ 
 | 
					 | 
				
			||||||
	resamp_vec = resmpl_sigvec(rx_hist, &rx_vec, rx_lpf,
 | 
					 | 
				
			||||||
				   INRATE, OUTRATE, OUTCHUNK);
 | 
					 | 
				
			||||||
	/* Truncate */
 | 
					 | 
				
			||||||
	trunc_vec = segment(resamp_vec, INHISTORY,
 | 
					 | 
				
			||||||
                            resamp_vec->size() - INHISTORY);
 | 
					 | 
				
			||||||
	/* Convert */
 | 
					 | 
				
			||||||
	num_resmpld = sigvec_to_float(trunc_vec, smpls_out);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return num_resmpld; 
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Wrapper for transmit-side float-to-int array resampling */
 | 
					 | 
				
			||||||
int tx_resmpl_flt_int(short *smpls_out, float *smpls_in, int num_smpls)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int num_resmpl, num_chunks;
 | 
					 | 
				
			||||||
	signalVector *convert_vec, *resamp_vec;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!tx_lpf || !tx_vec || !tx_hist)
 | 
					 | 
				
			||||||
		init_resampler(&tx_lpf, &tx_vec, &tx_hist, true);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Convert and add samples to the transmit buffer */
 | 
					 | 
				
			||||||
	convert_vec = float_to_sigvec(smpls_in, num_smpls);
 | 
					 | 
				
			||||||
	tx_vec = concat(tx_vec, convert_vec);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	num_chunks = tx_vec->size() / INCHUNK;
 | 
					 | 
				
			||||||
	if (num_chunks < 1)
 | 
					 | 
				
			||||||
		return 0;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Resample and convert to an integer array */
 | 
					 | 
				
			||||||
	resamp_vec = resmpl_sigvec(tx_hist, &tx_vec, tx_lpf,
 | 
					 | 
				
			||||||
				   OUTRATE, INRATE, INCHUNK);
 | 
					 | 
				
			||||||
	num_resmpl = sigvec_to_short(resamp_vec, smpls_out);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return num_resmpl; 
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Receive a timestamped chunk from the device */ 
 | 
					 | 
				
			||||||
void RadioInterface::pullBuffer()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int num_cv, num_rd;
 | 
					 | 
				
			||||||
	bool local_underrun;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Read samples. Fail if we don't get what we want. */
 | 
					 | 
				
			||||||
	num_rd = mRadio->readSamples(rx_buf, OUTCHUNK, &overrun,
 | 
					 | 
				
			||||||
				     readTimestamp, &local_underrun);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOG(DEBUG) << "Rx read " << num_rd << " samples from device";
 | 
					 | 
				
			||||||
	assert(num_rd == OUTCHUNK);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	underrun |= local_underrun;
 | 
					 | 
				
			||||||
	readTimestamp += (TIMESTAMP) num_rd;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Convert and resample */
 | 
					 | 
				
			||||||
	num_cv = rx_resmpl_int_flt(rcvBuffer + 2 * rcvCursor,
 | 
					 | 
				
			||||||
				   rx_buf, num_rd);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOG(DEBUG) << "Rx read " << num_cv << " samples from resampler";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	rcvCursor += num_cv;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Send a timestamped chunk to the device */ 
 | 
					 | 
				
			||||||
void RadioInterface::pushBuffer()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	int num_cv, num_wr;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (sendCursor < INCHUNK)
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOG(DEBUG) << "Tx wrote " << sendCursor << " samples to resampler";
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Resample and convert */
 | 
					 | 
				
			||||||
	num_cv = tx_resmpl_flt_int(tx_buf, sendBuffer, sendCursor);
 | 
					 | 
				
			||||||
	assert(num_cv > sendCursor);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/* Write samples. Fail if we don't get what we want. */
 | 
					 | 
				
			||||||
	num_wr = mRadio->writeSamples(tx_buf + OUTHISTORY * 2,
 | 
					 | 
				
			||||||
				      num_cv - OUTHISTORY,
 | 
					 | 
				
			||||||
				      &underrun,
 | 
					 | 
				
			||||||
				      writeTimestamp);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	LOG(DEBUG) << "Tx wrote " << num_wr << " samples to device";
 | 
					 | 
				
			||||||
	assert(num_wr == num_wr);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	writeTimestamp += (TIMESTAMP) num_wr;
 | 
					 | 
				
			||||||
	sendCursor = 0;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008, 2009 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2009, 2012 Free Software Foundation, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under the terms of the GNU Affero Public License.
 | 
					* This software is distributed under the terms of the GNU Affero Public License.
 | 
				
			||||||
* See the COPYING file in the main directory for details.
 | 
					* See the COPYING file in the main directory for details.
 | 
				
			||||||
@@ -24,26 +24,57 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
#include "radioInterface.h"
 | 
					#include "radioInterface.h"
 | 
				
			||||||
#include <Logger.h>
 | 
					#include <Logger.h>
 | 
				
			||||||
 | 
					#include "RTMD.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool started = false;
 | 
					bool started = false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Device side buffers */
 | 
				
			||||||
 | 
					static short *rx_buf[CHAN_MAX];
 | 
				
			||||||
 | 
					static short *tx_buf[CHAN_MAX];
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Complex float to short conversion */
 | 
				
			||||||
 | 
					static void floatToShort(short *out, float *in, int num)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  for (int i = 0; i < num; i++) {
 | 
				
			||||||
 | 
					    out[2 * i + 0] = (short) in[2 * i + 0];
 | 
				
			||||||
 | 
					    out[2 * i + 1] = (short) in[2 * i + 1];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Complex short to float conversion */
 | 
				
			||||||
 | 
					static void shortToFloat(float *out, short *in, int num)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  for (int i = 0; i < num; i++) {
 | 
				
			||||||
 | 
					    out[2 * i + 0] = (float) in[2 * i + 0];
 | 
				
			||||||
 | 
					    out[2 * i + 1] = (float) in[2 * i + 1];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RadioInterface::RadioInterface(RadioDevice *wRadio,
 | 
					RadioInterface::RadioInterface(RadioDevice *wRadio,
 | 
				
			||||||
 | 
								       int wChanM,
 | 
				
			||||||
 | 
								       int wSPS,
 | 
				
			||||||
                               int wReceiveOffset,
 | 
					                               int wReceiveOffset,
 | 
				
			||||||
			       int wRadioOversampling,
 | 
					 | 
				
			||||||
			       int wTransceiverOversampling,
 | 
					 | 
				
			||||||
			       GSM::Time wStartTime)
 | 
								       GSM::Time wStartTime)
 | 
				
			||||||
  : underrun(false), sendCursor(0), rcvCursor(0), mOn(false),
 | 
					  : mChanM(wChanM), underrun(false), sendCursor(0), rcvCursor(0), mOn(false),
 | 
				
			||||||
    mRadio(wRadio), receiveOffset(wReceiveOffset),
 | 
					    mRadio(wRadio), receiveOffset(wReceiveOffset), samplesPerSymbol(wSPS),
 | 
				
			||||||
    samplesPerSymbol(wRadioOversampling), powerScaling(1.0),
 | 
					    powerScaling(1.0), loadTest(false)
 | 
				
			||||||
    loadTest(false)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  mClock.set(wStartTime);
 | 
					  mClock.set(wStartTime);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					RadioInterface::~RadioInterface(void)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (mOn) {
 | 
				
			||||||
 | 
					    mRadio->stop();
 | 
				
			||||||
 | 
					    close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
RadioInterface::~RadioInterface(void) {
 | 
					    for (int i = 0; i < mChanM; i++) {
 | 
				
			||||||
  if (rcvBuffer!=NULL) delete rcvBuffer;
 | 
					      if (rcvBuffer[i] != NULL)
 | 
				
			||||||
  //mReceiveFIFO.clear();
 | 
					        delete rcvBuffer[i];
 | 
				
			||||||
 | 
					      if (sendBuffer[i] != NULL)
 | 
				
			||||||
 | 
					        delete sendBuffer[i];
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
double RadioInterface::fullScaleInputValue(void) {
 | 
					double RadioInterface::fullScaleInputValue(void) {
 | 
				
			||||||
@@ -55,11 +86,11 @@ double RadioInterface::fullScaleOutputValue(void) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void RadioInterface::setPowerAttenuation(double atten)
 | 
					void RadioInterface::setPowerAttenuation(double atten, int chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  double rfGain, digAtten;
 | 
					  double rfGain, digAtten;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten);
 | 
					  rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten, chan);
 | 
				
			||||||
  digAtten = atten - mRadio->maxTxGain() + rfGain;
 | 
					  digAtten = atten - mRadio->maxTxGain() + rfGain;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (digAtten < 1.0)
 | 
					  if (digAtten < 1.0)
 | 
				
			||||||
@@ -90,53 +121,75 @@ int RadioInterface::radioifyVector(signalVector &wVector,
 | 
				
			|||||||
  return wVector.size();
 | 
					  return wVector.size();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int RadioInterface::unRadioifyVector(float *floatVector,
 | 
					int RadioInterface::unRadioifyVector(float *floatVector, int offset,
 | 
				
			||||||
				     signalVector &newVector)
 | 
									     signalVector &newVector)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  int i;
 | 
					  int i;
 | 
				
			||||||
  signalVector::iterator itr = newVector.begin();
 | 
					  signalVector::iterator itr = newVector.begin();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  for (i = 0; i < newVector.size(); i++) {
 | 
					  for (i = 0; i < newVector.size(); i++) {
 | 
				
			||||||
    *itr++ = Complex<float>(floatVector[2 * i + 0],
 | 
					    *itr++ = Complex<float>(floatVector[offset + 2 * i + 0],
 | 
				
			||||||
			    floatVector[2 * i + 1]);
 | 
								    floatVector[offset + 2 * i + 1]);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  return newVector.size();
 | 
					  return newVector.size();
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool RadioInterface::tuneTx(double freq)
 | 
					bool RadioInterface::tuneTx(double freq, int chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  return mRadio->setTxFreq(freq);
 | 
					  return mRadio->setTxFreq(freq, chan);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool RadioInterface::tuneRx(double freq)
 | 
					bool RadioInterface::tuneRx(double freq, int chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  return mRadio->setRxFreq(freq);
 | 
					  return mRadio->setRxFreq(freq, chan);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void RadioInterface::start()
 | 
					bool RadioInterface::start()
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  LOG(INFO) << "starting radio interface...";
 | 
					  int i;
 | 
				
			||||||
  mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
 | 
					
 | 
				
			||||||
 | 
					  if (mOn)
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mOn = true;
 | 
				
			||||||
 | 
					#ifdef USRP1
 | 
				
			||||||
 | 
					  mAlignRadioServiceLoopThread = new Thread(32768);
 | 
				
			||||||
 | 
					  mAlignRadioServiceLoopThread->start((void * (*)(void*))AlignRadioServiceLoopAdapter,
 | 
				
			||||||
                                      (void*)this);
 | 
					                                      (void*)this);
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
  writeTimestamp = mRadio->initialWriteTimestamp();
 | 
					  writeTimestamp = mRadio->initialWriteTimestamp();
 | 
				
			||||||
  readTimestamp = mRadio->initialReadTimestamp();
 | 
					  readTimestamp = mRadio->initialReadTimestamp();
 | 
				
			||||||
 | 
					  for (i = 0; i < mChanM; i++) {
 | 
				
			||||||
 | 
					    sendBuffer[i] = new float[8*2*INCHUNK];
 | 
				
			||||||
 | 
					    rcvBuffer[i] = new float[8*2*OUTCHUNK];
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Init I/O specific variables if applicable */ 
 | 
				
			||||||
 | 
					  init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  mRadio->start(); 
 | 
					  mRadio->start(); 
 | 
				
			||||||
  LOG(DEBUG) << "Radio started";
 | 
					  LOG(DEBUG) << "Radio started";
 | 
				
			||||||
  mRadio->updateAlignment(writeTimestamp-10000); 
 | 
					  mRadio->updateAlignment(writeTimestamp-10000); 
 | 
				
			||||||
  mRadio->updateAlignment(writeTimestamp-10000);
 | 
					  mRadio->updateAlignment(writeTimestamp-10000);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  sendBuffer = new float[2*2*INCHUNK*samplesPerSymbol];
 | 
					  return true;
 | 
				
			||||||
  rcvBuffer = new float[2*2*OUTCHUNK*samplesPerSymbol];
 | 
					 | 
				
			||||||
 
 | 
					 | 
				
			||||||
  mOn = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool RadioInterface::stop()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  if (!mOn)
 | 
				
			||||||
 | 
					    return false;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  mOn = false;
 | 
				
			||||||
 | 
					  mRadio->stop();
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifdef USRP1
 | 
				
			||||||
void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
 | 
					void *AlignRadioServiceLoopAdapter(RadioInterface *radioInterface)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  while (1) {
 | 
					  while (radioInterface->on()) {
 | 
				
			||||||
    radioInterface->alignRadio();
 | 
					    radioInterface->alignRadio();
 | 
				
			||||||
    pthread_testcancel();
 | 
					    pthread_testcancel();
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
@@ -147,23 +200,67 @@ void RadioInterface::alignRadio() {
 | 
				
			|||||||
  sleep(60);
 | 
					  sleep(60);
 | 
				
			||||||
  mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
 | 
					  mRadio->updateAlignment(writeTimestamp+ (TIMESTAMP) 10000);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void RadioInterface::driveTransmitRadio(signalVector &radioBurst, bool zeroBurst) {
 | 
					void RadioInterface::driveTransmitRadio(signalVector **radioBurst, bool *zeroBurst)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  int i;
 | 
				
			||||||
 | 
					  RTMD_SET("drvTxRadio");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!mOn) return;
 | 
					  if (!mOn) {
 | 
				
			||||||
 | 
					    RTMD_VAL("drvTxRadio", -1);
 | 
				
			||||||
  radioifyVector(radioBurst, sendBuffer + 2 * sendCursor, powerScaling, zeroBurst);
 | 
					    RTMD_CLEAR("drvTxRadio");
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
  sendCursor += radioBurst.size();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  pushBuffer();
 | 
					 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void RadioInterface::driveReceiveRadio() {
 | 
					  for (i = 0; i < mChanM; i++) {
 | 
				
			||||||
 | 
					    radioifyVector(*radioBurst[i], sendBuffer[i] + 2 * sendCursor,
 | 
				
			||||||
 | 
					                   powerScaling, zeroBurst[i]);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (!mOn) return;
 | 
					  /* 
 | 
				
			||||||
 | 
					   * All bursts should be the same size since all transceivers are
 | 
				
			||||||
 | 
					   * tied with a single clock in the radio interface.
 | 
				
			||||||
 | 
					   */
 | 
				
			||||||
 | 
					  sendCursor += radioBurst[0]->size();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (mReceiveFIFO.size() > 8) return;
 | 
					  pushBuffer();
 | 
				
			||||||
 | 
					  RTMD_CLEAR("drvTxRadio");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static inline void shiftRxBuffers(float **buf, int offset, int len, int chanM)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  for (int i = 0; i < chanM; i++)
 | 
				
			||||||
 | 
					      memmove(buf[i], buf[i] + offset, sizeof(float) * len);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RadioInterface::loadVectors(unsigned tN, int samplesPerBurst,
 | 
				
			||||||
 | 
					                                 int idx, GSM::Time rxClock)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  int i;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (i = 0; i < mChanM; i++) {
 | 
				
			||||||
 | 
					    signalVector rxVector(samplesPerBurst);
 | 
				
			||||||
 | 
					    unRadioifyVector(rcvBuffer[i], idx * 2, rxVector);
 | 
				
			||||||
 | 
					    radioVector *rxBurst = new radioVector(rxVector, rxClock);
 | 
				
			||||||
 | 
					    mReceiveFIFO[i].write(rxBurst);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RadioInterface::driveReceiveRadio()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  RTMD_SET("drvRxRadio");
 | 
				
			||||||
 | 
					  if (!mOn) {
 | 
				
			||||||
 | 
					    RTMD_VAL("drvRxRadio", -1);
 | 
				
			||||||
 | 
					    RTMD_CLEAR("drvRxRadio");
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (mReceiveFIFO[0].size() > 8) {
 | 
				
			||||||
 | 
					    RTMD_VAL("drvRxRadio", 2);
 | 
				
			||||||
 | 
					    RTMD_CLEAR("drvRxRadio");
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  pullBuffer();
 | 
					  pullBuffer();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -173,71 +270,106 @@ void RadioInterface::driveReceiveRadio() {
 | 
				
			|||||||
  int rcvSz = rcvCursor;
 | 
					  int rcvSz = rcvCursor;
 | 
				
			||||||
  int readSz = 0;
 | 
					  int readSz = 0;
 | 
				
			||||||
  const int symbolsPerSlot = gSlotLen + 8;
 | 
					  const int symbolsPerSlot = gSlotLen + 8;
 | 
				
			||||||
 | 
					  int samplesPerBurst = (symbolsPerSlot + (tN % 4 == 0)) * samplesPerSymbol;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  // while there's enough data in receive buffer, form received 
 | 
					  // while there's enough data in receive buffer, form received 
 | 
				
			||||||
  //    GSM bursts and pass up to Transceiver
 | 
					  //    GSM bursts and pass up to Transceiver
 | 
				
			||||||
  // Using the 157-156-156-156 symbols per timeslot format.
 | 
					  // Using the 157-156-156-156 symbols per timeslot format.
 | 
				
			||||||
  while (rcvSz > (symbolsPerSlot + (tN % 4 == 0))*samplesPerSymbol) {
 | 
					  while (rcvSz >= samplesPerBurst) { 
 | 
				
			||||||
    signalVector rxVector((symbolsPerSlot + (tN % 4 == 0))*samplesPerSymbol);
 | 
					 | 
				
			||||||
    unRadioifyVector(rcvBuffer+readSz*2,rxVector);
 | 
					 | 
				
			||||||
    GSM::Time tmpTime = rcvClock;
 | 
					 | 
				
			||||||
    if (rcvClock.FN() >= 0) {
 | 
					    if (rcvClock.FN() >= 0) {
 | 
				
			||||||
      //LOG(DEBUG) << "FN: " << rcvClock.FN();
 | 
					      loadVectors(tN, samplesPerBurst, readSz, rcvClock);
 | 
				
			||||||
      radioVector *rxBurst = NULL;
 | 
					 | 
				
			||||||
      if (!loadTest)
 | 
					 | 
				
			||||||
        rxBurst = new radioVector(rxVector,tmpTime);
 | 
					 | 
				
			||||||
      else {
 | 
					 | 
				
			||||||
	if (tN % 4 == 0)
 | 
					 | 
				
			||||||
	  rxBurst = new radioVector(*finalVec9,tmpTime);
 | 
					 | 
				
			||||||
        else
 | 
					 | 
				
			||||||
          rxBurst = new radioVector(*finalVec,tmpTime); 
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
      mReceiveFIFO.put(rxBurst); 
 | 
					 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    mClock.incTN();
 | 
					    mClock.incTN();
 | 
				
			||||||
    rcvClock.incTN();
 | 
					    rcvClock.incTN();
 | 
				
			||||||
    //if (mReceiveFIFO.size() >= 16) mReceiveFIFO.wait(8);
 | 
					
 | 
				
			||||||
    //LOG(DEBUG) << "receiveFIFO: wrote radio vector at time: " << mClock.get() << ", new size: " << mReceiveFIFO.size() ;
 | 
					    readSz += samplesPerBurst;
 | 
				
			||||||
    readSz += (symbolsPerSlot+(tN % 4 == 0))*samplesPerSymbol;
 | 
					    rcvSz -= samplesPerBurst;
 | 
				
			||||||
    rcvSz -= (symbolsPerSlot+(tN % 4 == 0))*samplesPerSymbol;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    tN = rcvClock.TN();
 | 
					    tN = rcvClock.TN();
 | 
				
			||||||
 | 
					    samplesPerBurst = (symbolsPerSlot + (tN % 4 == 0)) * samplesPerSymbol;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (readSz > 0) {
 | 
					  if (readSz > 0) {
 | 
				
			||||||
    rcvCursor -= readSz;
 | 
					    rcvCursor -= readSz;
 | 
				
			||||||
    memmove(rcvBuffer,rcvBuffer+2*readSz,sizeof(float) * 2 * rcvCursor);
 | 
					    shiftRxBuffers(rcvBuffer, 2 * readSz, 2 * rcvCursor, mChanM);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					  RTMD_CLEAR("drvRxRadio");
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
bool RadioInterface::isUnderrun()
 | 
					double RadioInterface::setRxGain(double dB, int chan)
 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  bool retVal = underrun;
 | 
					 | 
				
			||||||
  underrun = false;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  return retVal;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void RadioInterface::attach(RadioDevice *wRadio, int wRadioOversampling)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
  if (!mOn) {
 | 
					 | 
				
			||||||
    mRadio = wRadio;
 | 
					 | 
				
			||||||
    mRadioOversampling = SAMPSPERSYM;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
double RadioInterface::setRxGain(double dB)
 | 
					 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  if (mRadio)
 | 
					  if (mRadio)
 | 
				
			||||||
    return mRadio->setRxGain(dB);
 | 
					    return mRadio->setRxGain(dB, chan);
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
    return -1;
 | 
					    return -1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
double RadioInterface::getRxGain()
 | 
					double RadioInterface::getRxGain(int chan)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  if (mRadio)
 | 
					  if (mRadio)
 | 
				
			||||||
    return mRadio->getRxGain();
 | 
					    return mRadio->getRxGain(chan);
 | 
				
			||||||
  else
 | 
					  else
 | 
				
			||||||
    return -1;
 | 
					    return -1;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					bool RadioInterface::init()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (int i = 0; i < CHAN_MAX; i++) {
 | 
				
			||||||
 | 
							rx_buf[i] = new short[2 * OUTCHUNK];
 | 
				
			||||||
 | 
							tx_buf[i] = new short[4 * 2 * INCHUNK];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					void RadioInterface::close()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						for (int i = 0; i < CHAN_MAX; i++) {
 | 
				
			||||||
 | 
							delete rx_buf[i];
 | 
				
			||||||
 | 
							delete tx_buf[i];
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Receive a timestamped chunk from the device */ 
 | 
				
			||||||
 | 
					void RadioInterface::pullBuffer()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  bool local_underrun;
 | 
				
			||||||
 | 
					  RTMD_SET("RI-PullBuff");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Read samples. Fail if we don't get what we want. */
 | 
				
			||||||
 | 
					  int num_rd = mRadio->readSamples(rx_buf, mChanM, OUTCHUNK, readTimestamp);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  LOG(DEBUG) << "Rx read " << num_rd << " samples from device";
 | 
				
			||||||
 | 
					  assert(num_rd == OUTCHUNK);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  underrun |= local_underrun;
 | 
				
			||||||
 | 
					  readTimestamp += (TIMESTAMP) num_rd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (int i = 0; i < mChanM; i++)
 | 
				
			||||||
 | 
					    shortToFloat(rcvBuffer[i] + 2 * rcvCursor, rx_buf[i], num_rd);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  rcvCursor += num_rd;
 | 
				
			||||||
 | 
					  RTMD_CLEAR("RI-PullBuff");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* Send timestamped chunk to the device with arbitrary size */ 
 | 
				
			||||||
 | 
					void RadioInterface::pushBuffer()
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  RTMD_SET("RI-PushBuff");
 | 
				
			||||||
 | 
					  if (sendCursor < INCHUNK) {
 | 
				
			||||||
 | 
					    RTMD_VAL("RI-PushBuff", -1);
 | 
				
			||||||
 | 
					    RTMD_CLEAR("RI-PushBuff");
 | 
				
			||||||
 | 
					    return;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  for (int i = 0; i < mChanM; i++)
 | 
				
			||||||
 | 
					    floatToShort(tx_buf[i], sendBuffer[i], sendCursor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /* Write samples. Fail if we don't get what we want. */
 | 
				
			||||||
 | 
					  int num_smpls = mRadio->writeSamples(tx_buf, mChanM, sendCursor,
 | 
				
			||||||
 | 
					                                       writeTimestamp, &underrun);
 | 
				
			||||||
 | 
					  assert(num_smpls == sendCursor);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  writeTimestamp += (TIMESTAMP) num_smpls;
 | 
				
			||||||
 | 
					  sendCursor = 0;
 | 
				
			||||||
 | 
					  RTMD_CLEAR("RI-PushBuff");
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,5 +1,5 @@
 | 
				
			|||||||
/*
 | 
					/*
 | 
				
			||||||
* Copyright 2008 Free Software Foundation, Inc.
 | 
					* Copyright 2008, 2012 Free Software Foundation, Inc.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
					* This software is distributed under multiple licenses; see the COPYING file in the main directory for licensing information for this specific distribuion.
 | 
				
			||||||
*
 | 
					*
 | 
				
			||||||
@@ -12,7 +12,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#ifndef _RADIOINTEFACE_H_
 | 
				
			||||||
 | 
					#define _RADIOINTEFACE_H_
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "sigProcLib.h"  
 | 
					#include "sigProcLib.h"  
 | 
				
			||||||
#include "GSMCommon.h"
 | 
					#include "GSMCommon.h"
 | 
				
			||||||
@@ -25,24 +26,26 @@
 | 
				
			|||||||
#define SAMPSPERSYM 1
 | 
					#define SAMPSPERSYM 1
 | 
				
			||||||
#define INCHUNK    (625)
 | 
					#define INCHUNK    (625)
 | 
				
			||||||
#define OUTCHUNK   (625)
 | 
					#define OUTCHUNK   (625)
 | 
				
			||||||
 | 
					#define CHAN_MAX    2
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static const unsigned gSlotLen = 148;      ///< number of symbols per slot, not counting guard periods
 | 
					static const unsigned gSlotLen = 148;      ///< number of symbols per slot, not counting guard periods
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** class to interface the transceiver with the USRP */
 | 
					/** class to interface the transceiver with the USRP */
 | 
				
			||||||
class RadioInterface {
 | 
					class RadioInterface {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private:
 | 
					protected:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  Thread mAlignRadioServiceLoopThread;	      ///< thread that synchronizes transmit and receive sections
 | 
					  int mChanM;                                 ///< channelizer width
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  VectorFIFO  mReceiveFIFO;		      ///< FIFO that holds receive  bursts
 | 
					  VectorFIFO mReceiveFIFO[CHAN_MAX];	      ///< FIFO that holds receive  bursts
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  RadioDevice *mRadio;			      ///< the USRP object
 | 
					  RadioDevice *mRadio;			      ///< the USRP object
 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
  float *sendBuffer;
 | 
					  float *sendBuffer[CHAN_MAX];
 | 
				
			||||||
  unsigned sendCursor;
 | 
					  unsigned sendCursor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  float *rcvBuffer;
 | 
					  float *rcvBuffer[CHAN_MAX];
 | 
				
			||||||
  unsigned rcvCursor;
 | 
					  unsigned rcvCursor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool underrun;			      ///< indicates writes to USRP are too slow
 | 
					  bool underrun;			      ///< indicates writes to USRP are too slow
 | 
				
			||||||
@@ -54,8 +57,6 @@ private:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  int samplesPerSymbol;			      ///< samples per GSM symbol
 | 
					  int samplesPerSymbol;			      ///< samples per GSM symbol
 | 
				
			||||||
  int receiveOffset;                          ///< offset b/w transmit and receive GSM timestamps, in timeslots
 | 
					  int receiveOffset;                          ///< offset b/w transmit and receive GSM timestamps, in timeslots
 | 
				
			||||||
  int mRadioOversampling;
 | 
					 | 
				
			||||||
  int mTransceiverOversampling;
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  bool mOn;				      ///< indicates radio is on
 | 
					  bool mOn;				      ///< indicates radio is on
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -65,6 +66,10 @@ private:
 | 
				
			|||||||
  int mNumARFCNs;
 | 
					  int mNumARFCNs;
 | 
				
			||||||
  signalVector *finalVec, *finalVec9;
 | 
					  signalVector *finalVec, *finalVec9;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private:
 | 
				
			||||||
 | 
					  /** initialize I/O internals */
 | 
				
			||||||
 | 
					  bool init();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** format samples to USRP */ 
 | 
					  /** format samples to USRP */ 
 | 
				
			||||||
  int radioifyVector(signalVector &wVector,
 | 
					  int radioifyVector(signalVector &wVector,
 | 
				
			||||||
                     float *floatVector,
 | 
					                     float *floatVector,
 | 
				
			||||||
@@ -72,25 +77,34 @@ private:
 | 
				
			|||||||
                     bool zero);
 | 
					                     bool zero);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** format samples from USRP */
 | 
					  /** format samples from USRP */
 | 
				
			||||||
  int unRadioifyVector(float *floatVector, signalVector &wVector);
 | 
					  int unRadioifyVector(float *floatVector, int offset, signalVector &wVector);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** push GSM bursts into the transmit buffer */
 | 
					  /** push GSM bursts into the transmit buffer */
 | 
				
			||||||
  void pushBuffer(void);
 | 
					  virtual void pushBuffer(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** pull GSM bursts from the receive buffer */
 | 
					  /** pull GSM bursts from the receive buffer */
 | 
				
			||||||
  void pullBuffer(void);
 | 
					  virtual void pullBuffer(void);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** load receive vectors into FIFO's */
 | 
				
			||||||
 | 
					  void loadVectors(unsigned tN, int samplesPerBurst, int index, GSM::Time rxClock);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
public:
 | 
					public:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** start the interface */
 | 
					  /** start the interface */
 | 
				
			||||||
  void start();
 | 
					  bool start();
 | 
				
			||||||
 | 
					  bool stop();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  bool started() { return mOn; };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  /** shutdown interface */
 | 
				
			||||||
 | 
					  void close();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** constructor */
 | 
					  /** constructor */
 | 
				
			||||||
  RadioInterface(RadioDevice* wRadio = NULL,
 | 
					  RadioInterface(RadioDevice* wRadio,
 | 
				
			||||||
 | 
							 int wChanM = 1,
 | 
				
			||||||
 | 
							 int wSPS = SAMPSPERSYM,
 | 
				
			||||||
                 int receiveOffset = 3,
 | 
					                 int receiveOffset = 3,
 | 
				
			||||||
		 int wRadioOversampling = SAMPSPERSYM,
 | 
							 GSM::Time wStartTime = GSM::Time(0, 0));
 | 
				
			||||||
		 int wTransceiverOversampling = SAMPSPERSYM,
 | 
					 | 
				
			||||||
		 GSM::Time wStartTime = GSM::Time(0));
 | 
					 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
  /** destructor */
 | 
					  /** destructor */
 | 
				
			||||||
  ~RadioInterface();
 | 
					  ~RadioInterface();
 | 
				
			||||||
@@ -99,37 +113,31 @@ public:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  int getSamplesPerSymbol() { return samplesPerSymbol;}
 | 
					  int getSamplesPerSymbol() { return samplesPerSymbol;}
 | 
				
			||||||
 
 | 
					 
 | 
				
			||||||
  /** check for underrun, resets underrun value */
 | 
					 | 
				
			||||||
  bool isUnderrun();
 | 
					 | 
				
			||||||
  
 | 
					 | 
				
			||||||
  /** attach an existing USRP to this interface */
 | 
					 | 
				
			||||||
  void attach(RadioDevice *wRadio, int wRadioOversampling);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** return the receive FIFO */
 | 
					  /** return the receive FIFO */
 | 
				
			||||||
  VectorFIFO* receiveFIFO() { return &mReceiveFIFO;}
 | 
					  VectorFIFO* receiveFIFO(int num) { return &mReceiveFIFO[num];}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** return the basestation clock */
 | 
					  /** return the basestation clock */
 | 
				
			||||||
  RadioClock* getClock(void) { return &mClock;};
 | 
					  RadioClock* getClock(void) { return &mClock;};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** set transmit frequency */
 | 
					  /** set transmit frequency */
 | 
				
			||||||
  bool tuneTx(double freq);
 | 
					  bool tuneTx(double freq, int chan = 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** set receive frequency */
 | 
					  /** set receive frequency */
 | 
				
			||||||
  bool tuneRx(double freq);
 | 
					  bool tuneRx(double freq, int chan = 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** set receive gain */
 | 
					  /** set receive gain */
 | 
				
			||||||
  double setRxGain(double dB);
 | 
					  double setRxGain(double dB, int chan = 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** get receive gain */
 | 
					  /** get receive gain */
 | 
				
			||||||
  double getRxGain(void);
 | 
					  double getRxGain(int chan = 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** drive transmission of GSM bursts */
 | 
					  /** drive transmission of GSM bursts */
 | 
				
			||||||
  void driveTransmitRadio(signalVector &radioBurst, bool zeroBurst);
 | 
					  void driveTransmitRadio(signalVector **radioBurst, bool *zeroBurst);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** drive reception of GSM bursts */
 | 
					  /** drive reception of GSM bursts */
 | 
				
			||||||
  void driveReceiveRadio();
 | 
					  void driveReceiveRadio();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  void setPowerAttenuation(double atten);
 | 
					  void setPowerAttenuation(double atten, int chan = 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** returns the full-scale transmit amplitude **/
 | 
					  /** returns the full-scale transmit amplitude **/
 | 
				
			||||||
  double fullScaleInputValue();
 | 
					  double fullScaleInputValue();
 | 
				
			||||||
@@ -140,20 +148,8 @@ public:
 | 
				
			|||||||
  /** set thread priority on current thread */
 | 
					  /** set thread priority on current thread */
 | 
				
			||||||
  void setPriority() { mRadio->setPriority(); }
 | 
					  void setPriority() { mRadio->setPriority(); }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  /** get transport bus type of attached device */ 
 | 
					  /** get transport window type of attached device */ 
 | 
				
			||||||
  enum RadioDevice::busType getBus() { return mRadio->getBus(); }
 | 
					  enum RadioDevice::TxWindowType getWindowType() { return mRadio->getWindowType(); }
 | 
				
			||||||
 | 
					 | 
				
			||||||
protected:
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** drive synchronization of Tx/Rx of USRP */
 | 
					 | 
				
			||||||
  void alignRadio();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  /** reset the interface */
 | 
					 | 
				
			||||||
  void reset();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  friend void *AlignRadioServiceLoopAdapter(RadioInterface*);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/** synchronization thread loop */
 | 
					#endif /* _RADIOINTEFACE_H_ */
 | 
				
			||||||
void *AlignRadioServiceLoopAdapter(RadioInterface*);
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
 * Written by Thomas Tsou <ttsou@vt.edu>
 | 
					 * Written by Thomas Tsou <ttsou@vt.edu>
 | 
				
			||||||
 * Based on code by Harvind S Samra <hssamra@kestrelsp.com>
 | 
					 * Based on code by Harvind S Samra <hssamra@kestrelsp.com>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright 2011 Free Software Foundation, Inc.
 | 
					 * Copyright 2011, 2012 Free Software Foundation, Inc.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
@@ -20,6 +20,7 @@
 | 
				
			|||||||
 */
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#include "radioVector.h"
 | 
					#include "radioVector.h"
 | 
				
			||||||
 | 
					#include "RTMD.h"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
radioVector::radioVector(const signalVector& wVector, GSM::Time& wTime)
 | 
					radioVector::radioVector(const signalVector& wVector, GSM::Time& wTime)
 | 
				
			||||||
	: signalVector(wVector), mTime(wTime)
 | 
						: signalVector(wVector), mTime(wTime)
 | 
				
			||||||
@@ -41,24 +42,11 @@ bool radioVector::operator>(const radioVector& other) const
 | 
				
			|||||||
	return mTime > other.mTime;
 | 
						return mTime > other.mTime;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
unsigned VectorFIFO::size()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return mQ.size();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
void VectorFIFO::put(radioVector *ptr)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	mQ.put((void*) ptr);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
radioVector *VectorFIFO::get()
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	return (radioVector*) mQ.get();
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
GSM::Time VectorQueue::nextTime() const
 | 
					GSM::Time VectorQueue::nextTime() const
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	GSM::Time retVal;
 | 
						GSM::Time retVal;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RTMD_SET("getStaleBurst");
 | 
				
			||||||
	mLock.lock();
 | 
						mLock.lock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	while (mQ.size()==0)
 | 
						while (mQ.size()==0)
 | 
				
			||||||
@@ -67,14 +55,18 @@ GSM::Time VectorQueue::nextTime() const
 | 
				
			|||||||
	retVal = mQ.top()->getTime();
 | 
						retVal = mQ.top()->getTime();
 | 
				
			||||||
	mLock.unlock();
 | 
						mLock.unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RTMD_CLEAR("getStaleBurst");
 | 
				
			||||||
	return retVal;
 | 
						return retVal;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
radioVector* VectorQueue::getStaleBurst(const GSM::Time& targTime)
 | 
					radioVector* VectorQueue::getStaleBurst(const GSM::Time& targTime)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						RTMD_SET("getStaleBurst");
 | 
				
			||||||
	mLock.lock();
 | 
						mLock.lock();
 | 
				
			||||||
	if ((mQ.size()==0)) {
 | 
						if ((mQ.size()==0)) {
 | 
				
			||||||
		mLock.unlock();
 | 
							mLock.unlock();
 | 
				
			||||||
 | 
							RTMD_VAL("getStaleBurst",2);
 | 
				
			||||||
 | 
							RTMD_CLEAR("getStaleBurst");
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -82,18 +74,24 @@ radioVector* VectorQueue::getStaleBurst(const GSM::Time& targTime)
 | 
				
			|||||||
		radioVector* retVal = mQ.top();
 | 
							radioVector* retVal = mQ.top();
 | 
				
			||||||
		mQ.pop();
 | 
							mQ.pop();
 | 
				
			||||||
		mLock.unlock();
 | 
							mLock.unlock();
 | 
				
			||||||
 | 
							RTMD_CLEAR("getStaleBurst");
 | 
				
			||||||
		return retVal;
 | 
							return retVal;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	mLock.unlock();
 | 
						mLock.unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RTMD_VAL("getStaleBurst",2);
 | 
				
			||||||
 | 
						RTMD_CLEAR("getStaleBurst");
 | 
				
			||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
radioVector* VectorQueue::getCurrentBurst(const GSM::Time& targTime)
 | 
					radioVector* VectorQueue::getCurrentBurst(const GSM::Time& targTime)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
 | 
						RTMD_SET("getStaleBurst");
 | 
				
			||||||
	mLock.lock();
 | 
						mLock.lock();
 | 
				
			||||||
	if ((mQ.size()==0)) {
 | 
						if ((mQ.size()==0)) {
 | 
				
			||||||
		mLock.unlock();
 | 
							mLock.unlock();
 | 
				
			||||||
 | 
							RTMD_VAL("getStaleBurst",2);
 | 
				
			||||||
 | 
							RTMD_CLEAR("getStaleBurst");
 | 
				
			||||||
		return NULL;
 | 
							return NULL;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -101,9 +99,12 @@ radioVector* VectorQueue::getCurrentBurst(const GSM::Time& targTime)
 | 
				
			|||||||
		radioVector* retVal = mQ.top();
 | 
							radioVector* retVal = mQ.top();
 | 
				
			||||||
		mQ.pop();
 | 
							mQ.pop();
 | 
				
			||||||
		mLock.unlock();
 | 
							mLock.unlock();
 | 
				
			||||||
 | 
							RTMD_CLEAR("getStaleBurst");
 | 
				
			||||||
		return retVal;
 | 
							return retVal;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	mLock.unlock();
 | 
						mLock.unlock();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						RTMD_VAL("getStaleBurst",2);
 | 
				
			||||||
 | 
						RTMD_CLEAR("getStaleBurst");
 | 
				
			||||||
	return NULL;
 | 
						return NULL;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2,7 +2,7 @@
 | 
				
			|||||||
 * Written by Thomas Tsou <ttsou@vt.edu>
 | 
					 * Written by Thomas Tsou <ttsou@vt.edu>
 | 
				
			||||||
 * Based on code by Harvind S Samra <hssamra@kestrelsp.com>
 | 
					 * Based on code by Harvind S Samra <hssamra@kestrelsp.com>
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * Copyright 2011 Free Software Foundation, Inc.
 | 
					 * Copyright 2011, 2012 Free Software Foundation, Inc.
 | 
				
			||||||
 *
 | 
					 *
 | 
				
			||||||
 * This program is free software: you can redistribute it and/or modify
 | 
					 * This program is free software: you can redistribute it and/or modify
 | 
				
			||||||
 * it under the terms of the GNU Affero General Public License as published by
 | 
					 * it under the terms of the GNU Affero General Public License as published by
 | 
				
			||||||
@@ -37,14 +37,7 @@ private:
 | 
				
			|||||||
	GSM::Time mTime;
 | 
						GSM::Time mTime;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class VectorFIFO {
 | 
					class VectorFIFO : public InterthreadQueue<radioVector> {
 | 
				
			||||||
public:
 | 
					 | 
				
			||||||
	unsigned size();
 | 
					 | 
				
			||||||
	void put(radioVector *ptr);
 | 
					 | 
				
			||||||
	radioVector *get();
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
private:
 | 
					 | 
				
			||||||
	PointerFIFO mQ;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class VectorQueue : public InterthreadPriorityQueue<radioVector> {
 | 
					class VectorQueue : public InterthreadPriorityQueue<radioVector> {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -36,12 +36,6 @@
 | 
				
			|||||||
#include <Logger.h>
 | 
					#include <Logger.h>
 | 
				
			||||||
#include <Configuration.h>
 | 
					#include <Configuration.h>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#ifdef RESAMPLE
 | 
					 | 
				
			||||||
  #define DEVICERATE 400e3
 | 
					 | 
				
			||||||
#else
 | 
					 | 
				
			||||||
  #define DEVICERATE 1625e3/6 
 | 
					 | 
				
			||||||
#endif
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
using namespace std;
 | 
					using namespace std;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ConfigurationTable gConfig("/etc/OpenBTS/OpenBTS.db");
 | 
					ConfigurationTable gConfig("/etc/OpenBTS/OpenBTS.db");
 | 
				
			||||||
@@ -58,6 +52,7 @@ static void ctrlCHandler(int signo)
 | 
				
			|||||||
int main(int argc, char *argv[])
 | 
					int main(int argc, char *argv[])
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
  std::string deviceArgs;
 | 
					  std::string deviceArgs;
 | 
				
			||||||
 | 
					  std::string txAntenna, rxAntenna;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if (argc == 3)
 | 
					  if (argc == 3)
 | 
				
			||||||
  {
 | 
					  {
 | 
				
			||||||
@@ -88,16 +83,43 @@ int main(int argc, char *argv[])
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  srandom(time(NULL));
 | 
					  srandom(time(NULL));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  int mOversamplingRate = numARFCN/2 + numARFCN;
 | 
					  RadioDevice *usrp = RadioDevice::make(SAMPSPERSYM);
 | 
				
			||||||
  RadioDevice *usrp = RadioDevice::make(DEVICERATE * SAMPSPERSYM);
 | 
					  int radioType = usrp->open(deviceArgs);
 | 
				
			||||||
  if (!usrp->open(deviceArgs)) {
 | 
					  if (radioType < 0) {
 | 
				
			||||||
    LOG(ALERT) << "Transceiver exiting..." << std::endl;
 | 
					    LOG(ALERT) << "Transceiver exiting..." << std::endl;
 | 
				
			||||||
    return EXIT_FAILURE;
 | 
					    return EXIT_FAILURE;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  RadioInterface* radio = new RadioInterface(usrp,3,SAMPSPERSYM,mOversamplingRate,false);
 | 
					  if (gConfig.defines("GSM.Radio.TxAntenna"))
 | 
				
			||||||
  Transceiver *trx = new Transceiver(gConfig.getNum("TRX.Port"),gConfig.getStr("TRX.IP").c_str(),SAMPSPERSYM,GSM::Time(3,0),radio);
 | 
					    txAntenna = gConfig.getStr("GSM.Radio.TxAntenna").c_str();
 | 
				
			||||||
  trx->receiveFIFO(radio->receiveFIFO());
 | 
					  if (gConfig.defines("GSM.Radio.RxAntenna"))
 | 
				
			||||||
 | 
					    rxAntenna = gConfig.getStr("GSM.Radio.RxAntenna").c_str();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  if (txAntenna != "")  
 | 
				
			||||||
 | 
					    usrp->setTxAntenna(txAntenna);
 | 
				
			||||||
 | 
					  if (rxAntenna != "")  
 | 
				
			||||||
 | 
					    usrp->setRxAntenna(rxAntenna);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  LOG(INFO) << "transceiver using transmit antenna " << usrp->getRxAntenna();
 | 
				
			||||||
 | 
					  LOG(INFO) << "transceiver using receive antenna " << usrp->getTxAntenna();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  RadioInterface* radio;
 | 
				
			||||||
 | 
					  switch (radioType) {
 | 
				
			||||||
 | 
					  case RadioDevice::NORMAL:
 | 
				
			||||||
 | 
					    radio = new RadioInterface(usrp, 3, SAMPSPERSYM, false);
 | 
				
			||||||
 | 
					    break;
 | 
				
			||||||
 | 
					  case RadioDevice::RESAMP:
 | 
				
			||||||
 | 
					  default:
 | 
				
			||||||
 | 
					    LOG(ALERT) << "Unsupported configuration";
 | 
				
			||||||
 | 
					    return EXIT_FAILURE;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  int port = gConfig.getNum("TRX.Port");
 | 
				
			||||||
 | 
					  const char *addr = gConfig.getStr("TRX.IP").c_str();
 | 
				
			||||||
 | 
					  DriveLoop *drive = new DriveLoop(SAMPSPERSYM,GSM::Time(3,0),radio);
 | 
				
			||||||
 | 
					  Transceiver *trx = new Transceiver(port, addr, SAMPSPERSYM, radio, drive, 0);
 | 
				
			||||||
 | 
					  radio->activateChan(0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/*
 | 
					/*
 | 
				
			||||||
  signalVector *gsmPulse = generateGSMPulse(2,1);
 | 
					  signalVector *gsmPulse = generateGSMPulse(2,1);
 | 
				
			||||||
  BitVector normalBurstSeg = "0000101010100111110010101010010110101110011000111001101010000";
 | 
					  BitVector normalBurstSeg = "0000101010100111110010101010010110101110011000111001101010000";
 | 
				
			||||||
@@ -130,12 +152,13 @@ int main(int argc, char *argv[])
 | 
				
			|||||||
  usrp->loadBurst(finalVecShort,finalVec.size());
 | 
					  usrp->loadBurst(finalVecShort,finalVec.size());
 | 
				
			||||||
*/
 | 
					*/
 | 
				
			||||||
  trx->start();
 | 
					  trx->start();
 | 
				
			||||||
  //int i = 0;
 | 
					
 | 
				
			||||||
  while(!gbShutdown) { sleep(1); }//i++; if (i==60) break;}
 | 
					  while(!gbShutdown) { sleep(1); }//i++; if (i==60) break;}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  cout << "Shutting down transceiver..." << endl;
 | 
					  cout << "Shutting down transceiver..." << endl;
 | 
				
			||||||
 | 
					  trx->shutdown();
 | 
				
			||||||
 | 
					
 | 
				
			||||||
//  trx->stop();
 | 
					 | 
				
			||||||
  delete trx;
 | 
					  delete trx;
 | 
				
			||||||
//  delete radio;
 | 
					  delete drive;
 | 
				
			||||||
 | 
					  delete radio;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
							
								
								
									
										12
									
								
								configure.ac
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								configure.ac
									
									
									
									
									
								
							@@ -72,11 +72,6 @@ AC_ARG_WITH(singledb, [
 | 
				
			|||||||
        [enable single daughterboard use on USRP1])
 | 
					        [enable single daughterboard use on USRP1])
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AC_ARG_WITH(resamp, [
 | 
					 | 
				
			||||||
    AS_HELP_STRING([--with-resamp],
 | 
					 | 
				
			||||||
        [enable resampling for non-52MHz devices])
 | 
					 | 
				
			||||||
])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AC_ARG_WITH(extref, [
 | 
					AC_ARG_WITH(extref, [
 | 
				
			||||||
    AS_HELP_STRING([--with-extref],
 | 
					    AS_HELP_STRING([--with-extref],
 | 
				
			||||||
        [enable external reference on UHD devices])
 | 
					        [enable external reference on UHD devices])
 | 
				
			||||||
@@ -98,14 +93,10 @@ AS_IF([test "x$with_usrp1" = "xyes"], [
 | 
				
			|||||||
])
 | 
					])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AS_IF([test "x$with_uhd" = "xyes"],[
 | 
					AS_IF([test "x$with_uhd" = "xyes"],[
 | 
				
			||||||
    PKG_CHECK_MODULES(UHD, uhd >= 003.002.000)
 | 
					    PKG_CHECK_MODULES(UHD, uhd >= 003.004.000)
 | 
				
			||||||
    AC_DEFINE(USE_UHD, 1, Define to 1 if using UHD)
 | 
					    AC_DEFINE(USE_UHD, 1, Define to 1 if using UHD)
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AS_IF([test "x$with_resamp" = "xyes"], [
 | 
					 | 
				
			||||||
    AC_DEFINE(RESAMPLE, 1, Define to 1 for resampling)
 | 
					 | 
				
			||||||
])
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
AS_IF([test "x$with_extref" = "xyes"], [
 | 
					AS_IF([test "x$with_extref" = "xyes"], [
 | 
				
			||||||
    AC_DEFINE(EXTREF, 1, Define to 1 for external reference)
 | 
					    AC_DEFINE(EXTREF, 1, Define to 1 for external reference)
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
@@ -114,7 +105,6 @@ AS_IF([test "x$with_singledb" = "xyes"], [
 | 
				
			|||||||
    AC_DEFINE(SINGLEDB, 1, Define to 1 for single daughterboard)
 | 
					    AC_DEFINE(SINGLEDB, 1, Define to 1 for single daughterboard)
 | 
				
			||||||
])
 | 
					])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AM_CONDITIONAL(RESAMPLE, [test "x$with_resamp" = "xyes"])
 | 
					 | 
				
			||||||
AM_CONDITIONAL(UHD, [test "x$with_uhd" = "xyes"])
 | 
					AM_CONDITIONAL(UHD, [test "x$with_uhd" = "xyes"])
 | 
				
			||||||
AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])
 | 
					AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user