mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-03 05:33:16 +00:00
Compare commits
57 Commits
Hoernchen/
...
achemeris/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0832c0c34b | ||
|
|
9cddd4aa5d | ||
|
|
f1c5b4f765 | ||
|
|
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,66 +28,91 @@
|
|||||||
|
|
||||||
#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
|
||||||
{
|
{
|
||||||
int *p;
|
public:
|
||||||
for (int i=0; i<20; i++) {
|
QueueWriter() : Thread("QueueWriter") {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void runThread()
|
||||||
|
{
|
||||||
|
int *p;
|
||||||
|
for (int i=0; i<20; i++) {
|
||||||
|
p = new int;
|
||||||
|
*p = i;
|
||||||
|
COUT("queue write " << *p);
|
||||||
|
gQ.write(p);
|
||||||
|
msleep(1);
|
||||||
|
}
|
||||||
p = new int;
|
p = new int;
|
||||||
*p = i;
|
*p = -1;
|
||||||
COUT("queue write " << *p);
|
|
||||||
gQ.write(p);
|
gQ.write(p);
|
||||||
if (random()%2) sleep(1);
|
|
||||||
}
|
}
|
||||||
p = new int;
|
};
|
||||||
*p = -1;
|
|
||||||
gQ.write(p);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* qReader(void*)
|
class QueueReader : public Thread
|
||||||
{
|
{
|
||||||
bool done = false;
|
public:
|
||||||
while (!done) {
|
QueueReader() : Thread("QueueReader") {}
|
||||||
int *p = gQ.read();
|
|
||||||
COUT("queue read " << *p);
|
protected:
|
||||||
if (*p<0) done=true;
|
virtual void runThread()
|
||||||
delete p;
|
{
|
||||||
|
bool done = false;
|
||||||
|
while (!done) {
|
||||||
|
int *p = gQ.read();
|
||||||
|
COUT("queue read " << *p);
|
||||||
|
if (*p<0) done=true;
|
||||||
|
delete p;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void* mapWriter(void*)
|
class MapWriter : public Thread
|
||||||
{
|
{
|
||||||
int *p;
|
public:
|
||||||
for (int i=0; i<20; i++) {
|
MapWriter() : Thread("MapWriter") {}
|
||||||
p = new int;
|
|
||||||
*p = i;
|
|
||||||
COUT("map write " << *p);
|
|
||||||
gMap.write(i,p);
|
|
||||||
if (random()%2) sleep(1);
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void* mapReader(void*)
|
protected:
|
||||||
{
|
virtual void runThread()
|
||||||
for (int i=0; i<20; i++) {
|
{
|
||||||
int *p = gMap.read(i);
|
int *p;
|
||||||
COUT("map read " << *p);
|
for (int i=0; i<20; i++) {
|
||||||
// InterthreadMap will delete the pointers
|
p = new int;
|
||||||
// delete p;
|
*p = i;
|
||||||
|
COUT("map write " << *p);
|
||||||
|
gMap.write(i,p);
|
||||||
|
msleep(1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
};
|
||||||
}
|
|
||||||
|
class MapReader : public Thread
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
MapReader() : Thread("MapReader") {}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
virtual void runThread()
|
||||||
|
{
|
||||||
|
for (int i=0; i<20; i++) {
|
||||||
|
int *p = gMap.read(i);
|
||||||
|
COUT("map read " << *p);
|
||||||
|
// InterthreadMap will delete the pointers
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -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,59 +29,73 @@
|
|||||||
|
|
||||||
#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
|
||||||
{
|
{
|
||||||
UDPSocket readSocket(5934, "localhost", 5061);
|
public:
|
||||||
readSocket.nonblocking();
|
TestReaderIP() : Thread("TestReaderIP") {}
|
||||||
int rc = 0;
|
|
||||||
while (rc<gNumToSend) {
|
protected:
|
||||||
char buf[MAX_UDP_LENGTH];
|
virtual void runThread()
|
||||||
int count = readSocket.read(buf);
|
{
|
||||||
if (count>0) {
|
UDPSocket readSocket(5934, "localhost", 5061);
|
||||||
COUT("read: " << buf);
|
readSocket.nonblocking();
|
||||||
rc++;
|
int rc = 0;
|
||||||
} else {
|
while (rc<gNumToSend) {
|
||||||
sleep(2);
|
char buf[MAX_UDP_LENGTH];
|
||||||
|
int count = readSocket.read(buf);
|
||||||
|
if (count>0) {
|
||||||
|
COUT("IP read: " << buf);
|
||||||
|
rc++;
|
||||||
|
} else {
|
||||||
|
COUT("IP sleeping...");
|
||||||
|
sleep(2);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return NULL;
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
class TestReaderUnix : public Thread
|
||||||
|
|
||||||
void *testReaderUnix(void *)
|
|
||||||
{
|
{
|
||||||
UDDSocket readSocket("testDestination");
|
public:
|
||||||
readSocket.nonblocking();
|
TestReaderUnix() : Thread("TestReaderUnix") {}
|
||||||
int rc = 0;
|
|
||||||
while (rc<gNumToSend) {
|
protected:
|
||||||
char buf[MAX_UDP_LENGTH];
|
virtual void runThread()
|
||||||
int count = readSocket.read(buf);
|
{
|
||||||
if (count>0) {
|
UDDSocket readSocket("testDestination");
|
||||||
COUT("read: " << buf);
|
readSocket.nonblocking();
|
||||||
rc++;
|
int rc = 0;
|
||||||
} else {
|
while (rc<gNumToSend) {
|
||||||
sleep(2);
|
char buf[MAX_UDP_LENGTH];
|
||||||
|
int count = readSocket.read(buf);
|
||||||
|
if (count>0) {
|
||||||
|
COUT("UNIX read: " << buf);
|
||||||
|
rc++;
|
||||||
|
} else {
|
||||||
|
COUT("UNIX sleeping...");
|
||||||
|
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");
|
||||||
@@ -92,12 +107,10 @@ 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));
|
|
||||||
bool res;
|
|
||||||
// (pat) Moved initialization to constructor to avoid crash in destructor.
|
|
||||||
//res = pthread_attr_init(&mAttrib);
|
|
||||||
//assert(!res);
|
|
||||||
res = pthread_attr_setstacksize(&mAttrib, mStackSize);
|
|
||||||
assert(!res);
|
|
||||||
res = pthread_create(&mThread, &mAttrib, task, arg);
|
|
||||||
assert(!res);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
// Lock startup synchronization mutex. It will be used in conjunction with
|
||||||
|
// mThreadInitializedEvent and mThreadStartStopEvent conditional variables.
|
||||||
|
ScopedLock lock(mThreadStartupMutex);
|
||||||
|
|
||||||
|
{
|
||||||
|
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,47 +138,105 @@ 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:
|
public:
|
||||||
|
|
||||||
pthread_t mThread;
|
typedef void *(*Adaptor)(void*);
|
||||||
pthread_attr_t mAttrib;
|
enum ReturnStatus {
|
||||||
// FIXME -- Can this be reduced now?
|
RETURN_OK = 0,
|
||||||
size_t mStackSize;
|
ALREADY_STARTED,
|
||||||
|
ALREADY_IDLE,
|
||||||
|
PTHREAD_ERROR,
|
||||||
|
WRONG_STATE,
|
||||||
|
RETURN_TIMEOUT
|
||||||
|
};
|
||||||
|
enum ThreadState {
|
||||||
|
THREAD_STATE_IDLE, ///< Thread is not started. On start() => STARTING
|
||||||
|
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
|
||||||
|
};
|
||||||
|
enum {
|
||||||
|
THREAD_STARTUP_TIMEOUT=5, ///< Time to wait for thread startup (in seconds).
|
||||||
|
THREAD_STOP_TIMEOUT=5 ///< Time to wait for thread stop (in seconds).
|
||||||
|
};
|
||||||
|
|
||||||
public:
|
/** Create a thread in a non-running state. */
|
||||||
|
Thread(const std::string &name, size_t stackSize = (65536*4));
|
||||||
|
|
||||||
/** Create a thread in a non-running state. */
|
/** Destroy the Thread. */
|
||||||
Thread(size_t wStackSize = (65536*4)):mThread((pthread_t)0) {
|
virtual ~Thread();
|
||||||
pthread_attr_init(&mAttrib); // (pat) moved this here.
|
|
||||||
mStackSize=wStackSize;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/** Start the thread. */
|
||||||
Destroy the Thread.
|
ReturnStatus startThread(void *data=NULL);
|
||||||
It should be stopped and joined.
|
|
||||||
*/
|
|
||||||
// (pat) If the Thread is destroyed without being started, then mAttrib is undefined. Oops.
|
|
||||||
~Thread() { pthread_attr_destroy(&mAttrib); }
|
|
||||||
|
|
||||||
|
/** Stop the thread. */
|
||||||
|
ReturnStatus stopThread();
|
||||||
|
|
||||||
/** Start the thread on a task. */
|
ThreadState getThreadState() const
|
||||||
void start(void *(*task)(void*), void *arg);
|
{
|
||||||
|
ScopedLock lock(mThreadStateMutex);
|
||||||
|
return mThreadState;
|
||||||
|
}
|
||||||
|
|
||||||
/** Join a thread that will stop on its own. */
|
bool isThreadRunning() const
|
||||||
void join() { int s = pthread_join(mThread,NULL); assert(!s); mThread = 0; }
|
{
|
||||||
|
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
|
||||||
// vim: ts=4 sw=4
|
// vim: ts=4 sw=4
|
||||||
|
|||||||
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
|
||||||
295
Transceiver52M/DriveLoop.cpp
Normal file
295
Transceiver52M/DriveLoop.cpp
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
/*
|
||||||
|
* 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>
|
||||||
|
|
||||||
|
using namespace GSM;
|
||||||
|
|
||||||
|
DriveLoop::DriveLoop(int wBasePort, const char *TRXAddress,
|
||||||
|
RadioInterface *wRadioInterface,
|
||||||
|
int wChanM, int wC0, int wSamplesPerSymbol,
|
||||||
|
GSM::Time wTransmitLatency)
|
||||||
|
: Thread("DriveLoop")
|
||||||
|
, mUseCount(0)
|
||||||
|
, 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();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DriveLoop::start()
|
||||||
|
{
|
||||||
|
// Use count must not be negative
|
||||||
|
assert(mUseCount>=0);
|
||||||
|
|
||||||
|
mUseCount++;
|
||||||
|
if (mUseCount>1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
startThread();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DriveLoop::stop()
|
||||||
|
{
|
||||||
|
// Use count must not be negative or zero
|
||||||
|
assert(mUseCount>0);
|
||||||
|
|
||||||
|
mUseCount--;
|
||||||
|
if (mUseCount>0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
stopThread();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DriveLoop::pushRadioVector(GSM::Time &nowTime)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
radioVector *staleBurst;
|
||||||
|
radioVector *next;
|
||||||
|
|
||||||
|
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];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
RadioClock *radioClock = (mRadioInterface->getClock());
|
||||||
|
while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
|
||||||
|
pushRadioVector(mTransmitDeadlineClock);
|
||||||
|
mTransmitDeadlineClock.incTN();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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()
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DriveLoop::runThread()
|
||||||
|
{
|
||||||
|
setPriority();
|
||||||
|
|
||||||
|
while (isThreadRunning()) {
|
||||||
|
driveReceiveFIFO();
|
||||||
|
driveTransmitFIFO();
|
||||||
|
}
|
||||||
|
}
|
||||||
195
Transceiver52M/DriveLoop.h
Normal file
195
Transceiver52M/DriveLoop.h
Normal file
@@ -0,0 +1,195 @@
|
|||||||
|
/*
|
||||||
|
* 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 : private Thread {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
int mUseCount; ///< Use counter
|
||||||
|
|
||||||
|
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();
|
||||||
|
|
||||||
|
/** Increase usage counter and start the thread if not started yet */
|
||||||
|
bool start();
|
||||||
|
/** Decrease usage counter and stop the thread if no users left */
|
||||||
|
bool stop();
|
||||||
|
|
||||||
|
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_ */
|
||||||
@@ -52,21 +52,14 @@ COMMON_SOURCES = \
|
|||||||
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
|
|
||||||
else
|
|
||||||
libtransceiver_la_SOURCES = \
|
|
||||||
$(COMMON_SOURCES) \
|
|
||||||
radioIO.cpp
|
|
||||||
endif
|
|
||||||
|
|
||||||
noinst_PROGRAMS = \
|
noinst_PROGRAMS = \
|
||||||
USRPping \
|
|
||||||
transceiver \
|
transceiver \
|
||||||
sigProcLibTest
|
sigProcLibTest
|
||||||
|
|
||||||
@@ -83,12 +76,7 @@ noinst_HEADERS = \
|
|||||||
rcvLPF_651.h \
|
rcvLPF_651.h \
|
||||||
sendLPF_961.h
|
sendLPF_961.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 +92,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
|
||||||
|
|||||||
@@ -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.
|
||||||
@@ -36,6 +37,8 @@
|
|||||||
#include "config.h"
|
#include "config.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern volatile bool gbShutdown;
|
||||||
|
|
||||||
using namespace GSM;
|
using namespace GSM;
|
||||||
|
|
||||||
#define USB_LATENCY_INTRVL 10,0
|
#define USB_LATENCY_INTRVL 10,0
|
||||||
@@ -48,72 +51,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;
|
||||||
mPower = -10;
|
mFreqOffset = 0.0;
|
||||||
|
|
||||||
|
mPower = -20;
|
||||||
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,182 +115,18 @@ 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)
|
||||||
{
|
{
|
||||||
bool needDFE = (mMaxExpectedDelay > 1);
|
bool needDFE = (mMaxExpectedDelay > 1);
|
||||||
|
|
||||||
radioVector *rxBurst = (radioVector *) mReceiveFIFO->get();
|
radioVector *rxBurst = (radioVector *) mReceiveFIFO->read();
|
||||||
|
|
||||||
if (!rxBurst) return NULL;
|
if (!rxBurst) return NULL;
|
||||||
|
|
||||||
@@ -310,9 +134,9 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
|
|||||||
|
|
||||||
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;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -322,6 +146,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;
|
||||||
@@ -339,8 +164,9 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
|
|||||||
|
|
||||||
// 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;
|
||||||
@@ -413,7 +239,7 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
|
|||||||
// 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,
|
||||||
@@ -440,16 +266,62 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime,
|
|||||||
return burst;
|
return burst;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Transceiver::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;
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Transceiver::start()
|
void Transceiver::start()
|
||||||
{
|
{
|
||||||
mControlServiceLoopThread->start((void * (*)(void*))ControlServiceLoopAdapter,(void*) this);
|
mRunning = true;
|
||||||
|
mControlServiceLoop.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();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -462,10 +334,19 @@ void Transceiver::driveControl()
|
|||||||
char buffer[MAX_PACKET_LENGTH];
|
char buffer[MAX_PACKET_LENGTH];
|
||||||
int msgLen = -1;
|
int msgLen = -1;
|
||||||
buffer[0] = '\0';
|
buffer[0] = '\0';
|
||||||
|
|
||||||
msgLen = mControlSocket.read(buffer);
|
|
||||||
|
|
||||||
if (msgLen < 1) {
|
try {
|
||||||
|
msgLen = mControlSocket.read(buffer);
|
||||||
|
if (msgLen < 1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
/* Ignore the read exception on shutdown */
|
||||||
|
if (!mRunning) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(ALERT) << "Caught UHD socket exception";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -474,8 +355,8 @@ void Transceiver::driveControl()
|
|||||||
char response[MAX_PACKET_LENGTH];
|
char response[MAX_PACKET_LENGTH];
|
||||||
|
|
||||||
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";
|
||||||
@@ -486,25 +367,40 @@ void Transceiver::driveControl()
|
|||||||
if (strcmp(command,"POWEROFF")==0) {
|
if (strcmp(command,"POWEROFF")==0) {
|
||||||
// turn off transmitter/demod
|
// turn off transmitter/demod
|
||||||
sprintf(response,"RSP POWEROFF 0");
|
sprintf(response,"RSP POWEROFF 0");
|
||||||
|
if (mOn) {
|
||||||
|
// Stop radio interface threads.
|
||||||
|
mOn = false;
|
||||||
|
mFIFOServiceLoop.shutdown();
|
||||||
|
mTransmitPriorityQueueServiceLoop.shutdown();
|
||||||
|
|
||||||
|
mRadioInterface->stop();
|
||||||
|
mDriveLoop->stop();
|
||||||
|
|
||||||
|
if (mPrimary) {
|
||||||
|
gbShutdown = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
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 (!mOn) {
|
||||||
// Prepare for thread start
|
// Prepare for thread start
|
||||||
mPower = -20;
|
|
||||||
mRadioInterface->start();
|
mRadioInterface->start();
|
||||||
generateRACHSequence(*gsmPulse,mSamplesPerSymbol);
|
mDriveLoop->start();
|
||||||
|
|
||||||
|
mDriveLoop->writeClockInterface();
|
||||||
|
if (mPrimary) {
|
||||||
|
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 +415,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 +436,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 +451,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 {
|
||||||
|
sprintf(response,"RSP RXTUNE 0 %d",freqKhz);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
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 {
|
||||||
|
sprintf(response,"RSP TXTUNE 0 %d",freqKhz);
|
||||||
}
|
}
|
||||||
else
|
|
||||||
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,7 +489,7 @@ 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);
|
||||||
@@ -603,13 +498,14 @@ void Transceiver::driveControl()
|
|||||||
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
|
sprintf(response,"RSP SETSLOT 1 %d %d",timeslot,corrCode);
|
||||||
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);
|
||||||
@@ -618,14 +514,24 @@ void Transceiver::driveControl()
|
|||||||
|
|
||||||
bool Transceiver::driveTransmitPriorityQueue()
|
bool Transceiver::driveTransmitPriorityQueue()
|
||||||
{
|
{
|
||||||
|
|
||||||
char buffer[gSlotLen+50];
|
char buffer[gSlotLen+50];
|
||||||
|
|
||||||
// check data socket
|
if (!mOn)
|
||||||
size_t msgLen = mDataSocket.read(buffer);
|
return true;
|
||||||
|
|
||||||
if (msgLen!=gSlotLen+1+4+1) {
|
try {
|
||||||
LOG(ERR) << "badly formatted packet on GSM->TRX interface";
|
size_t msgLen = mDataSocket.read(buffer);
|
||||||
|
if (msgLen!=gSlotLen+1+4+1) {
|
||||||
|
LOG(ERR) << "badly formatted packet on GSM->TRX interface";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} catch (...) {
|
||||||
|
if (!mOn) {
|
||||||
|
/* Shutdown condition. End the thread. */
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(ALERT) << "Caught UHD socket exception";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -634,28 +540,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);
|
||||||
|
|
||||||
@@ -676,154 +566,106 @@ bool Transceiver::driveTransmitPriorityQueue()
|
|||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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';
|
|
||||||
delete rxBurst;
|
|
||||||
|
|
||||||
mDataSocket.write(burstString,gSlotLen+10);
|
|
||||||
}
|
}
|
||||||
|
return transceiver->mFIFOServiceLoop.stopThread();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Transceiver::driveTransmitFIFO()
|
void FIFOServiceLoopThread::runThread()
|
||||||
{
|
{
|
||||||
|
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
|
LOG(DEBUG) << "FIFOServiceLoopThread has finished operations";
|
||||||
pushed into the FIFO right NOW. If transmit queue does
|
}
|
||||||
not have a burst, stick in filler data.
|
|
||||||
*/
|
|
||||||
|
|
||||||
|
Thread::ReturnStatus ControlServiceLoopThread::shutdown()
|
||||||
RadioClock *radioClock = (mRadioInterface->getClock());
|
{
|
||||||
|
Transceiver *transceiver = (Transceiver *)mThreadData;
|
||||||
|
|
||||||
if (mOn) {
|
if (transceiver == NULL)
|
||||||
//radioClock->wait(); // wait until clock updates
|
// Nothing to do
|
||||||
LOG(DEBUG) << "radio clock " << radioClock->get();
|
return ALREADY_IDLE;
|
||||||
while (radioClock->get() + mTransmitLatency > mTransmitDeadlineClock) {
|
|
||||||
// if underrun, then we're not providing bursts to radio/USRP fast
|
transceiver->mControlServiceLoop.requestThreadStop();
|
||||||
// enough. Need to increase latency by one GSM frame.
|
// FIXME: We should use shutdown() here, but the socket should be
|
||||||
if (mRadioInterface->getBus() == RadioDevice::USB) {
|
// re-openned on the next start() then. Righ now the socket
|
||||||
if (mRadioInterface->isUnderrun()) {
|
// is created in the constructor and if we shutdown() it here,
|
||||||
// only update latency at the defined frame interval
|
// we'll get errors when we try to use it on the next start.
|
||||||
if (radioClock->get() > mLatencyUpdateTime + GSM::Time(USB_LATENCY_INTRVL)) {
|
// transceiver->mControlSocket.shutdown();
|
||||||
mTransmitLatency = mTransmitLatency + GSM::Time(1,0);
|
{
|
||||||
LOG(INFO) << "new latency: " << mTransmitLatency;
|
// mBasePort+1 is mControlSocket port
|
||||||
mLatencyUpdateTime = radioClock->get();
|
UDPSocket tmpSock(0, "127.0.0.1", transceiver->mBasePort+1);
|
||||||
}
|
tmpSock.write(NULL, 0);
|
||||||
}
|
|
||||||
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();
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
// FIXME -- This should not be a hard spin.
|
return transceiver->mControlServiceLoop.stopThread();
|
||||||
// But any delay here causes us to throw omni_thread_fatal.
|
|
||||||
//else radioClock->wait();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ControlServiceLoopThread::runThread()
|
||||||
|
|
||||||
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;
|
while (isThreadRunning()) {
|
||||||
|
|
||||||
mClockSocket.write(command,strlen(command)+1);
|
|
||||||
|
|
||||||
mLastClockUpdateTime = mTransmitDeadlineClock;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void *FIFOServiceLoopAdapter(Transceiver *transceiver)
|
|
||||||
{
|
|
||||||
transceiver->setPriority();
|
|
||||||
|
|
||||||
while (1) {
|
|
||||||
transceiver->driveReceiveFIFO();
|
|
||||||
transceiver->driveTransmitFIFO();
|
|
||||||
pthread_testcancel();
|
|
||||||
}
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
void *ControlServiceLoopAdapter(Transceiver *transceiver)
|
|
||||||
{
|
|
||||||
while (1) {
|
|
||||||
transceiver->driveControl();
|
transceiver->driveControl();
|
||||||
pthread_testcancel();
|
|
||||||
}
|
}
|
||||||
return NULL;
|
|
||||||
|
LOG(DEBUG) << "ControlServiceLoopThread has finished operations";
|
||||||
}
|
}
|
||||||
|
|
||||||
void *TransmitPriorityQueueServiceLoopAdapter(Transceiver *transceiver)
|
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,23 +156,19 @@ 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();
|
||||||
|
|
||||||
/** 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
|
||||||
@@ -32,31 +33,98 @@
|
|||||||
#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,
|
||||||
|
* which are captured in these empirically measured (using a loopback test)
|
||||||
|
* timing correction values.
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* USRP1 with timestamps is not supported by UHD.
|
||||||
|
*/
|
||||||
|
static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 3] = {
|
||||||
|
{ USRP1, 1, 0.0 },
|
||||||
|
{ 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 },
|
||||||
|
};
|
||||||
|
|
||||||
rx_smpl_offset - Timing correction in seconds between receive and
|
static double get_dev_offset(enum uhd_dev_type type, int sps)
|
||||||
transmit timestamps. This value corrects for delays on
|
{
|
||||||
on the RF side of the timestamping point of the device.
|
if (type == USRP1) {
|
||||||
This value is generally empirically measured.
|
LOG(ERR) << "Invalid device type";
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
smpl_buf_sz - The receive sample buffer size in bytes.
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
tx_ampl - Transmit amplitude must be between 0 and 1.0
|
LOG(ERR) << "Unsupported samples-per-symbols: " << sps;
|
||||||
*/
|
return 0.0;
|
||||||
const double master_clk_rt = 52e6;
|
}
|
||||||
const size_t smpl_buf_sz = (1 << 20);
|
|
||||||
const float tx_ampl = .3;
|
|
||||||
|
|
||||||
#ifdef RESAMPLE
|
/*
|
||||||
const double rx_smpl_offset = .00005;
|
* Select sample rate based on device type and requested samples-per-symbol.
|
||||||
#else
|
* The base rate is either GSM symbol rate, 270.833 kHz, or the minimum
|
||||||
const double rx_smpl_offset = .0000869;
|
* usable channel spacing of 400 kHz.
|
||||||
#endif
|
*/
|
||||||
|
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 +207,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 +225,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 +276,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 +286,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 +309,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 +322,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 +355,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 +397,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
|
||||||
usrp_dev->set_tx_rate(rate);
|
try {
|
||||||
usrp_dev->set_rx_rate(rate);
|
usrp_dev->set_tx_rate(rate);
|
||||||
actual_rt = usrp_dev->get_tx_rate();
|
usrp_dev->set_rx_rate(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";
|
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;
|
return tx_gain[chan];
|
||||||
}
|
}
|
||||||
|
|
||||||
double uhd_device::setRxGain(double db)
|
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 +533,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 +582,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 +591,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 +643,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 +682,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 +702,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 +715,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 +781,48 @@ 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];
|
|
||||||
|
|
||||||
if (skip_rx)
|
if (skip_rx) {
|
||||||
|
LOG(INFO) << "Skipping Rx";
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (chans != NUM_RX_CHANS) {
|
||||||
|
LOG(ERR) << "Number of requested channels does not match build";
|
||||||
|
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();
|
||||||
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,39 +835,45 @@ 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++) {
|
||||||
num_smpls,
|
rc = rx_smpl_buf[i]->write((short *) &bufs[i].front(),
|
||||||
metadata.time_spec);
|
num_smpls,
|
||||||
|
metadata.time_spec);
|
||||||
// Continue on local overrun, exit on other errors
|
|
||||||
if ((rc < 0)) {
|
// Continue on local overrun, exit on other errors
|
||||||
LOG(ERR) << rx_smpl_buf->str_code(rc);
|
if ((rc < 0)) {
|
||||||
LOG(ERR) << rx_smpl_buf->str_status();
|
LOG(ERR) << rx_smpl_buf[i]->str_code(rc);
|
||||||
if (rc != smpl_buf::ERROR_OVERFLOW)
|
LOG(ERR) << rx_smpl_buf[i]->str_status();
|
||||||
return 0;
|
if (rc != smpl_buf::ERROR_OVERFLOW)
|
||||||
|
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++) {
|
||||||
if ((rc < 0) || (rc != len)) {
|
rc = rx_smpl_buf[i]->read(buf[i], len, timestamp);
|
||||||
LOG(ERR) << rx_smpl_buf->str_code(rc);
|
if ((rc < 0) || (rc != len)) {
|
||||||
LOG(ERR) << rx_smpl_buf->str_status();
|
LOG(ERR) << rx_smpl_buf[i]->str_code(rc);
|
||||||
return 0;
|
LOG(ERR) << rx_smpl_buf[i]->str_status();
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
||||||
{
|
{
|
||||||
uhd::tx_metadata_t metadata;
|
uhd::tx_metadata_t metadata;
|
||||||
metadata.has_time_spec = true;
|
metadata.has_time_spec = true;
|
||||||
@@ -718,12 +906,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();
|
||||||
@@ -736,24 +923,33 @@ int uhd_device::writeSamples(short *buf, int len, bool *underrun,
|
|||||||
|
|
||||||
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 +1084,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 +1182,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("");
|
||||||
|
|
||||||
|
|||||||
120
Transceiver52M/multiTRX.cpp
Normal file
120
Transceiver52M/multiTRX.cpp
Normal file
@@ -0,0 +1,120 @@
|
|||||||
|
/*
|
||||||
|
* 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"
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
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...";
|
||||||
|
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.
|
||||||
@@ -27,23 +27,54 @@
|
|||||||
|
|
||||||
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 wReceiveOffset,
|
int wChanM,
|
||||||
int wRadioOversampling,
|
int wSPS,
|
||||||
int wTransceiverOversampling,
|
int wReceiveOffset,
|
||||||
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),
|
mUseCount(0),
|
||||||
samplesPerSymbol(wRadioOversampling), powerScaling(1.0),
|
mRadio(wRadio), receiveOffset(wReceiveOffset), samplesPerSymbol(wSPS),
|
||||||
loadTest(false)
|
powerScaling(1.0), 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,86 @@ 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...";
|
// Use count must not be negative
|
||||||
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
|
assert(mUseCount>=0);
|
||||||
(void*)this);
|
// Being on while mUseCount is 0 is a wrong condition
|
||||||
|
assert(!(mOn && mUseCount==0));
|
||||||
|
|
||||||
|
mUseCount++;
|
||||||
|
if (mOn || mUseCount>1)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mOn = true;
|
||||||
|
#ifdef USRP1
|
||||||
|
mAlignRadioServiceLoopThread = new Thread(32768);
|
||||||
|
mAlignRadioServiceLoopThread->start((void * (*)(void*))AlignRadioServiceLoopAdapter,
|
||||||
|
(void*)this);
|
||||||
|
#endif
|
||||||
writeTimestamp = mRadio->initialWriteTimestamp();
|
writeTimestamp = mRadio->initialWriteTimestamp();
|
||||||
readTimestamp = mRadio->initialReadTimestamp();
|
readTimestamp = mRadio->initialReadTimestamp();
|
||||||
|
for (int 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()
|
||||||
|
{
|
||||||
|
// Use count must not be negative or zero
|
||||||
|
assert(mUseCount>0);
|
||||||
|
// Must be on while stopping
|
||||||
|
assert(mOn);
|
||||||
|
|
||||||
|
mUseCount--;
|
||||||
|
if (mUseCount>0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mOn = false;
|
||||||
|
mRadio->stop();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
#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 +211,55 @@ 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;
|
||||||
|
|
||||||
if (!mOn) return;
|
if (!mOn)
|
||||||
|
return;
|
||||||
|
|
||||||
radioifyVector(radioBurst, sendBuffer + 2 * sendCursor, powerScaling, zeroBurst);
|
for (i = 0; i < mChanM; i++) {
|
||||||
|
radioifyVector(*radioBurst[i], sendBuffer[i] + 2 * sendCursor,
|
||||||
|
powerScaling, zeroBurst[i]);
|
||||||
|
}
|
||||||
|
|
||||||
sendCursor += radioBurst.size();
|
/*
|
||||||
|
* All bursts should be the same size since all transceivers are
|
||||||
|
* tied with a single clock in the radio interface.
|
||||||
|
*/
|
||||||
|
sendCursor += radioBurst[0]->size();
|
||||||
|
|
||||||
pushBuffer();
|
pushBuffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioInterface::driveReceiveRadio() {
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
if (!mOn) return;
|
void RadioInterface::loadVectors(unsigned tN, int samplesPerBurst,
|
||||||
|
int idx, GSM::Time rxClock)
|
||||||
|
{
|
||||||
|
int i;
|
||||||
|
|
||||||
if (mReceiveFIFO.size() > 8) return;
|
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()
|
||||||
|
{
|
||||||
|
if (!mOn)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (mReceiveFIFO[0].size() > 8)
|
||||||
|
return;
|
||||||
|
|
||||||
pullBuffer();
|
pullBuffer();
|
||||||
|
|
||||||
@@ -173,71 +269,98 @@ 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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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;
|
||||||
|
|
||||||
|
/* 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Send timestamped chunk to the device with arbitrary size */
|
||||||
|
void RadioInterface::pushBuffer()
|
||||||
|
{
|
||||||
|
if (sendCursor < INCHUNK)
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|||||||
@@ -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"
|
||||||
@@ -22,29 +23,31 @@
|
|||||||
#include "radioClock.h"
|
#include "radioClock.h"
|
||||||
|
|
||||||
/** samples per GSM symbol */
|
/** samples per GSM symbol */
|
||||||
#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
|
||||||
bool overrun; ///< indicates reads from USRP are too slow
|
bool overrun; ///< indicates reads from USRP are too slow
|
||||||
TIMESTAMP writeTimestamp; ///< sample timestamp of next packet written to USRP
|
TIMESTAMP writeTimestamp; ///< sample timestamp of next packet written to USRP
|
||||||
@@ -54,10 +57,9 @@ 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
|
||||||
|
int mUseCount; ///< Use counter
|
||||||
|
|
||||||
double powerScaling;
|
double powerScaling;
|
||||||
|
|
||||||
@@ -65,6 +67,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 +78,35 @@ 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 */
|
/** Increase usage counter and start the interface if not started yet */
|
||||||
void start();
|
bool start();
|
||||||
|
/** Decrease usage counter and stop the interface if no users left */
|
||||||
|
bool stop();
|
||||||
|
|
||||||
|
bool started() { return mOn; };
|
||||||
|
|
||||||
|
/** shutdown interface */
|
||||||
|
void close();
|
||||||
|
|
||||||
/** constructor */
|
/** constructor */
|
||||||
RadioInterface(RadioDevice* wRadio = NULL,
|
RadioInterface(RadioDevice* wRadio,
|
||||||
int receiveOffset = 3,
|
int wChanM = 1,
|
||||||
int wRadioOversampling = SAMPSPERSYM,
|
int wSPS = SAMPSPERSYM,
|
||||||
int wTransceiverOversampling = SAMPSPERSYM,
|
int receiveOffset = 3,
|
||||||
GSM::Time wStartTime = GSM::Time(0));
|
GSM::Time wStartTime = GSM::Time(0, 0));
|
||||||
|
|
||||||
/** destructor */
|
/** destructor */
|
||||||
~RadioInterface();
|
~RadioInterface();
|
||||||
@@ -98,38 +114,32 @@ public:
|
|||||||
void setSamplesPerSymbol(int wSamplesPerSymbol) {if (!mOn) samplesPerSymbol = wSamplesPerSymbol;}
|
void setSamplesPerSymbol(int wSamplesPerSymbol) {if (!mOn) samplesPerSymbol = wSamplesPerSymbol;}
|
||||||
|
|
||||||
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 +150,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
|
||||||
@@ -41,21 +41,6 @@ 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;
|
||||||
|
|||||||
@@ -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