mirror of
https://gitea.osmocom.org/cellular-infrastructure/osmo-trx.git
synced 2025-11-04 06:03:17 +00:00
Compare commits
32 Commits
kluchnikov
...
fairwaves/
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2518186b45 | ||
|
|
6512812e43 | ||
|
|
ded68da44f | ||
|
|
37bbfa2125 | ||
|
|
fdbf914584 | ||
|
|
bbef7e4d70 | ||
|
|
351fd76706 | ||
|
|
6a2bf0d87b | ||
|
|
2966048b07 | ||
|
|
f5fd578d60 | ||
|
|
57ef87d061 | ||
|
|
5721920a08 | ||
|
|
194a9b1982 | ||
|
|
1fe5282133 | ||
|
|
4438a9fd8f | ||
|
|
64ad712daa | ||
|
|
5c7c178369 | ||
|
|
90f7a01d1d | ||
|
|
e171425b99 | ||
|
|
4d029d8965 | ||
|
|
6613331459 | ||
|
|
577cd020c1 | ||
|
|
88bbf1aafd | ||
|
|
2cc2ddda41 | ||
|
|
d7610cf0b8 | ||
|
|
722d4f70a4 | ||
|
|
93b7f37309 | ||
|
|
4ad9ea69ab | ||
|
|
eb54bddf47 | ||
|
|
a4d1a41244 | ||
|
|
b999759175 | ||
|
|
1ae25561fa |
@@ -67,7 +67,8 @@ const char *levelNames[] = {
|
|||||||
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
|
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
|
||||||
};
|
};
|
||||||
int numLevels = 8;
|
int numLevels = 8;
|
||||||
bool gLogToConsole = 0;
|
bool gLogToConsole = true;
|
||||||
|
bool gLogToSyslog = false;
|
||||||
FILE *gLogToFile = NULL;
|
FILE *gLogToFile = NULL;
|
||||||
Mutex gLogToLock;
|
Mutex gLogToLock;
|
||||||
|
|
||||||
@@ -196,14 +197,16 @@ Log::~Log()
|
|||||||
if (sLoggerInited) addAlarm(mStream.str().c_str());
|
if (sLoggerInited) addAlarm(mStream.str().c_str());
|
||||||
cerr << mStream.str() << endl;
|
cerr << mStream.str() << endl;
|
||||||
}
|
}
|
||||||
// Current logging level was already checked by the macro.
|
// Current logging level was already checked by the macro. So just log.
|
||||||
// So just log.
|
// Log to syslog
|
||||||
syslog(mPriority, "%s", mStream.str().c_str());
|
if (gLogToSyslog) {
|
||||||
// pat added for easy debugging.
|
syslog(mPriority, "%s", mStream.str().c_str());
|
||||||
|
}
|
||||||
|
// Log to file and console
|
||||||
if (gLogToConsole||gLogToFile) {
|
if (gLogToConsole||gLogToFile) {
|
||||||
int mlen = mStream.str().size();
|
int mlen = mStream.str().size();
|
||||||
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
|
||||||
gLogToLock.lock();
|
ScopedLock lock(gLogToLock);
|
||||||
if (gLogToConsole) {
|
if (gLogToConsole) {
|
||||||
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
|
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
|
||||||
// so just use std::cout.
|
// so just use std::cout.
|
||||||
@@ -215,7 +218,6 @@ Log::~Log()
|
|||||||
if (neednl) {fputc('\n',gLogToFile);}
|
if (neednl) {fputc('\n',gLogToFile);}
|
||||||
fflush(gLogToFile);
|
fflush(gLogToFile);
|
||||||
}
|
}
|
||||||
gLogToLock.unlock();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -243,10 +245,9 @@ void gLogInit(const char* name, const char* level, int facility)
|
|||||||
gConfig.set("Log.Level",level);
|
gConfig.set("Log.Level",level);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pat added, tired of the syslog facility.
|
|
||||||
// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
|
// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
|
||||||
string str = gConfig.getStr("Log.File");
|
string str = gConfig.getStr("Log.File");
|
||||||
if (gLogToFile==0 && str.length() && 0==strncmp(gCmdName,"Open",4)) {
|
if (gLogToFile==NULL && str.length() && 0==strncmp(gCmdName,"Open",4)) {
|
||||||
const char *fn = str.c_str();
|
const char *fn = str.c_str();
|
||||||
if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
|
if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
|
||||||
gLogToFile = fopen(fn,"w"); // New log file each time we start.
|
gLogToFile = fopen(fn,"w"); // New log file each time we start.
|
||||||
|
|||||||
@@ -116,7 +116,8 @@ class Log {
|
|||||||
|
|
||||||
std::ostringstream& get();
|
std::ostringstream& get();
|
||||||
};
|
};
|
||||||
extern bool gLogToConsole; // Pat added for easy debugging.
|
extern bool gLogToConsole; // Output log messages to stdout
|
||||||
|
extern bool gLogToSyslog; // Output log messages to syslog
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -172,8 +172,15 @@ class Thread {
|
|||||||
void start(void *(*task)(void*), void *arg);
|
void start(void *(*task)(void*), void *arg);
|
||||||
|
|
||||||
/** Join a thread that will stop on its own. */
|
/** Join a thread that will stop on its own. */
|
||||||
void join() { int s = pthread_join(mThread,NULL); assert(!s); mThread = 0; }
|
void join() {
|
||||||
|
if (mThread) {
|
||||||
|
int s = pthread_join(mThread, NULL);
|
||||||
|
assert(!s);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Send cancelation to thread */
|
||||||
|
void cancel() { pthread_cancel(mThread); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
#include <iomanip> // std::setprecision
|
||||||
#include "Transceiver.h"
|
#include "Transceiver.h"
|
||||||
#include <Logger.h>
|
#include <Logger.h>
|
||||||
|
|
||||||
@@ -43,7 +44,7 @@ using namespace GSM;
|
|||||||
#define NOISE_CNT 20
|
#define NOISE_CNT 20
|
||||||
|
|
||||||
TransceiverState::TransceiverState()
|
TransceiverState::TransceiverState()
|
||||||
: mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT)
|
: mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
|
||||||
{
|
{
|
||||||
for (int i = 0; i < 8; i++) {
|
for (int i = 0; i < 8; i++) {
|
||||||
chanType[i] = Transceiver::NONE;
|
chanType[i] = Transceiver::NONE;
|
||||||
@@ -69,61 +70,122 @@ TransceiverState::~TransceiverState()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TransceiverState::init(size_t slot, signalVector *burst, bool fill)
|
static BitVector *genRandNormalBurst(size_t tsc)
|
||||||
{
|
{
|
||||||
signalVector *filler;
|
if (tsc > 7)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
for (int i = 0; i < 102; i++) {
|
BitVector *bits = new BitVector(148);
|
||||||
if (fill)
|
|
||||||
filler = new signalVector(*burst);
|
|
||||||
else
|
|
||||||
filler = new signalVector(burst->size());
|
|
||||||
|
|
||||||
fillerTable[i][slot] = filler;
|
size_t i = 0;
|
||||||
|
|
||||||
|
/* Tail bits */
|
||||||
|
for (; i < 4; i++)
|
||||||
|
(*bits)[i] = 0;
|
||||||
|
|
||||||
|
/* Random bits */
|
||||||
|
for (; i < 61; i++)
|
||||||
|
(*bits)[i] = rand() % 2;
|
||||||
|
|
||||||
|
/* Training sequence */
|
||||||
|
for (int j = 0; i < 87; i++, j++)
|
||||||
|
(*bits)[i] = GSM::gTrainingSequence[tsc][j];
|
||||||
|
|
||||||
|
/* Random bits */
|
||||||
|
for (; i < 144; i++)
|
||||||
|
(*bits)[i] = rand() % 2;
|
||||||
|
|
||||||
|
/* Tail bits */
|
||||||
|
for (; i < 148; i++)
|
||||||
|
(*bits)[i] = 0;
|
||||||
|
|
||||||
|
return bits;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc)
|
||||||
|
{
|
||||||
|
BitVector *bits;
|
||||||
|
signalVector *burst;
|
||||||
|
|
||||||
|
if ((sps != 1) && (sps != 4))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
for (size_t n = 0; n < 8; n++) {
|
||||||
|
size_t guard = 8 + !(n % 4);
|
||||||
|
size_t len = sps == 4 ? 625 : 148 + guard;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < 102; i++) {
|
||||||
|
switch (filler) {
|
||||||
|
case Transceiver::FILLER_DUMMY:
|
||||||
|
burst = modulateBurst(gDummyBurst, guard, sps);
|
||||||
|
break;
|
||||||
|
case Transceiver::FILLER_RAND:
|
||||||
|
bits = genRandNormalBurst(rtsc);
|
||||||
|
burst = modulateBurst(*bits, guard, sps);
|
||||||
|
delete bits;
|
||||||
|
break;
|
||||||
|
case Transceiver::FILLER_ZERO:
|
||||||
|
default:
|
||||||
|
burst = new signalVector(len);
|
||||||
|
}
|
||||||
|
|
||||||
|
scaleVector(*burst, scale);
|
||||||
|
fillerTable[i][n] = burst;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filler == Transceiver::FILLER_RAND)
|
||||||
|
chanType[n] = Transceiver::TSC;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Transceiver::Transceiver(int wBasePort,
|
Transceiver::Transceiver(int wBasePort,
|
||||||
const char *TRXAddress,
|
const char *wTRXAddress,
|
||||||
size_t wSPS, size_t wChans,
|
size_t wSPS, size_t wChans,
|
||||||
GSM::Time wTransmitLatency,
|
GSM::Time wTransmitLatency,
|
||||||
RadioInterface *wRadioInterface)
|
RadioInterface *wRadioInterface,
|
||||||
: mBasePort(wBasePort), mAddr(TRXAddress),
|
double wRssiOffset)
|
||||||
mTransmitLatency(wTransmitLatency), mClockSocket(NULL),
|
: mBasePort(wBasePort), mAddr(wTRXAddress),
|
||||||
mRadioInterface(wRadioInterface), mSPSTx(wSPS), mSPSRx(1), mChans(wChans),
|
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
|
||||||
mOn(false), mTxFreq(0.0), mRxFreq(0.0), mPower(-10), mMaxExpectedDelay(0)
|
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
|
||||||
|
rssiOffset(wRssiOffset),
|
||||||
|
mSPSTx(wSPS), mSPSRx(1), mChans(wChans), mOn(false),
|
||||||
|
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelay(0)
|
||||||
{
|
{
|
||||||
GSM::Time startTime(random() % gHyperframe,0);
|
|
||||||
|
|
||||||
mRxLowerLoopThread = new Thread(32768);
|
|
||||||
mTxLowerLoopThread = new Thread(32768);
|
|
||||||
|
|
||||||
mTransmitDeadlineClock = startTime;
|
|
||||||
mLastClockUpdateTime = startTime;
|
|
||||||
mLatencyUpdateTime = startTime;
|
|
||||||
mRadioInterface->getClock()->set(startTime);
|
|
||||||
|
|
||||||
txFullScale = mRadioInterface->fullScaleInputValue();
|
txFullScale = mRadioInterface->fullScaleInputValue();
|
||||||
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
rxFullScale = mRadioInterface->fullScaleOutputValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
Transceiver::~Transceiver()
|
Transceiver::~Transceiver()
|
||||||
{
|
{
|
||||||
|
stop();
|
||||||
|
|
||||||
sigProcLibDestroy();
|
sigProcLibDestroy();
|
||||||
|
|
||||||
delete mClockSocket;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < mChans; i++) {
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
|
mControlServiceLoopThreads[i]->cancel();
|
||||||
|
mControlServiceLoopThreads[i]->join();
|
||||||
|
delete mControlServiceLoopThreads[i];
|
||||||
|
|
||||||
mTxPriorityQueues[i].clear();
|
mTxPriorityQueues[i].clear();
|
||||||
delete mCtrlSockets[i];
|
delete mCtrlSockets[i];
|
||||||
delete mDataSockets[i];
|
delete mDataSockets[i];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Transceiver::init(bool filler)
|
/*
|
||||||
|
* Initialize transceiver
|
||||||
|
*
|
||||||
|
* Start or restart the control loop. Any further control is handled through the
|
||||||
|
* socket API. Randomize the central radio clock set the downlink burst
|
||||||
|
* counters. Note that the clock will not update until the radio starts, but we
|
||||||
|
* are still expected to report clock indications through control channel
|
||||||
|
* activity.
|
||||||
|
*/
|
||||||
|
bool Transceiver::init(int filler, size_t rtsc)
|
||||||
{
|
{
|
||||||
int d_srcport, d_dstport, c_srcport, c_dstport;
|
int d_srcport, d_dstport, c_srcport, c_dstport;
|
||||||
signalVector *burst;
|
|
||||||
|
|
||||||
if (!mChans) {
|
if (!mChans) {
|
||||||
LOG(ALERT) << "No channels assigned";
|
LOG(ALERT) << "No channels assigned";
|
||||||
@@ -137,7 +199,6 @@ bool Transceiver::init(bool filler)
|
|||||||
|
|
||||||
mDataSockets.resize(mChans);
|
mDataSockets.resize(mChans);
|
||||||
mCtrlSockets.resize(mChans);
|
mCtrlSockets.resize(mChans);
|
||||||
|
|
||||||
mControlServiceLoopThreads.resize(mChans);
|
mControlServiceLoopThreads.resize(mChans);
|
||||||
mTxPriorityQueueServiceLoopThreads.resize(mChans);
|
mTxPriorityQueueServiceLoopThreads.resize(mChans);
|
||||||
mRxServiceLoopThreads.resize(mChans);
|
mRxServiceLoopThreads.resize(mChans);
|
||||||
@@ -147,11 +208,10 @@ bool Transceiver::init(bool filler)
|
|||||||
mStates.resize(mChans);
|
mStates.resize(mChans);
|
||||||
|
|
||||||
/* Filler table retransmissions - support only on channel 0 */
|
/* Filler table retransmissions - support only on channel 0 */
|
||||||
if (filler)
|
if (filler == FILLER_DUMMY)
|
||||||
mStates[0].mRetrans = true;
|
mStates[0].mRetrans = true;
|
||||||
|
|
||||||
mClockSocket = new UDPSocket(mBasePort, mAddr.c_str(), mBasePort + 100);
|
/* Setup sockets */
|
||||||
|
|
||||||
for (size_t i = 0; i < mChans; i++) {
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
c_srcport = mBasePort + 2 * i + 1;
|
c_srcport = mBasePort + 2 * i + 1;
|
||||||
c_dstport = mBasePort + 2 * i + 101;
|
c_dstport = mBasePort + 2 * i + 101;
|
||||||
@@ -162,22 +222,129 @@ bool Transceiver::init(bool filler)
|
|||||||
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
|
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < mChans; i++) {
|
/* Randomize the central clock */
|
||||||
mControlServiceLoopThreads[i] = new Thread(32768);
|
GSM::Time startTime(random() % gHyperframe, 0);
|
||||||
mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
|
mRadioInterface->getClock()->set(startTime);
|
||||||
mRxServiceLoopThreads[i] = new Thread(32768);
|
mTransmitDeadlineClock = startTime;
|
||||||
|
mLastClockUpdateTime = startTime;
|
||||||
|
mLatencyUpdateTime = startTime;
|
||||||
|
|
||||||
for (size_t n = 0; n < 8; n++) {
|
/* Start control threads */
|
||||||
burst = modulateBurst(gDummyBurst, 8 + (n % 4 == 0), mSPSTx);
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
scaleVector(*burst, txFullScale);
|
TransceiverChannel *chan = new TransceiverChannel(this, i);
|
||||||
mStates[i].init(n, burst, filler && !i);
|
mControlServiceLoopThreads[i] = new Thread(32768);
|
||||||
delete burst;
|
mControlServiceLoopThreads[i]->start((void * (*)(void*))
|
||||||
}
|
ControlServiceLoopAdapter, (void*) chan);
|
||||||
|
|
||||||
|
if (i && filler == FILLER_DUMMY)
|
||||||
|
filler = FILLER_ZERO;
|
||||||
|
|
||||||
|
mStates[i].init(filler, mSPSTx, txFullScale, rtsc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Start the transceiver
|
||||||
|
*
|
||||||
|
* Submit command(s) to the radio device to commence streaming samples and
|
||||||
|
* launch threads to handle sample I/O. Re-synchronize the transmit burst
|
||||||
|
* counters to the central radio clock here as well.
|
||||||
|
*/
|
||||||
|
bool Transceiver::start()
|
||||||
|
{
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
|
||||||
|
if (mOn) {
|
||||||
|
LOG(ERR) << "Transceiver already running";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(NOTICE) << "Starting the transceiver";
|
||||||
|
|
||||||
|
GSM::Time time = mRadioInterface->getClock()->get();
|
||||||
|
mTransmitDeadlineClock = time;
|
||||||
|
mLastClockUpdateTime = time;
|
||||||
|
mLatencyUpdateTime = time;
|
||||||
|
|
||||||
|
if (!mRadioInterface->start()) {
|
||||||
|
LOG(ALERT) << "Device failed to start";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Device is running - launch I/O threads */
|
||||||
|
mRxLowerLoopThread = new Thread(32768);
|
||||||
|
mTxLowerLoopThread = new Thread(32768);
|
||||||
|
mTxLowerLoopThread->start((void * (*)(void*))
|
||||||
|
TxLowerLoopAdapter,(void*) this);
|
||||||
|
mRxLowerLoopThread->start((void * (*)(void*))
|
||||||
|
RxLowerLoopAdapter,(void*) this);
|
||||||
|
|
||||||
|
/* Launch uplink and downlink burst processing threads */
|
||||||
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
|
TransceiverChannel *chan = new TransceiverChannel(this, i);
|
||||||
|
mRxServiceLoopThreads[i] = new Thread(32768);
|
||||||
|
mRxServiceLoopThreads[i]->start((void * (*)(void*))
|
||||||
|
RxUpperLoopAdapter, (void*) chan);
|
||||||
|
|
||||||
|
chan = new TransceiverChannel(this, i);
|
||||||
|
mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
|
||||||
|
mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
|
||||||
|
TxUpperLoopAdapter, (void*) chan);
|
||||||
|
}
|
||||||
|
|
||||||
|
writeClockInterface();
|
||||||
|
mOn = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop the transceiver
|
||||||
|
*
|
||||||
|
* Perform stopping by disabling receive streaming and issuing cancellation
|
||||||
|
* requests to running threads. Most threads will timeout and terminate once
|
||||||
|
* device is disabled, but the transmit loop may block waiting on the central
|
||||||
|
* UMTS clock. Explicitly signal the clock to make sure that the transmit loop
|
||||||
|
* makes it to the thread cancellation point.
|
||||||
|
*/
|
||||||
|
void Transceiver::stop()
|
||||||
|
{
|
||||||
|
ScopedLock lock(mLock);
|
||||||
|
|
||||||
|
if (!mOn)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOG(NOTICE) << "Stopping the transceiver";
|
||||||
|
mTxLowerLoopThread->cancel();
|
||||||
|
mRxLowerLoopThread->cancel();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
|
mRxServiceLoopThreads[i]->cancel();
|
||||||
|
mTxPriorityQueueServiceLoopThreads[i]->cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG(INFO) << "Stopping the device";
|
||||||
|
mRadioInterface->stop();
|
||||||
|
|
||||||
|
for (size_t i = 0; i < mChans; i++) {
|
||||||
|
mRxServiceLoopThreads[i]->join();
|
||||||
|
mTxPriorityQueueServiceLoopThreads[i]->join();
|
||||||
|
delete mRxServiceLoopThreads[i];
|
||||||
|
delete mTxPriorityQueueServiceLoopThreads[i];
|
||||||
|
|
||||||
|
mTxPriorityQueues[i].clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
mTxLowerLoopThread->join();
|
||||||
|
mRxLowerLoopThread->join();
|
||||||
|
delete mTxLowerLoopThread;
|
||||||
|
delete mRxLowerLoopThread;
|
||||||
|
|
||||||
|
mOn = false;
|
||||||
|
LOG(NOTICE) << "Transceiver stopped";
|
||||||
|
}
|
||||||
|
|
||||||
void Transceiver::addRadioVector(size_t chan, BitVector &bits,
|
void Transceiver::addRadioVector(size_t chan, BitVector &bits,
|
||||||
int RSSI, GSM::Time &wTime)
|
int RSSI, GSM::Time &wTime)
|
||||||
{
|
{
|
||||||
@@ -367,9 +534,9 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
|
|||||||
* Detect RACH synchronization sequence within a burst. No equalization
|
* Detect RACH synchronization sequence within a burst. No equalization
|
||||||
* is used or available on the RACH channel.
|
* is used or available on the RACH channel.
|
||||||
*/
|
*/
|
||||||
bool Transceiver::detectRACH(TransceiverState *state,
|
int Transceiver::detectRACH(TransceiverState *state,
|
||||||
signalVector &burst,
|
signalVector &burst,
|
||||||
complex &, float &toa)
|
complex &, float &toa)
|
||||||
{
|
{
|
||||||
float threshold = 6.0;
|
float threshold = 6.0;
|
||||||
|
|
||||||
@@ -381,9 +548,10 @@ bool Transceiver::detectRACH(TransceiverState *state,
|
|||||||
* state information and channel estimate if necessary. Equalization
|
* state information and channel estimate if necessary. Equalization
|
||||||
* is currently disabled.
|
* is currently disabled.
|
||||||
*/
|
*/
|
||||||
bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
|
int Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
|
||||||
complex &, float &toa, GSM::Time &time)
|
complex &, float &toa, GSM::Time &time)
|
||||||
{
|
{
|
||||||
|
int success;
|
||||||
int tn = time.TN();
|
int tn = time.TN();
|
||||||
float chanOffset, threshold = 5.0;
|
float chanOffset, threshold = 5.0;
|
||||||
bool noise, needDFE = false, estimateChan = false;
|
bool noise, needDFE = false, estimateChan = false;
|
||||||
@@ -401,10 +569,11 @@ bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Detect normal burst midambles */
|
/* Detect normal burst midambles */
|
||||||
if (!analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, &,
|
success = analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, &,
|
||||||
&toa, mMaxExpectedDelay, estimateChan,
|
&toa, mMaxExpectedDelay, estimateChan,
|
||||||
&chanResp, &chanOffset)) {
|
&chanResp, &chanOffset);
|
||||||
return false;
|
if (success <= 0) {
|
||||||
|
return success;
|
||||||
}
|
}
|
||||||
|
|
||||||
noise = state->mNoiseLev;
|
noise = state->mNoiseLev;
|
||||||
@@ -424,7 +593,7 @@ bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
|
|||||||
state->chanEstimateTime[tn] = time;
|
state->chanEstimateTime[tn] = time;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@@ -451,10 +620,12 @@ SoftVector *Transceiver::demodulate(TransceiverState *state,
|
|||||||
* Pull bursts from the FIFO and handle according to the slot
|
* Pull bursts from the FIFO and handle according to the slot
|
||||||
* and burst correlation type. Equalzation is currently disabled.
|
* and burst correlation type. Equalzation is currently disabled.
|
||||||
*/
|
*/
|
||||||
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI,
|
||||||
int &timingOffset, size_t chan)
|
double &timingOffset, double &noise,
|
||||||
|
size_t chan)
|
||||||
{
|
{
|
||||||
bool success, equalize = false;
|
int success;
|
||||||
|
bool equalize = false;
|
||||||
complex amp;
|
complex amp;
|
||||||
float toa, pow, max = -1.0, avg = 0.0;
|
float toa, pow, max = -1.0, avg = 0.0;
|
||||||
int max_i = -1;
|
int max_i = -1;
|
||||||
@@ -503,8 +674,16 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
|||||||
else
|
else
|
||||||
success = detectRACH(state, *burst, amp, toa);
|
success = detectRACH(state, *burst, amp, toa);
|
||||||
|
|
||||||
if (!success) {
|
/* Update noise average if no bust detected or alert on error */
|
||||||
state->mNoises.insert(avg);
|
if (success <= 0) {
|
||||||
|
if (success == SIGERR_NONE) {
|
||||||
|
state->mNoises.insert(avg);
|
||||||
|
} else if (success == -SIGERR_CLIP) {
|
||||||
|
LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
|
||||||
|
} else {
|
||||||
|
LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
|
||||||
|
}
|
||||||
|
|
||||||
delete radio_burst;
|
delete radio_burst;
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
@@ -517,25 +696,15 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
|
|||||||
bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
|
bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
|
||||||
|
|
||||||
wTime = time;
|
wTime = time;
|
||||||
RSSI = (int) floor(20.0 * log10(rxFullScale / avg));
|
RSSI = 20.0 * log10(rxFullScale / avg);
|
||||||
timingOffset = (int) round(toa * 256.0 / mSPSRx);
|
timingOffset = toa / mSPSRx;
|
||||||
|
noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
|
||||||
|
|
||||||
delete radio_burst;
|
delete radio_burst;
|
||||||
|
|
||||||
return bits;
|
return bits;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Transceiver::start()
|
|
||||||
{
|
|
||||||
TransceiverChannel *chan;
|
|
||||||
|
|
||||||
for (size_t i = 0; i < mControlServiceLoopThreads.size(); i++) {
|
|
||||||
chan = new TransceiverChannel(this, i);
|
|
||||||
mControlServiceLoopThreads[i]->start((void * (*)(void*))
|
|
||||||
ControlServiceLoopAdapter, (void*) chan);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Transceiver::reset()
|
void Transceiver::reset()
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
|
for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
|
||||||
@@ -574,40 +743,14 @@ void Transceiver::driveControl(size_t chan)
|
|||||||
LOG(INFO) << "command is " << buffer;
|
LOG(INFO) << "command is " << buffer;
|
||||||
|
|
||||||
if (strcmp(command,"POWEROFF")==0) {
|
if (strcmp(command,"POWEROFF")==0) {
|
||||||
// turn off transmitter/demod
|
stop();
|
||||||
sprintf(response,"RSP POWEROFF 0");
|
sprintf(response,"RSP POWEROFF 0");
|
||||||
}
|
}
|
||||||
else if (strcmp(command,"POWERON")==0) {
|
else if (strcmp(command,"POWERON")==0) {
|
||||||
// turn on transmitter/demod
|
if (!start())
|
||||||
if (!mTxFreq || !mRxFreq)
|
|
||||||
sprintf(response,"RSP POWERON 1");
|
sprintf(response,"RSP POWERON 1");
|
||||||
else {
|
else
|
||||||
sprintf(response,"RSP POWERON 0");
|
sprintf(response,"RSP POWERON 0");
|
||||||
if (!chan && !mOn) {
|
|
||||||
// Prepare for thread start
|
|
||||||
mPower = -20;
|
|
||||||
mRadioInterface->start();
|
|
||||||
|
|
||||||
// Start radio interface threads.
|
|
||||||
mTxLowerLoopThread->start((void * (*)(void*))
|
|
||||||
TxLowerLoopAdapter,(void*) this);
|
|
||||||
mRxLowerLoopThread->start((void * (*)(void*))
|
|
||||||
RxLowerLoopAdapter,(void*) this);
|
|
||||||
|
|
||||||
for (size_t i = 0; i < mChans; i++) {
|
|
||||||
TransceiverChannel *chan = new TransceiverChannel(this, i);
|
|
||||||
mRxServiceLoopThreads[i]->start((void * (*)(void*))
|
|
||||||
RxUpperLoopAdapter, (void*) chan);
|
|
||||||
|
|
||||||
chan = new TransceiverChannel(this, i);
|
|
||||||
mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
|
|
||||||
TxUpperLoopAdapter, (void*) chan);
|
|
||||||
}
|
|
||||||
|
|
||||||
writeClockInterface();
|
|
||||||
mOn = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (strcmp(command,"SETMAXDLY")==0) {
|
else if (strcmp(command,"SETMAXDLY")==0) {
|
||||||
//set expected maximum time-of-arrival
|
//set expected maximum time-of-arrival
|
||||||
@@ -634,28 +777,19 @@ void Transceiver::driveControl(size_t chan)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!strcmp(command, "SETPOWER")) {
|
else if (!strcmp(command, "SETPOWER")) {
|
||||||
// set output power in dB
|
int power;
|
||||||
int dbPwr;
|
sscanf(buffer, "%3s %s %d", cmdcheck, command, &power);
|
||||||
sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbPwr);
|
power = mRadioInterface->setPowerAttenuation(power, chan);
|
||||||
if (!mOn)
|
mStates[chan].mPower = power;
|
||||||
sprintf(response, "RSP SETPOWER 1 %d", dbPwr);
|
sprintf(response, "RSP SETPOWER 0 %d", power);
|
||||||
else {
|
|
||||||
mPower = dbPwr;
|
|
||||||
mRadioInterface->setPowerAttenuation(mPower, chan);
|
|
||||||
sprintf(response, "RSP SETPOWER 0 %d", dbPwr);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (!strcmp(command,"ADJPOWER")) {
|
else if (!strcmp(command,"ADJPOWER")) {
|
||||||
// adjust power in dB steps
|
int power, step;
|
||||||
int dbStep;
|
sscanf(buffer, "%3s %s %d", cmdcheck, command, &step);
|
||||||
sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbStep);
|
power = mStates[chan].mPower + step;
|
||||||
if (!mOn)
|
power = mRadioInterface->setPowerAttenuation(power, chan);
|
||||||
sprintf(response, "RSP ADJPOWER 1 %d", mPower);
|
mStates[chan].mPower = power;
|
||||||
else {
|
sprintf(response, "RSP ADJPOWER 0 %d", power);
|
||||||
mPower += dbStep;
|
|
||||||
mRadioInterface->setPowerAttenuation(mPower, chan);
|
|
||||||
sprintf(response, "RSP ADJPOWER 0 %d", mPower);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else if (strcmp(command,"RXTUNE")==0) {
|
else if (strcmp(command,"RXTUNE")==0) {
|
||||||
// tune receiver
|
// tune receiver
|
||||||
@@ -685,7 +819,7 @@ void Transceiver::driveControl(size_t chan)
|
|||||||
// set TSC
|
// set TSC
|
||||||
unsigned TSC;
|
unsigned 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 if (chan && (TSC != mTSC))
|
else if (chan && (TSC != mTSC))
|
||||||
sprintf(response, "RSP SETTSC 1 %d", TSC);
|
sprintf(response, "RSP SETTSC 1 %d", TSC);
|
||||||
@@ -696,7 +830,7 @@ void Transceiver::driveControl(size_t chan)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
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);
|
||||||
@@ -712,6 +846,7 @@ void Transceiver::driveControl(size_t chan)
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
LOG(WARNING) << "bogus command " << command << " on control interface.";
|
LOG(WARNING) << "bogus command " << command << " on control interface.";
|
||||||
|
sprintf(response,"RSP ERR 1");
|
||||||
}
|
}
|
||||||
|
|
||||||
mCtrlSockets[chan]->write(response, strlen(response) + 1);
|
mCtrlSockets[chan]->write(response, strlen(response) + 1);
|
||||||
@@ -734,15 +869,6 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
|
|||||||
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]);
|
||||||
|
|
||||||
// periodically update GSM core clock
|
|
||||||
LOG(DEBUG) << "mTransmitDeadlineClock " << mTransmitDeadlineClock
|
|
||||||
<< " mLastClockUpdateTime " << mLastClockUpdateTime;
|
|
||||||
|
|
||||||
if (!chan) {
|
|
||||||
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
|
|
||||||
writeClockInterface();
|
|
||||||
}
|
|
||||||
|
|
||||||
LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
|
LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
|
||||||
|
|
||||||
int RSSI = (int) buffer[5];
|
int RSSI = (int) buffer[5];
|
||||||
@@ -763,34 +889,44 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
|
|||||||
|
|
||||||
void Transceiver::driveReceiveRadio()
|
void Transceiver::driveReceiveRadio()
|
||||||
{
|
{
|
||||||
if (!mRadioInterface->driveReceiveRadio())
|
if (!mRadioInterface->driveReceiveRadio()) {
|
||||||
usleep(100000);
|
usleep(100000);
|
||||||
|
} else {
|
||||||
|
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
|
||||||
|
writeClockInterface();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Transceiver::driveReceiveFIFO(size_t chan)
|
void Transceiver::driveReceiveFIFO(size_t chan)
|
||||||
{
|
{
|
||||||
SoftVector *rxBurst = NULL;
|
SoftVector *rxBurst = NULL;
|
||||||
int RSSI;
|
double RSSI; // in dBFS
|
||||||
int TOA; // in 1/256 of a symbol
|
double dBm; // in dBm
|
||||||
|
double TOA; // in symbols
|
||||||
|
int TOAint; // in 1/256 symbols
|
||||||
|
double noise; // noise level in dBFS
|
||||||
GSM::Time burstTime;
|
GSM::Time burstTime;
|
||||||
|
|
||||||
rxBurst = pullRadioVector(burstTime, RSSI, TOA, chan);
|
rxBurst = pullRadioVector(burstTime, RSSI, TOA, noise, chan);
|
||||||
|
|
||||||
if (rxBurst) {
|
if (rxBurst) {
|
||||||
|
dBm = RSSI+rssiOffset;
|
||||||
|
TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
|
||||||
|
|
||||||
|
LOG(DEBUG) << std::fixed << std::right
|
||||||
|
<< " time: " << burstTime
|
||||||
|
<< " RSSI: " << std::setw(5) << std::setprecision(1) << RSSI << "dBFS/" << std::setw(6) << -dBm << "dBm"
|
||||||
|
<< " noise: " << std::setw(5) << std::setprecision(1) << noise << "dBFS/" << std::setw(6) << -(noise+rssiOffset) << "dBm"
|
||||||
|
<< " TOA: " << std::setw(5) << std::setprecision(2) << TOA
|
||||||
|
<< " bits: " << *rxBurst;
|
||||||
|
|
||||||
LOG(DEBUG) << "burst parameters: "
|
|
||||||
<< " time: " << burstTime
|
|
||||||
<< " RSSI: " << RSSI
|
|
||||||
<< " TOA: " << TOA
|
|
||||||
<< " bits: " << *rxBurst;
|
|
||||||
|
|
||||||
char burstString[gSlotLen+10];
|
char burstString[gSlotLen+10];
|
||||||
burstString[0] = burstTime.TN();
|
burstString[0] = burstTime.TN();
|
||||||
for (int i = 0; i < 4; i++)
|
for (int i = 0; i < 4; i++)
|
||||||
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
|
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
|
||||||
burstString[5] = RSSI;
|
burstString[5] = (int)dBm;
|
||||||
burstString[6] = (TOA >> 8) & 0x0ff;
|
burstString[6] = (TOAint >> 8) & 0x0ff;
|
||||||
burstString[7] = TOA & 0x0ff;
|
burstString[7] = TOAint & 0x0ff;
|
||||||
SoftVector::iterator burstItr = rxBurst->begin();
|
SoftVector::iterator burstItr = rxBurst->begin();
|
||||||
|
|
||||||
for (unsigned int i = 0; i < gSlotLen; i++) {
|
for (unsigned int i = 0; i < gSlotLen; i++) {
|
||||||
@@ -865,7 +1001,7 @@ void Transceiver::writeClockInterface()
|
|||||||
|
|
||||||
LOG(INFO) << "ClockInterface: sending " << command;
|
LOG(INFO) << "ClockInterface: sending " << command;
|
||||||
|
|
||||||
mClockSocket->write(command, strlen(command) + 1);
|
mClockSocket.write(command, strlen(command) + 1);
|
||||||
|
|
||||||
mLastClockUpdateTime = mTransmitDeadlineClock;
|
mLastClockUpdateTime = mTransmitDeadlineClock;
|
||||||
|
|
||||||
@@ -933,15 +1069,7 @@ void *TxUpperLoopAdapter(TransceiverChannel *chan)
|
|||||||
trx->setPriority(0.40);
|
trx->setPriority(0.40);
|
||||||
|
|
||||||
while (1) {
|
while (1) {
|
||||||
bool stale = false;
|
trx->driveTxPriorityQueue(num);
|
||||||
// Flush the UDP packets until a successful transfer.
|
|
||||||
while (!trx->driveTxPriorityQueue(num)) {
|
|
||||||
stale = true;
|
|
||||||
}
|
|
||||||
if (!num && stale) {
|
|
||||||
// If a packet was stale, remind the GSM stack of the clock.
|
|
||||||
trx->writeClockInterface();
|
|
||||||
}
|
|
||||||
pthread_testcancel();
|
pthread_testcancel();
|
||||||
}
|
}
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ struct TransceiverState {
|
|||||||
~TransceiverState();
|
~TransceiverState();
|
||||||
|
|
||||||
/* Initialize a multiframe slot in the filler table */
|
/* Initialize a multiframe slot in the filler table */
|
||||||
void init(size_t slot, signalVector *burst, bool fill);
|
bool init(int filler, size_t sps, float scale, size_t rtsc);
|
||||||
|
|
||||||
int chanType[8];
|
int chanType[8];
|
||||||
|
|
||||||
@@ -81,98 +81,14 @@ struct TransceiverState {
|
|||||||
/* Received noise energy levels */
|
/* Received noise energy levels */
|
||||||
float mNoiseLev;
|
float mNoiseLev;
|
||||||
noiseVector mNoises;
|
noiseVector mNoises;
|
||||||
|
|
||||||
|
/* Shadowed downlink attenuation */
|
||||||
|
int mPower;
|
||||||
};
|
};
|
||||||
|
|
||||||
/** The Transceiver class, responsible for physical layer of basestation */
|
/** The Transceiver class, responsible for physical layer of basestation */
|
||||||
class Transceiver {
|
class Transceiver {
|
||||||
private:
|
|
||||||
int mBasePort;
|
|
||||||
std::string mAddr;
|
|
||||||
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
|
||||||
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
|
||||||
|
|
||||||
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
|
||||||
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
|
||||||
UDPSocket *mClockSocket; ///< socket for writing clock updates to GSM core
|
|
||||||
|
|
||||||
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
|
|
||||||
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
|
||||||
|
|
||||||
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
|
|
||||||
Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO
|
|
||||||
Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO
|
|
||||||
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
|
|
||||||
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
|
|
||||||
|
|
||||||
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
|
||||||
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
|
||||||
|
|
||||||
RadioInterface *mRadioInterface; ///< associated radioInterface object
|
|
||||||
double txFullScale; ///< full scale input 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;
|
|
||||||
|
|
||||||
/** modulate and add a burst to the transmit queue */
|
|
||||||
void addRadioVector(size_t chan, BitVector &bits,
|
|
||||||
int RSSI, GSM::Time &wTime);
|
|
||||||
|
|
||||||
/** Update filler table */
|
|
||||||
void updateFillerTable(size_t chan, radioVector *burst);
|
|
||||||
|
|
||||||
/** 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, size_t chan = 0);
|
|
||||||
|
|
||||||
/** Set modulus for specific timeslot */
|
|
||||||
void setModulus(size_t timeslot, size_t chan);
|
|
||||||
|
|
||||||
/** return the expected burst type for the specified timestamp */
|
|
||||||
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
|
|
||||||
|
|
||||||
/** send messages over the clock socket */
|
|
||||||
void writeClockInterface(void);
|
|
||||||
|
|
||||||
/** Detect RACH bursts */
|
|
||||||
bool detectRACH(TransceiverState *state,
|
|
||||||
signalVector &burst,
|
|
||||||
complex &, float &toa);
|
|
||||||
|
|
||||||
/** Detect normal bursts */
|
|
||||||
bool detectTSC(TransceiverState *state,
|
|
||||||
signalVector &burst,
|
|
||||||
complex &, float &toa, GSM::Time &time);
|
|
||||||
|
|
||||||
/** Demodulat burst and output soft bits */
|
|
||||||
SoftVector *demodulate(TransceiverState *state,
|
|
||||||
signalVector &burst, complex amp,
|
|
||||||
float toa, size_t tn, bool equalize);
|
|
||||||
|
|
||||||
|
|
||||||
int mSPSTx; ///< number of samples per Tx symbol
|
|
||||||
int mSPSRx; ///< number of samples per Rx symbol
|
|
||||||
size_t mChans;
|
|
||||||
|
|
||||||
bool mOn; ///< flag to indicate that transceiver is powered on
|
|
||||||
double mTxFreq; ///< the transmit frequency
|
|
||||||
double mRxFreq; ///< the receive frequency
|
|
||||||
int mPower; ///< the transmit power in dB
|
|
||||||
unsigned mTSC; ///< the midamble sequence code
|
|
||||||
unsigned mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
|
|
||||||
|
|
||||||
std::vector<TransceiverState> mStates;
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
/** Transceiver constructor
|
/** Transceiver constructor
|
||||||
@param wBasePort base port number of UDP sockets
|
@param wBasePort base port number of UDP sockets
|
||||||
@param TRXAddress IP address of the TRX manager, as a string
|
@param TRXAddress IP address of the TRX manager, as a string
|
||||||
@@ -181,17 +97,17 @@ public:
|
|||||||
@param radioInterface associated radioInterface object
|
@param radioInterface associated radioInterface object
|
||||||
*/
|
*/
|
||||||
Transceiver(int wBasePort,
|
Transceiver(int wBasePort,
|
||||||
const char *TRXAddress,
|
const char *TRXAddress,
|
||||||
size_t wSPS, size_t chans,
|
size_t wSPS, size_t chans,
|
||||||
GSM::Time wTransmitLatency,
|
GSM::Time wTransmitLatency,
|
||||||
RadioInterface *wRadioInterface);
|
RadioInterface *wRadioInterface,
|
||||||
|
double wRssiOffset);
|
||||||
|
|
||||||
/** Destructor */
|
/** Destructor */
|
||||||
~Transceiver();
|
~Transceiver();
|
||||||
|
|
||||||
/** start the Transceiver */
|
/** Start the control loop */
|
||||||
void start();
|
bool init(int filler, size_t rtsc);
|
||||||
bool init(bool filler);
|
|
||||||
|
|
||||||
/** attach the radioInterface receive FIFO */
|
/** attach the radioInterface receive FIFO */
|
||||||
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
|
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
|
||||||
@@ -226,6 +142,106 @@ public:
|
|||||||
LOOPBACK ///< similar go VII, used in loopback testing
|
LOOPBACK ///< similar go VII, used in loopback testing
|
||||||
} ChannelCombination;
|
} ChannelCombination;
|
||||||
|
|
||||||
|
/** 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;
|
||||||
|
|
||||||
|
enum FillerType {
|
||||||
|
FILLER_DUMMY,
|
||||||
|
FILLER_ZERO,
|
||||||
|
FILLER_RAND,
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
int mBasePort;
|
||||||
|
std::string mAddr;
|
||||||
|
|
||||||
|
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
|
||||||
|
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
|
||||||
|
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
|
||||||
|
|
||||||
|
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
|
||||||
|
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
|
||||||
|
|
||||||
|
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
|
||||||
|
Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO
|
||||||
|
Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO
|
||||||
|
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
|
||||||
|
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
|
||||||
|
|
||||||
|
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
|
||||||
|
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
|
||||||
|
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
|
||||||
|
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
|
||||||
|
|
||||||
|
RadioInterface *mRadioInterface; ///< associated radioInterface object
|
||||||
|
double txFullScale; ///< full scale input to radio
|
||||||
|
double rxFullScale; ///< full scale output to radio
|
||||||
|
|
||||||
|
double rssiOffset; ///< RSSI to dBm conversion offset
|
||||||
|
|
||||||
|
/** modulate and add a burst to the transmit queue */
|
||||||
|
void addRadioVector(size_t chan, BitVector &bits,
|
||||||
|
int RSSI, GSM::Time &wTime);
|
||||||
|
|
||||||
|
/** Update filler table */
|
||||||
|
void updateFillerTable(size_t chan, radioVector *burst);
|
||||||
|
|
||||||
|
/** 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, double &RSSI,
|
||||||
|
double &timingOffset, double &noise,
|
||||||
|
size_t chan = 0);
|
||||||
|
|
||||||
|
/** Set modulus for specific timeslot */
|
||||||
|
void setModulus(size_t timeslot, size_t chan);
|
||||||
|
|
||||||
|
/** return the expected burst type for the specified timestamp */
|
||||||
|
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
|
||||||
|
|
||||||
|
/** send messages over the clock socket */
|
||||||
|
void writeClockInterface(void);
|
||||||
|
|
||||||
|
/** Detect RACH bursts */
|
||||||
|
int detectRACH(TransceiverState *state,
|
||||||
|
signalVector &burst,
|
||||||
|
complex &, float &toa);
|
||||||
|
|
||||||
|
/** Detect normal bursts */
|
||||||
|
int detectTSC(TransceiverState *state,
|
||||||
|
signalVector &burst,
|
||||||
|
complex &, float &toa, GSM::Time &time);
|
||||||
|
|
||||||
|
/** Demodulat burst and output soft bits */
|
||||||
|
SoftVector *demodulate(TransceiverState *state,
|
||||||
|
signalVector &burst, complex amp,
|
||||||
|
float toa, size_t tn, bool equalize);
|
||||||
|
|
||||||
|
int mSPSTx; ///< number of samples per Tx symbol
|
||||||
|
int mSPSRx; ///< number of samples per Rx symbol
|
||||||
|
size_t mChans;
|
||||||
|
|
||||||
|
bool mOn; ///< flag to indicate that transceiver is powered on
|
||||||
|
double mTxFreq; ///< the transmit frequency
|
||||||
|
double mRxFreq; ///< the receive frequency
|
||||||
|
unsigned mTSC; ///< the midamble sequence code
|
||||||
|
unsigned mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
|
||||||
|
|
||||||
|
std::vector<TransceiverState> mStates;
|
||||||
|
|
||||||
|
/** Start and stop I/O threads through the control socket API */
|
||||||
|
bool start();
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
/** Protect destructor accessable stop call */
|
||||||
|
Mutex mLock;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
/** drive lower receive I/O and burst generation */
|
/** drive lower receive I/O and burst generation */
|
||||||
void driveReceiveRadio();
|
void driveReceiveRadio();
|
||||||
|
|||||||
@@ -34,12 +34,25 @@
|
|||||||
|
|
||||||
#define B2XX_CLK_RT 26e6
|
#define B2XX_CLK_RT 26e6
|
||||||
#define E1XX_CLK_RT 52e6
|
#define E1XX_CLK_RT 52e6
|
||||||
#define B2XX_BASE_RT GSMRATE
|
|
||||||
#define B100_BASE_RT 400000
|
#define B100_BASE_RT 400000
|
||||||
#define USRP2_BASE_RT 390625
|
#define USRP2_BASE_RT 390625
|
||||||
#define TX_AMPL 0.3
|
#define USRP_TX_AMPL 0.3
|
||||||
|
#define UMTRX_TX_AMPL 0.7
|
||||||
#define SAMPLE_BUF_SZ (1 << 20)
|
#define SAMPLE_BUF_SZ (1 << 20)
|
||||||
|
|
||||||
|
/*
|
||||||
|
* UHD timeout value on streaming (re)start
|
||||||
|
*
|
||||||
|
* Allow some time for streaming to commence after the start command is issued,
|
||||||
|
* but consider a wait beyond one second to be a definite error condition.
|
||||||
|
*/
|
||||||
|
#define UHD_RESTART_TIMEOUT 1.0
|
||||||
|
|
||||||
|
/*
|
||||||
|
* UmTRX specific settings
|
||||||
|
*/
|
||||||
|
#define UMTRX_VGA1_DEF -18
|
||||||
|
|
||||||
enum uhd_dev_type {
|
enum uhd_dev_type {
|
||||||
USRP1,
|
USRP1,
|
||||||
USRP2,
|
USRP2,
|
||||||
@@ -47,6 +60,8 @@ enum uhd_dev_type {
|
|||||||
B200,
|
B200,
|
||||||
B210,
|
B210,
|
||||||
E1XX,
|
E1XX,
|
||||||
|
E3XX,
|
||||||
|
X3XX,
|
||||||
UMTRX,
|
UMTRX,
|
||||||
NUM_USRP_TYPES,
|
NUM_USRP_TYPES,
|
||||||
};
|
};
|
||||||
@@ -81,6 +96,10 @@ static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
|
|||||||
{ B210, 4, 6.9248e-5, "B210 4 SPS" },
|
{ B210, 4, 6.9248e-5, "B210 4 SPS" },
|
||||||
{ E1XX, 1, 9.5192e-5, "E1XX 1 SPS" },
|
{ E1XX, 1, 9.5192e-5, "E1XX 1 SPS" },
|
||||||
{ E1XX, 4, 6.5571e-5, "E1XX 4 SPS" },
|
{ E1XX, 4, 6.5571e-5, "E1XX 4 SPS" },
|
||||||
|
{ E3XX, 1, 1.5000e-4, "E3XX 1 SPS" },
|
||||||
|
{ E3XX, 4, 1.2740e-4, "E3XX 4 SPS" },
|
||||||
|
{ X3XX, 1, 1.5360e-4, "X3XX 1 SPS"},
|
||||||
|
{ X3XX, 4, 1.1264e-4, "X3XX 4 SPS"},
|
||||||
{ UMTRX, 1, 9.9692e-5, "UmTRX 1 SPS" },
|
{ UMTRX, 1, 9.9692e-5, "UmTRX 1 SPS" },
|
||||||
{ UMTRX, 4, 7.3846e-5, "UmTRX 4 SPS" },
|
{ UMTRX, 4, 7.3846e-5, "UmTRX 4 SPS" },
|
||||||
};
|
};
|
||||||
@@ -97,7 +116,7 @@ static struct uhd_dev_offset special_offsets[] = {
|
|||||||
static double get_dev_offset(enum uhd_dev_type type,
|
static double get_dev_offset(enum uhd_dev_type type,
|
||||||
int sps, bool diversity = false)
|
int sps, bool diversity = false)
|
||||||
{
|
{
|
||||||
struct uhd_dev_offset *offset;
|
struct uhd_dev_offset *offset = NULL;
|
||||||
|
|
||||||
/* Reject USRP1 */
|
/* Reject USRP1 */
|
||||||
if (type == USRP1) {
|
if (type == USRP1) {
|
||||||
@@ -121,17 +140,21 @@ static double get_dev_offset(enum uhd_dev_type type,
|
|||||||
offset = &special_offsets[1];
|
offset = &special_offsets[1];
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* Normal operation */
|
/* Search for matching offset value */
|
||||||
switch (sps) {
|
for (int i = 0; i < NUM_USRP_TYPES * 2; i++) {
|
||||||
case 1:
|
if ((type == uhd_offsets[i].type) &&
|
||||||
offset = &uhd_offsets[2 * type + 0];
|
(sps == uhd_offsets[i].sps)) {
|
||||||
break;
|
offset = &uhd_offsets[i];
|
||||||
case 4:
|
break;
|
||||||
default:
|
}
|
||||||
offset = &uhd_offsets[2 * type + 1];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!offset) {
|
||||||
|
LOG(ERR) << "Invalid device configuration";
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "-- Setting " << offset->desc << std::endl;
|
std::cout << "-- Setting " << offset->desc << std::endl;
|
||||||
|
|
||||||
return offset->offset;
|
return offset->offset;
|
||||||
@@ -156,12 +179,14 @@ static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
|
|||||||
|
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case USRP2:
|
case USRP2:
|
||||||
|
case X3XX:
|
||||||
return USRP2_BASE_RT * sps;
|
return USRP2_BASE_RT * sps;
|
||||||
case B100:
|
case B100:
|
||||||
return B100_BASE_RT * sps;
|
return B100_BASE_RT * sps;
|
||||||
case B200:
|
case B200:
|
||||||
case B210:
|
case B210:
|
||||||
case E1XX:
|
case E1XX:
|
||||||
|
case E3XX:
|
||||||
case UMTRX:
|
case UMTRX:
|
||||||
return GSMRATE * sps;
|
return GSMRATE * sps;
|
||||||
default:
|
default:
|
||||||
@@ -225,7 +250,7 @@ public:
|
|||||||
/** Buffer status string
|
/** Buffer status string
|
||||||
@return a formatted string describing internal buffer state
|
@return a formatted string describing internal buffer state
|
||||||
*/
|
*/
|
||||||
std::string str_status() const;
|
std::string str_status(size_t ts) const;
|
||||||
|
|
||||||
/** Formatted error string
|
/** Formatted error string
|
||||||
@param code an error code
|
@param code an error code
|
||||||
@@ -268,7 +293,7 @@ public:
|
|||||||
int open(const std::string &args, bool extref);
|
int open(const std::string &args, bool extref);
|
||||||
bool start();
|
bool start();
|
||||||
bool stop();
|
bool stop();
|
||||||
void restart();
|
bool restart();
|
||||||
void setPriority(float prio);
|
void setPriority(float prio);
|
||||||
enum TxWindowType getWindowType() { return tx_window; }
|
enum TxWindowType getWindowType() { return tx_window; }
|
||||||
|
|
||||||
@@ -286,8 +311,8 @@ public:
|
|||||||
inline TIMESTAMP initialWriteTimestamp() { return ts_initial * sps; }
|
inline TIMESTAMP initialWriteTimestamp() { return ts_initial * sps; }
|
||||||
inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
|
inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
|
||||||
|
|
||||||
inline double fullScaleInputValue() { return 32000 * TX_AMPL; }
|
double fullScaleInputValue();
|
||||||
inline double fullScaleOutputValue() { return 32000; }
|
double fullScaleOutputValue();
|
||||||
|
|
||||||
double setRxGain(double db, size_t chan);
|
double setRxGain(double db, size_t chan);
|
||||||
double getRxGain(size_t chan);
|
double getRxGain(size_t chan);
|
||||||
@@ -313,8 +338,9 @@ public:
|
|||||||
|
|
||||||
enum err_code {
|
enum err_code {
|
||||||
ERROR_TIMING = -1,
|
ERROR_TIMING = -1,
|
||||||
ERROR_UNRECOVERABLE = -2,
|
ERROR_TIMEOUT = -2,
|
||||||
ERROR_UNHANDLED = -3,
|
ERROR_UNRECOVERABLE = -3,
|
||||||
|
ERROR_UNHANDLED = -4,
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
@@ -358,8 +384,9 @@ private:
|
|||||||
uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
|
uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
|
||||||
bool set_freq(double freq, size_t chan, bool tx);
|
bool set_freq(double freq, size_t chan, bool tx);
|
||||||
|
|
||||||
Thread async_event_thrd;
|
Thread *async_event_thrd;
|
||||||
bool diversity;
|
bool diversity;
|
||||||
|
Mutex tune_lock;
|
||||||
};
|
};
|
||||||
|
|
||||||
void *async_event_loop(uhd_device *dev)
|
void *async_event_loop(uhd_device *dev)
|
||||||
@@ -396,6 +423,12 @@ void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void thread_enable_cancel(bool cancel)
|
||||||
|
{
|
||||||
|
cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
|
||||||
|
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
uhd_device::uhd_device(size_t sps, size_t chans, bool diversity, double offset)
|
uhd_device::uhd_device(size_t sps, size_t chans, bool diversity, double offset)
|
||||||
: tx_gain_min(0.0), tx_gain_max(0.0),
|
: tx_gain_min(0.0), tx_gain_max(0.0),
|
||||||
rx_gain_min(0.0), rx_gain_max(0.0),
|
rx_gain_min(0.0), rx_gain_max(0.0),
|
||||||
@@ -421,9 +454,25 @@ void uhd_device::init_gains()
|
|||||||
{
|
{
|
||||||
uhd::gain_range_t range;
|
uhd::gain_range_t range;
|
||||||
|
|
||||||
range = usrp_dev->get_tx_gain_range();
|
if (dev_type == UMTRX) {
|
||||||
tx_gain_min = range.start();
|
std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
|
||||||
tx_gain_max = range.stop();
|
if (gain_stages[0] == "VGA") {
|
||||||
|
LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
|
||||||
|
}
|
||||||
|
if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
|
||||||
|
range = usrp_dev->get_tx_gain_range();
|
||||||
|
tx_gain_min = range.start();
|
||||||
|
tx_gain_max = range.stop();
|
||||||
|
} else {
|
||||||
|
range = usrp_dev->get_tx_gain_range("VGA2");
|
||||||
|
tx_gain_min = UMTRX_VGA1_DEF + range.start();
|
||||||
|
tx_gain_max = UMTRX_VGA1_DEF + range.stop();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
range = usrp_dev->get_tx_gain_range();
|
||||||
|
tx_gain_min = range.start();
|
||||||
|
tx_gain_max = range.stop();
|
||||||
|
}
|
||||||
|
|
||||||
range = usrp_dev->get_rx_gain_range();
|
range = usrp_dev->get_rx_gain_range();
|
||||||
rx_gain_min = range.start();
|
rx_gain_min = range.start();
|
||||||
@@ -474,7 +523,7 @@ int uhd_device::set_rates(double tx_rate, double rx_rate)
|
|||||||
double tx_offset, rx_offset;
|
double tx_offset, rx_offset;
|
||||||
|
|
||||||
/* B2XX and E1xx are the only device where we set FPGA clocking */
|
/* B2XX and E1xx are the only device where we set FPGA clocking */
|
||||||
if ((dev_type == B200) || (dev_type == B210)) {
|
if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
|
||||||
if (set_master_clk(B2XX_CLK_RT) < 0)
|
if (set_master_clk(B2XX_CLK_RT) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
@@ -514,7 +563,23 @@ double uhd_device::setTxGain(double db, size_t chan)
|
|||||||
return 0.0f;
|
return 0.0f;
|
||||||
}
|
}
|
||||||
|
|
||||||
usrp_dev->set_tx_gain(db, chan);
|
if (dev_type == UMTRX) {
|
||||||
|
std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
|
||||||
|
if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
|
||||||
|
usrp_dev->set_tx_gain(db, chan);
|
||||||
|
} else {
|
||||||
|
// New UHD versions support split configuration of
|
||||||
|
// Tx gain stages. We utilize this to set the gain
|
||||||
|
// configuration, optimal for the Tx signal quality.
|
||||||
|
// From our measurements, VGA1 must be 18dB plus-minus
|
||||||
|
// one and VGA2 is the best when 23dB or lower.
|
||||||
|
usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
|
||||||
|
usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
usrp_dev->set_tx_gain(db, chan);
|
||||||
|
}
|
||||||
|
|
||||||
tx_gains[chan] = usrp_dev->get_tx_gain(chan);
|
tx_gains[chan] = usrp_dev->get_tx_gain(chan);
|
||||||
|
|
||||||
LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB";
|
LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB";
|
||||||
@@ -557,8 +622,8 @@ 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, e100_str, e110_str,
|
size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str,
|
||||||
b100_str, b200_str, b210_str, umtrx_str;
|
b100_str, b200_str, b210_str, x300_str, x310_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();
|
||||||
@@ -571,6 +636,9 @@ bool uhd_device::parse_dev_type()
|
|||||||
b210_str = mboard_str.find("B210");
|
b210_str = mboard_str.find("B210");
|
||||||
e100_str = mboard_str.find("E100");
|
e100_str = mboard_str.find("E100");
|
||||||
e110_str = mboard_str.find("E110");
|
e110_str = mboard_str.find("E110");
|
||||||
|
e310_str = mboard_str.find("E310");
|
||||||
|
x300_str = mboard_str.find("X300");
|
||||||
|
x310_str = mboard_str.find("X310");
|
||||||
umtrx_str = dev_str.find("UmTRX");
|
umtrx_str = dev_str.find("UmTRX");
|
||||||
|
|
||||||
if (usrp1_str != std::string::npos) {
|
if (usrp1_str != std::string::npos) {
|
||||||
@@ -598,6 +666,15 @@ bool uhd_device::parse_dev_type()
|
|||||||
} else if (usrp2_str != std::string::npos) {
|
} else if (usrp2_str != std::string::npos) {
|
||||||
tx_window = TX_WINDOW_FIXED;
|
tx_window = TX_WINDOW_FIXED;
|
||||||
dev_type = USRP2;
|
dev_type = USRP2;
|
||||||
|
} else if (e310_str != std::string::npos) {
|
||||||
|
tx_window = TX_WINDOW_FIXED;
|
||||||
|
dev_type = E3XX;
|
||||||
|
} else if (x300_str != std::string::npos) {
|
||||||
|
tx_window = TX_WINDOW_FIXED;
|
||||||
|
dev_type = X3XX;
|
||||||
|
} else if (x310_str != std::string::npos) {
|
||||||
|
tx_window = TX_WINDOW_FIXED;
|
||||||
|
dev_type = X3XX;
|
||||||
} else if (umtrx_str != std::string::npos) {
|
} else if (umtrx_str != std::string::npos) {
|
||||||
tx_window = TX_WINDOW_FIXED;
|
tx_window = TX_WINDOW_FIXED;
|
||||||
dev_type = UMTRX;
|
dev_type = UMTRX;
|
||||||
@@ -630,9 +707,9 @@ int uhd_device::open(const std::string &args, bool extref)
|
|||||||
// Use the first found device
|
// Use the first found device
|
||||||
LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
|
LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
|
||||||
try {
|
try {
|
||||||
usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
|
usrp_dev = uhd::usrp::multi_usrp::make(addr);
|
||||||
} catch(...) {
|
} catch(...) {
|
||||||
LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
|
LOG(ALERT) << "UHD make failed, device " << args;
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -673,6 +750,16 @@ int uhd_device::open(const std::string &args, bool extref)
|
|||||||
if (set_rates(_tx_rate, _rx_rate) < 0)
|
if (set_rates(_tx_rate, _rx_rate) < 0)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
|
// Set RF frontend bandwidth
|
||||||
|
if (dev_type == UMTRX) {
|
||||||
|
// Setting LMS6002D LPF to 500kHz gives us the best signal quality
|
||||||
|
for (size_t i = 0; i < chans; i++) {
|
||||||
|
usrp_dev->set_tx_bandwidth(500*1000*2, i);
|
||||||
|
if (!diversity)
|
||||||
|
usrp_dev->set_rx_bandwidth(500*1000*2, i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Create TX and RX streamers */
|
/* Create TX and RX streamers */
|
||||||
uhd::stream_args_t stream_args("sc16");
|
uhd::stream_args_t stream_args("sc16");
|
||||||
for (size_t i = 0; i < chans; i++)
|
for (size_t i = 0; i < chans; i++)
|
||||||
@@ -712,10 +799,12 @@ int uhd_device::open(const std::string &args, bool extref)
|
|||||||
case B100:
|
case B100:
|
||||||
return RESAMP_64M;
|
return RESAMP_64M;
|
||||||
case USRP2:
|
case USRP2:
|
||||||
|
case X3XX:
|
||||||
return RESAMP_100M;
|
return RESAMP_100M;
|
||||||
case B200:
|
case B200:
|
||||||
case B210:
|
case B210:
|
||||||
case E1XX:
|
case E1XX:
|
||||||
|
case E3XX:
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -727,7 +816,7 @@ 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;
|
||||||
float timeout = 0.1f;
|
float timeout = UHD_RESTART_TIMEOUT;
|
||||||
|
|
||||||
std::vector<std::vector<short> >
|
std::vector<std::vector<short> >
|
||||||
pkt_bufs(chans, std::vector<short>(2 * rx_spp));
|
pkt_bufs(chans, std::vector<short>(2 * rx_spp));
|
||||||
@@ -743,6 +832,8 @@ bool uhd_device::flush_recv(size_t num_pkts)
|
|||||||
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:
|
||||||
|
LOG(ALERT) << "Device timed out";
|
||||||
|
return false;
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -756,7 +847,7 @@ bool uhd_device::flush_recv(size_t num_pkts)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void uhd_device::restart()
|
bool uhd_device::restart()
|
||||||
{
|
{
|
||||||
/* Allow 100 ms delay to align multi-channel streams */
|
/* Allow 100 ms delay to align multi-channel streams */
|
||||||
double delay = 0.1;
|
double delay = 0.1;
|
||||||
@@ -771,7 +862,7 @@ void uhd_device::restart()
|
|||||||
|
|
||||||
usrp_dev->issue_stream_cmd(cmd);
|
usrp_dev->issue_stream_cmd(cmd);
|
||||||
|
|
||||||
flush_recv(1);
|
return flush_recv(10);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool uhd_device::start()
|
bool uhd_device::start()
|
||||||
@@ -787,10 +878,12 @@ bool uhd_device::start()
|
|||||||
uhd::msg::register_handler(&uhd_msg_handler);
|
uhd::msg::register_handler(&uhd_msg_handler);
|
||||||
|
|
||||||
// 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 = new Thread();
|
||||||
|
async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
|
||||||
|
|
||||||
// Start streaming
|
// Start streaming
|
||||||
restart();
|
if (!restart())
|
||||||
|
return false;
|
||||||
|
|
||||||
// Display usrp time
|
// Display usrp time
|
||||||
double time_now = usrp_dev->get_time_now().get_real_secs();
|
double time_now = usrp_dev->get_time_now().get_real_secs();
|
||||||
@@ -810,6 +903,10 @@ bool uhd_device::stop()
|
|||||||
|
|
||||||
usrp_dev->issue_stream_cmd(stream_cmd);
|
usrp_dev->issue_stream_cmd(stream_cmd);
|
||||||
|
|
||||||
|
async_event_thrd->cancel();
|
||||||
|
async_event_thrd->join();
|
||||||
|
delete async_event_thrd;
|
||||||
|
|
||||||
started = false;
|
started = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -830,6 +927,7 @@ int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t 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:
|
||||||
LOG(ALERT) << "UHD: Receive timed out";
|
LOG(ALERT) << "UHD: Receive timed out";
|
||||||
|
return ERROR_TIMEOUT;
|
||||||
case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
|
case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
|
||||||
case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
|
case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
|
||||||
case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
|
case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
|
||||||
@@ -885,7 +983,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
|||||||
rc = rx_buffers[0]->avail_smpls(timestamp);
|
rc = rx_buffers[0]->avail_smpls(timestamp);
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
LOG(ERR) << rx_buffers[0]->str_code(rc);
|
LOG(ERR) << rx_buffers[0]->str_code(rc);
|
||||||
LOG(ERR) << rx_buffers[0]->str_status();
|
LOG(ERR) << rx_buffers[0]->str_status(timestamp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -899,8 +997,11 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
|||||||
|
|
||||||
// Receive samples from the usrp until we have enough
|
// Receive samples from the usrp until we have enough
|
||||||
while (rx_buffers[0]->avail_smpls(timestamp) < len) {
|
while (rx_buffers[0]->avail_smpls(timestamp) < len) {
|
||||||
|
thread_enable_cancel(false);
|
||||||
size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
|
size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
|
||||||
metadata, 0.1, true);
|
metadata, 0.1, true);
|
||||||
|
thread_enable_cancel(true);
|
||||||
|
|
||||||
rx_pkt_cnt++;
|
rx_pkt_cnt++;
|
||||||
|
|
||||||
// Check for errors
|
// Check for errors
|
||||||
@@ -910,6 +1011,9 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
|||||||
LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
|
LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
|
||||||
LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
|
LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
|
||||||
exit(-1);
|
exit(-1);
|
||||||
|
case ERROR_TIMEOUT:
|
||||||
|
// Assume stopping condition
|
||||||
|
return 0;
|
||||||
case ERROR_TIMING:
|
case ERROR_TIMING:
|
||||||
restart();
|
restart();
|
||||||
case ERROR_UNHANDLED:
|
case ERROR_UNHANDLED:
|
||||||
@@ -927,7 +1031,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
|||||||
// Continue on local overrun, exit on other errors
|
// Continue on local overrun, exit on other errors
|
||||||
if ((rc < 0)) {
|
if ((rc < 0)) {
|
||||||
LOG(ERR) << rx_buffers[i]->str_code(rc);
|
LOG(ERR) << rx_buffers[i]->str_code(rc);
|
||||||
LOG(ERR) << rx_buffers[i]->str_status();
|
LOG(ERR) << rx_buffers[i]->str_status(timestamp);
|
||||||
if (rc != smpl_buf::ERROR_OVERFLOW)
|
if (rc != smpl_buf::ERROR_OVERFLOW)
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
@@ -939,7 +1043,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
|
|||||||
rc = rx_buffers[i]->read(bufs[i], len, timestamp);
|
rc = rx_buffers[i]->read(bufs[i], len, timestamp);
|
||||||
if ((rc < 0) || (rc != len)) {
|
if ((rc < 0) || (rc != len)) {
|
||||||
LOG(ERR) << rx_buffers[i]->str_code(rc);
|
LOG(ERR) << rx_buffers[i]->str_code(rc);
|
||||||
LOG(ERR) << rx_buffers[i]->str_status();
|
LOG(ERR) << rx_buffers[i]->str_status(timestamp);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -988,7 +1092,10 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
thread_enable_cancel(false);
|
||||||
size_t num_smpls = tx_stream->send(bufs, len, metadata);
|
size_t num_smpls = tx_stream->send(bufs, len, metadata);
|
||||||
|
thread_enable_cancel(true);
|
||||||
|
|
||||||
if (num_smpls != (unsigned) len) {
|
if (num_smpls != (unsigned) len) {
|
||||||
LOG(ALERT) << "UHD: Device send timed out";
|
LOG(ALERT) << "UHD: Device send timed out";
|
||||||
}
|
}
|
||||||
@@ -1007,7 +1114,19 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
|
|||||||
std::vector<double> freqs;
|
std::vector<double> freqs;
|
||||||
uhd::tune_request_t treq(freq);
|
uhd::tune_request_t treq(freq);
|
||||||
|
|
||||||
if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) {
|
if (dev_type == UMTRX) {
|
||||||
|
if (offset > 0.0)
|
||||||
|
return uhd::tune_request_t(freq, offset);
|
||||||
|
|
||||||
|
// Don't use DSP tuning, because LMS6002D PLL steps are small enough.
|
||||||
|
// We end up with DSP tuning just for 2-3Hz, which is meaningless and
|
||||||
|
// only distort the signal (because cordic is not ideal).
|
||||||
|
treq.target_freq = freq;
|
||||||
|
treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
|
||||||
|
treq.rf_freq = freq;
|
||||||
|
treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
|
||||||
|
treq.dsp_freq = 0.0;
|
||||||
|
} else if (chans == 1) {
|
||||||
if (offset == 0.0)
|
if (offset == 0.0)
|
||||||
return treq;
|
return treq;
|
||||||
|
|
||||||
@@ -1087,6 +1206,7 @@ bool uhd_device::setTxFreq(double wFreq, size_t chan)
|
|||||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
ScopedLock lock(tune_lock);
|
||||||
|
|
||||||
return set_freq(wFreq, chan, true);
|
return set_freq(wFreq, chan, true);
|
||||||
}
|
}
|
||||||
@@ -1097,6 +1217,7 @@ bool uhd_device::setRxFreq(double wFreq, size_t chan)
|
|||||||
LOG(ALERT) << "Requested non-existent channel " << chan;
|
LOG(ALERT) << "Requested non-existent channel " << chan;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
ScopedLock lock(tune_lock);
|
||||||
|
|
||||||
return set_freq(wFreq, chan, false);
|
return set_freq(wFreq, chan, false);
|
||||||
}
|
}
|
||||||
@@ -1121,10 +1242,27 @@ double uhd_device::getRxFreq(size_t chan)
|
|||||||
return rx_freqs[chan];
|
return rx_freqs[chan];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double uhd_device::fullScaleInputValue()
|
||||||
|
{
|
||||||
|
if (dev_type == UMTRX)
|
||||||
|
return (double) SHRT_MAX * UMTRX_TX_AMPL;
|
||||||
|
else
|
||||||
|
return (double) SHRT_MAX * USRP_TX_AMPL;
|
||||||
|
}
|
||||||
|
|
||||||
|
double uhd_device::fullScaleOutputValue()
|
||||||
|
{
|
||||||
|
return (double) SHRT_MAX;
|
||||||
|
}
|
||||||
|
|
||||||
bool uhd_device::recv_async_msg()
|
bool uhd_device::recv_async_msg()
|
||||||
{
|
{
|
||||||
uhd::async_metadata_t md;
|
uhd::async_metadata_t md;
|
||||||
if (!usrp_dev->get_device()->recv_async_msg(md))
|
|
||||||
|
thread_enable_cancel(false);
|
||||||
|
bool rc = usrp_dev->get_device()->recv_async_msg(md);
|
||||||
|
thread_enable_cancel(true);
|
||||||
|
if (!rc)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Assume that any error requires resynchronization
|
// Assume that any error requires resynchronization
|
||||||
@@ -1326,11 +1464,12 @@ ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
|
|||||||
return write(buf, len, convert_time(ts, clk_rt));
|
return write(buf, len, convert_time(ts, clk_rt));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string smpl_buf::str_status() const
|
std::string smpl_buf::str_status(size_t ts) const
|
||||||
{
|
{
|
||||||
std::ostringstream ost("Sample buffer: ");
|
std::ostringstream ost("Sample buffer: ");
|
||||||
|
|
||||||
ost << "length = " << buf_len;
|
ost << "timestamp = " << ts;
|
||||||
|
ost << ", length = " << buf_len;
|
||||||
ost << ", time_start = " << time_start;
|
ost << ", time_start = " << time_start;
|
||||||
ost << ", time_end = " << time_end;
|
ost << ", time_end = " << time_end;
|
||||||
ost << ", data_start = " << data_start;
|
ost << ", data_start = " << data_start;
|
||||||
|
|||||||
@@ -600,7 +600,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(size_t sps, size_t chans, bool diversity)
|
RadioDevice *RadioDevice::make(size_t sps, size_t chans, bool diversity, double)
|
||||||
{
|
{
|
||||||
return new USRPDevice(sps, chans, diversity);
|
return new USRPDevice(sps, chans, diversity);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,10 +65,12 @@ struct trx_config {
|
|||||||
unsigned port;
|
unsigned port;
|
||||||
unsigned sps;
|
unsigned sps;
|
||||||
unsigned chans;
|
unsigned chans;
|
||||||
|
unsigned rtsc;
|
||||||
bool extref;
|
bool extref;
|
||||||
bool filler;
|
Transceiver::FillerType filler;
|
||||||
bool diversity;
|
bool diversity;
|
||||||
double offset;
|
double offset;
|
||||||
|
double rssi_offset;
|
||||||
};
|
};
|
||||||
|
|
||||||
ConfigurationTable gConfig;
|
ConfigurationTable gConfig;
|
||||||
@@ -154,19 +156,23 @@ bool trx_setup_config(struct trx_config *config)
|
|||||||
config->diversity = DEFAULT_DIVERSITY;
|
config->diversity = DEFAULT_DIVERSITY;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!config->sps)
|
|
||||||
config->sps = DEFAULT_SPS;
|
|
||||||
|
|
||||||
if (!config->chans)
|
|
||||||
config->chans = DEFAULT_CHANS;
|
|
||||||
|
|
||||||
/* Diversity only supported on 2 channels */
|
/* Diversity only supported on 2 channels */
|
||||||
if (config->diversity)
|
if (config->diversity)
|
||||||
config->chans = 2;
|
config->chans = 2;
|
||||||
|
|
||||||
refstr = config->extref ? "Enabled" : "Disabled";
|
refstr = config->extref ? "Enabled" : "Disabled";
|
||||||
fillstr = config->filler ? "Enabled" : "Disabled";
|
|
||||||
divstr = config->diversity ? "Enabled" : "Disabled";
|
divstr = config->diversity ? "Enabled" : "Disabled";
|
||||||
|
switch (config->filler) {
|
||||||
|
case Transceiver::FILLER_DUMMY:
|
||||||
|
fillstr = "Dummy bursts";
|
||||||
|
break;
|
||||||
|
case Transceiver::FILLER_ZERO:
|
||||||
|
fillstr = "Disabled";
|
||||||
|
break;
|
||||||
|
case Transceiver::FILLER_RAND:
|
||||||
|
fillstr = "Normal busrts with random payload";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
std::ostringstream ost("");
|
std::ostringstream ost("");
|
||||||
ost << "Config Settings" << std::endl;
|
ost << "Config Settings" << std::endl;
|
||||||
@@ -180,6 +186,7 @@ bool trx_setup_config(struct trx_config *config)
|
|||||||
ost << " C0 Filler Table......... " << fillstr << std::endl;
|
ost << " C0 Filler Table......... " << fillstr << std::endl;
|
||||||
ost << " Diversity............... " << divstr << std::endl;
|
ost << " Diversity............... " << divstr << std::endl;
|
||||||
ost << " Tuning offset........... " << config->offset << std::endl;
|
ost << " Tuning offset........... " << config->offset << std::endl;
|
||||||
|
ost << " RSSI to dBm offset...... " << config->rssi_offset << std::endl;
|
||||||
std::cout << ost << std::endl;
|
std::cout << ost << std::endl;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@@ -235,8 +242,8 @@ Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
|
|||||||
VectorFIFO *fifo;
|
VectorFIFO *fifo;
|
||||||
|
|
||||||
trx = new Transceiver(config->port, config->addr.c_str(), config->sps,
|
trx = new Transceiver(config->port, config->addr.c_str(), config->sps,
|
||||||
config->chans, GSM::Time(3,0), radio);
|
config->chans, GSM::Time(3,0), radio, config->rssi_offset);
|
||||||
if (!trx->init(config->filler)) {
|
if (!trx->init(config->filler, config->rtsc)) {
|
||||||
LOG(ALERT) << "Failed to initialize transceiver";
|
LOG(ALERT) << "Failed to initialize transceiver";
|
||||||
delete trx;
|
delete trx;
|
||||||
return NULL;
|
return NULL;
|
||||||
@@ -286,7 +293,9 @@ static void print_help()
|
|||||||
" -s Samples-per-symbol (1 or 4)\n"
|
" -s Samples-per-symbol (1 or 4)\n"
|
||||||
" -c Number of ARFCN channels (default=1)\n"
|
" -c Number of ARFCN channels (default=1)\n"
|
||||||
" -f Enable C0 filler table\n"
|
" -f Enable C0 filler table\n"
|
||||||
" -o Set baseband frequency offset (default=auto)\n",
|
" -o Set baseband frequency offset (default=auto)\n"
|
||||||
|
" -r Random burst test mode with TSC\n"
|
||||||
|
" -R RSSI to dBm offset in dB (default=0)\n",
|
||||||
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
|
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -295,14 +304,16 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||||||
int option;
|
int option;
|
||||||
|
|
||||||
config->port = 0;
|
config->port = 0;
|
||||||
config->sps = 0;
|
config->sps = DEFAULT_SPS;
|
||||||
config->chans = 0;
|
config->chans = DEFAULT_CHANS;
|
||||||
|
config->rtsc = 0;
|
||||||
config->extref = false;
|
config->extref = false;
|
||||||
config->filler = false;
|
config->filler = Transceiver::FILLER_ZERO;
|
||||||
config->diversity = false;
|
config->diversity = false;
|
||||||
config->offset = 0.0;
|
config->offset = 0.0;
|
||||||
|
config->rssi_offset = 0.0;
|
||||||
|
|
||||||
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:")) != -1) {
|
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:R:")) != -1) {
|
||||||
switch (option) {
|
switch (option) {
|
||||||
case 'h':
|
case 'h':
|
||||||
print_help();
|
print_help();
|
||||||
@@ -330,24 +341,38 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
|
|||||||
config->extref = true;
|
config->extref = true;
|
||||||
break;
|
break;
|
||||||
case 'f':
|
case 'f':
|
||||||
config->filler = true;
|
config->filler = Transceiver::FILLER_DUMMY;
|
||||||
break;
|
break;
|
||||||
case 'o':
|
case 'o':
|
||||||
config->offset = atof(optarg);
|
config->offset = atof(optarg);
|
||||||
break;
|
break;
|
||||||
case 's':
|
case 's':
|
||||||
config->sps = atoi(optarg);
|
config->sps = atoi(optarg);
|
||||||
if ((config->sps != 1) && (config->sps != 4)) {
|
break;
|
||||||
printf("Unsupported samples-per-symbol\n\n");
|
case 'r':
|
||||||
print_help();
|
config->rtsc = atoi(optarg);
|
||||||
exit(0);
|
config->filler = Transceiver::FILLER_RAND;
|
||||||
}
|
break;
|
||||||
|
case 'R':
|
||||||
|
config->rssi_offset = atof(optarg);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
print_help();
|
print_help();
|
||||||
exit(0);
|
exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ((config->sps != 1) && (config->sps != 4)) {
|
||||||
|
printf("Unsupported samples-per-symbol %i\n\n", config->sps);
|
||||||
|
print_help();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config->rtsc > 7) {
|
||||||
|
printf("Invalid training sequence %i\n\n", config->rtsc);
|
||||||
|
print_help();
|
||||||
|
exit(0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
@@ -391,8 +416,6 @@ int main(int argc, char *argv[])
|
|||||||
if (!trx)
|
if (!trx)
|
||||||
goto shutdown;
|
goto shutdown;
|
||||||
|
|
||||||
trx->start();
|
|
||||||
|
|
||||||
chans = trx->numChans();
|
chans = trx->numChans();
|
||||||
std::cout << "-- Transceiver active with "
|
std::cout << "-- Transceiver active with "
|
||||||
<< chans << " channel(s)" << std::endl;
|
<< chans << " channel(s)" << std::endl;
|
||||||
|
|||||||
@@ -23,32 +23,27 @@
|
|||||||
|
|
||||||
void RadioClock::set(const GSM::Time& wTime)
|
void RadioClock::set(const GSM::Time& wTime)
|
||||||
{
|
{
|
||||||
mLock.lock();
|
ScopedLock lock(mLock);
|
||||||
mClock = wTime;
|
mClock = wTime;
|
||||||
updateSignal.signal();
|
updateSignal.signal();
|
||||||
mLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioClock::incTN()
|
void RadioClock::incTN()
|
||||||
{
|
{
|
||||||
mLock.lock();
|
ScopedLock lock(mLock);
|
||||||
mClock.incTN();
|
mClock.incTN();
|
||||||
updateSignal.signal();
|
updateSignal.signal();
|
||||||
mLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
GSM::Time RadioClock::get()
|
GSM::Time RadioClock::get()
|
||||||
{
|
{
|
||||||
mLock.lock();
|
ScopedLock lock(mLock);
|
||||||
GSM::Time retVal = mClock;
|
GSM::Time retVal = mClock;
|
||||||
mLock.unlock();
|
|
||||||
|
|
||||||
return retVal;
|
return retVal;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RadioClock::wait()
|
void RadioClock::wait()
|
||||||
{
|
{
|
||||||
mLock.lock();
|
ScopedLock lock(mLock);
|
||||||
updateSignal.wait(mLock,1);
|
updateSignal.wait(mLock,1);
|
||||||
mLock.unlock();
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -106,23 +106,27 @@ double RadioInterface::fullScaleOutputValue(void) {
|
|||||||
return mRadio->fullScaleOutputValue();
|
return mRadio->fullScaleOutputValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int RadioInterface::setPowerAttenuation(int atten, size_t chan)
|
||||||
void RadioInterface::setPowerAttenuation(double atten, size_t chan)
|
|
||||||
{
|
{
|
||||||
double rfGain, digAtten;
|
double rfGain, digAtten;
|
||||||
|
|
||||||
if (chan >= mChans) {
|
if (chan >= mChans) {
|
||||||
LOG(ALERT) << "Invalid channel requested";
|
LOG(ALERT) << "Invalid channel requested";
|
||||||
return;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten, chan);
|
if (atten < 0.0)
|
||||||
digAtten = atten - mRadio->maxTxGain() + rfGain;
|
atten = 0.0;
|
||||||
|
|
||||||
|
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - (double) atten, chan);
|
||||||
|
digAtten = (double) atten - mRadio->maxTxGain() + rfGain;
|
||||||
|
|
||||||
if (digAtten < 1.0)
|
if (digAtten < 1.0)
|
||||||
powerScaling[chan] = 1.0;
|
powerScaling[chan] = 1.0;
|
||||||
else
|
else
|
||||||
powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
|
powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
|
||||||
|
|
||||||
|
return atten;
|
||||||
}
|
}
|
||||||
|
|
||||||
int RadioInterface::radioifyVector(signalVector &wVector,
|
int RadioInterface::radioifyVector(signalVector &wVector,
|
||||||
@@ -167,15 +171,23 @@ bool RadioInterface::tuneRx(double freq, size_t chan)
|
|||||||
return mRadio->setRxFreq(freq, chan);
|
return mRadio->setRxFreq(freq, chan);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool RadioInterface::start()
|
||||||
void RadioInterface::start()
|
|
||||||
{
|
{
|
||||||
LOG(INFO) << "Starting radio";
|
if (mOn)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
LOG(INFO) << "Starting radio device";
|
||||||
#ifdef USRP1
|
#ifdef USRP1
|
||||||
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
|
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
|
||||||
(void*)this);
|
(void*)this);
|
||||||
#endif
|
#endif
|
||||||
mRadio->start();
|
|
||||||
|
if (!mRadio->start())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
recvCursor = 0;
|
||||||
|
sendCursor = 0;
|
||||||
|
|
||||||
writeTimestamp = mRadio->initialWriteTimestamp();
|
writeTimestamp = mRadio->initialWriteTimestamp();
|
||||||
readTimestamp = mRadio->initialReadTimestamp();
|
readTimestamp = mRadio->initialReadTimestamp();
|
||||||
|
|
||||||
@@ -184,6 +196,23 @@ void RadioInterface::start()
|
|||||||
|
|
||||||
mOn = true;
|
mOn = true;
|
||||||
LOG(INFO) << "Radio started";
|
LOG(INFO) << "Radio started";
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Stop the radio device
|
||||||
|
*
|
||||||
|
* This is a pass-through call to the device interface. Because the underlying
|
||||||
|
* stop command issuance generally doesn't return confirmation on device status,
|
||||||
|
* this call will only return false if the device is already stopped.
|
||||||
|
*/
|
||||||
|
bool RadioInterface::stop()
|
||||||
|
{
|
||||||
|
if (!mOn || !mRadio->stop())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
mOn = false;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef USRP1
|
#ifdef USRP1
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ private:
|
|||||||
public:
|
public:
|
||||||
|
|
||||||
/** start the interface */
|
/** start the interface */
|
||||||
void start();
|
bool start();
|
||||||
|
bool stop();
|
||||||
|
|
||||||
/** intialization */
|
/** intialization */
|
||||||
virtual bool init(int type);
|
virtual bool init(int type);
|
||||||
@@ -120,7 +121,7 @@ public:
|
|||||||
/** drive reception of GSM bursts */
|
/** drive reception of GSM bursts */
|
||||||
bool driveReceiveRadio();
|
bool driveReceiveRadio();
|
||||||
|
|
||||||
void setPowerAttenuation(double atten, size_t chan = 0);
|
int setPowerAttenuation(int atten, size_t chan = 0);
|
||||||
|
|
||||||
/** returns the full-scale transmit amplitude **/
|
/** returns the full-scale transmit amplitude **/
|
||||||
double fullScaleInputValue();
|
double fullScaleInputValue();
|
||||||
|
|||||||
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include "sigProcLib.h"
|
#include "sigProcLib.h"
|
||||||
#include "GSMCommon.h"
|
#include "GSMCommon.h"
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#include "convolve.h"
|
#include "convolve.h"
|
||||||
@@ -40,6 +41,12 @@ using namespace GSM;
|
|||||||
#define TABLESIZE 1024
|
#define TABLESIZE 1024
|
||||||
#define DELAYFILTS 64
|
#define DELAYFILTS 64
|
||||||
|
|
||||||
|
/* Clipping detection threshold */
|
||||||
|
#define CLIP_THRESH 30000.0f
|
||||||
|
|
||||||
|
/* GSM 4 sps burst length */
|
||||||
|
#define GSM_BURST_LEN_4SPS 625
|
||||||
|
|
||||||
/** Lookup tables for trigonometric approximation */
|
/** Lookup tables for trigonometric approximation */
|
||||||
float cosTable[TABLESIZE+1]; // add 1 element for wrap around
|
float cosTable[TABLESIZE+1]; // add 1 element for wrap around
|
||||||
float sinTable[TABLESIZE+1];
|
float sinTable[TABLESIZE+1];
|
||||||
@@ -693,27 +700,22 @@ static signalVector *rotateBurst(const BitVector &wBurst,
|
|||||||
return shaped;
|
return shaped;
|
||||||
}
|
}
|
||||||
|
|
||||||
static signalVector *modulateBurstLaurent(const BitVector &bits,
|
/*
|
||||||
int guard_len, int sps)
|
* Laurent decomposition based GMSK modulator - 4 SPS only
|
||||||
|
*/
|
||||||
|
static signalVector *modulateBurstLaurent(const BitVector &bits)
|
||||||
{
|
{
|
||||||
int burst_len;
|
const int burst_len = GSM_BURST_LEN_4SPS;
|
||||||
|
const int sps = 4;
|
||||||
|
|
||||||
float phase;
|
float phase;
|
||||||
signalVector *c0_pulse, *c1_pulse, *c0_burst;
|
signalVector *c0_pulse, *c1_pulse, *c0_burst;
|
||||||
signalVector *c1_burst, *c0_shaped, *c1_shaped;
|
signalVector *c1_burst, *c0_shaped, *c1_shaped;
|
||||||
signalVector::iterator c0_itr, c1_itr;
|
signalVector::iterator c0_itr, c1_itr;
|
||||||
|
|
||||||
/*
|
|
||||||
* Apply before and after bits to reduce phase error at burst edges.
|
|
||||||
* Make sure there is enough room in the burst to accomodate all bits.
|
|
||||||
*/
|
|
||||||
if (guard_len < 4)
|
|
||||||
guard_len = 4;
|
|
||||||
|
|
||||||
c0_pulse = GSMPulse->c0;
|
c0_pulse = GSMPulse->c0;
|
||||||
c1_pulse = GSMPulse->c1;
|
c1_pulse = GSMPulse->c1;
|
||||||
|
|
||||||
burst_len = sps * (bits.size() + guard_len);
|
|
||||||
|
|
||||||
c0_burst = new signalVector(burst_len, c0_pulse->size());
|
c0_burst = new signalVector(burst_len, c0_pulse->size());
|
||||||
c0_burst->isReal(true);
|
c0_burst->isReal(true);
|
||||||
c0_itr = c0_burst->begin();
|
c0_itr = c0_burst->begin();
|
||||||
@@ -723,7 +725,7 @@ static signalVector *modulateBurstLaurent(const BitVector &bits,
|
|||||||
c1_itr = c1_burst->begin();
|
c1_itr = c1_burst->begin();
|
||||||
|
|
||||||
/* Padded differential start bits */
|
/* Padded differential start bits */
|
||||||
*c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
|
*c0_itr = 2.0 * (0x01 & 0x01) - 1.0;
|
||||||
c0_itr += sps;
|
c0_itr += sps;
|
||||||
|
|
||||||
/* Main burst bits */
|
/* Main burst bits */
|
||||||
@@ -822,7 +824,7 @@ signalVector *modulateBurst(const BitVector &wBurst, int guardPeriodLength,
|
|||||||
if (emptyPulse)
|
if (emptyPulse)
|
||||||
return rotateBurst(wBurst, guardPeriodLength, sps);
|
return rotateBurst(wBurst, guardPeriodLength, sps);
|
||||||
else if (sps == 4)
|
else if (sps == 4)
|
||||||
return modulateBurstLaurent(wBurst, guardPeriodLength, sps);
|
return modulateBurstLaurent(wBurst);
|
||||||
else
|
else
|
||||||
return modulateBurstBasic(wBurst, guardPeriodLength, sps);
|
return modulateBurstBasic(wBurst, guardPeriodLength, sps);
|
||||||
}
|
}
|
||||||
@@ -1281,12 +1283,12 @@ static float computePeakRatio(signalVector *corr,
|
|||||||
complex *peak;
|
complex *peak;
|
||||||
float rms, avg = 0.0;
|
float rms, avg = 0.0;
|
||||||
|
|
||||||
peak = corr->begin() + (int) rint(toa);
|
|
||||||
|
|
||||||
/* Check for bogus results */
|
/* Check for bogus results */
|
||||||
if ((toa < 0.0) || (toa > corr->size()))
|
if ((toa < 0.0) || (toa > corr->size()))
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
|
||||||
|
peak = corr->begin() + (int) rint(toa);
|
||||||
|
|
||||||
for (int i = 2 * sps; i <= 5 * sps; i++) {
|
for (int i = 2 * sps; i <= 5 * sps; i++) {
|
||||||
if (peak - i >= corr->begin()) {
|
if (peak - i >= corr->begin()) {
|
||||||
avg += (peak - i)->norm2();
|
avg += (peak - i)->norm2();
|
||||||
@@ -1369,6 +1371,19 @@ static int detectBurst(signalVector &burst,
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static float maxAmplitude(signalVector &burst)
|
||||||
|
{
|
||||||
|
float max = 0.0;
|
||||||
|
for (size_t i = 0; i < burst.size(); i++) {
|
||||||
|
if (fabs(burst[i].real()) > max)
|
||||||
|
max = fabs(burst[i].real());
|
||||||
|
if (fabs(burst[i].imag()) > max)
|
||||||
|
max = fabs(burst[i].imag());
|
||||||
|
}
|
||||||
|
|
||||||
|
return max;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* RACH burst detection
|
* RACH burst detection
|
||||||
*
|
*
|
||||||
@@ -1384,13 +1399,23 @@ int detectRACHBurst(signalVector &rxBurst,
|
|||||||
float *toa)
|
float *toa)
|
||||||
{
|
{
|
||||||
int rc, start, target, head, tail, len;
|
int rc, start, target, head, tail, len;
|
||||||
|
bool clipping = false;
|
||||||
float _toa;
|
float _toa;
|
||||||
complex _amp;
|
complex _amp;
|
||||||
signalVector *corr;
|
signalVector *corr;
|
||||||
CorrelationSequence *sync;
|
CorrelationSequence *sync;
|
||||||
|
|
||||||
if ((sps != 1) && (sps != 4))
|
if ((sps != 1) && (sps != 4))
|
||||||
return -1;
|
return -SIGERR_UNSUPPORTED;
|
||||||
|
|
||||||
|
// Detect potential clipping
|
||||||
|
// We still may be able to demod the burst, so we'll give it a try
|
||||||
|
// and only report clipping if we can't demod.
|
||||||
|
float maxAmpl = maxAmplitude(rxBurst);
|
||||||
|
if (maxAmpl > CLIP_THRESH) {
|
||||||
|
LOG(DEBUG) << "max burst amplitude: " << maxAmpl << " is above the clipping threshold: " << CLIP_THRESH << std::endl;
|
||||||
|
clipping = true;
|
||||||
|
}
|
||||||
|
|
||||||
target = 8 + 40;
|
target = 8 + 40;
|
||||||
head = 4;
|
head = 4;
|
||||||
@@ -1406,13 +1431,13 @@ int detectRACHBurst(signalVector &rxBurst,
|
|||||||
delete corr;
|
delete corr;
|
||||||
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
return -1;
|
return -SIGERR_INTERNAL;
|
||||||
} else if (!rc) {
|
} else if (!rc) {
|
||||||
if (amp)
|
if (amp)
|
||||||
*amp = 0.0f;
|
*amp = 0.0f;
|
||||||
if (toa)
|
if (toa)
|
||||||
*toa = 0.0f;
|
*toa = 0.0f;
|
||||||
return 0;
|
return clipping?-SIGERR_CLIP:SIGERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Subtract forward search bits from delay */
|
/* Subtract forward search bits from delay */
|
||||||
@@ -1437,13 +1462,23 @@ int analyzeTrafficBurst(signalVector &rxBurst, unsigned tsc, float thresh,
|
|||||||
bool chan_req, signalVector **chan, float *chan_offset)
|
bool chan_req, signalVector **chan, float *chan_offset)
|
||||||
{
|
{
|
||||||
int rc, start, target, head, tail, len;
|
int rc, start, target, head, tail, len;
|
||||||
|
bool clipping = false;
|
||||||
complex _amp;
|
complex _amp;
|
||||||
float _toa;
|
float _toa;
|
||||||
signalVector *corr;
|
signalVector *corr;
|
||||||
CorrelationSequence *sync;
|
CorrelationSequence *sync;
|
||||||
|
|
||||||
if ((tsc < 0) || (tsc > 7) || ((sps != 1) && (sps != 4)))
|
if ((tsc < 0) || (tsc > 7) || ((sps != 1) && (sps != 4)))
|
||||||
return -1;
|
return -SIGERR_UNSUPPORTED;
|
||||||
|
|
||||||
|
// Detect potential clipping
|
||||||
|
// We still may be able to demod the burst, so we'll give it a try
|
||||||
|
// and only report clipping if we can't demod.
|
||||||
|
float maxAmpl = maxAmplitude(rxBurst);
|
||||||
|
if (maxAmpl > CLIP_THRESH) {
|
||||||
|
LOG(DEBUG) << "max burst amplitude: " << maxAmpl << " is above the clipping threshold: " << CLIP_THRESH << std::endl;
|
||||||
|
clipping = true;
|
||||||
|
}
|
||||||
|
|
||||||
target = 3 + 58 + 16 + 5;
|
target = 3 + 58 + 16 + 5;
|
||||||
head = 4;
|
head = 4;
|
||||||
@@ -1459,13 +1494,13 @@ int analyzeTrafficBurst(signalVector &rxBurst, unsigned tsc, float thresh,
|
|||||||
delete corr;
|
delete corr;
|
||||||
|
|
||||||
if (rc < 0) {
|
if (rc < 0) {
|
||||||
return -1;
|
return -SIGERR_INTERNAL;
|
||||||
} else if (!rc) {
|
} else if (!rc) {
|
||||||
if (amp)
|
if (amp)
|
||||||
*amp = 0.0f;
|
*amp = 0.0f;
|
||||||
if (toa)
|
if (toa)
|
||||||
*toa = 0.0f;
|
*toa = 0.0f;
|
||||||
return 0;
|
return clipping?-SIGERR_CLIP:SIGERR_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Subtract forward search bits from delay */
|
/* Subtract forward search bits from delay */
|
||||||
|
|||||||
@@ -28,6 +28,14 @@ enum ConvType {
|
|||||||
UNDEFINED,
|
UNDEFINED,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum signalError {
|
||||||
|
SIGERR_NONE,
|
||||||
|
SIGERR_BOUNDS,
|
||||||
|
SIGERR_CLIP,
|
||||||
|
SIGERR_UNSUPPORTED,
|
||||||
|
SIGERR_INTERNAL,
|
||||||
|
};
|
||||||
|
|
||||||
/** Convert a linear number to a dB value */
|
/** Convert a linear number to a dB value */
|
||||||
float dB(float x);
|
float dB(float x);
|
||||||
|
|
||||||
|
|||||||
11
configure.ac
11
configure.ac
@@ -29,7 +29,7 @@ AC_CANONICAL_BUILD
|
|||||||
AC_CANONICAL_HOST
|
AC_CANONICAL_HOST
|
||||||
AC_CANONICAL_TARGET
|
AC_CANONICAL_TARGET
|
||||||
|
|
||||||
AM_INIT_AUTOMAKE
|
AM_INIT_AUTOMAKE([subdir-objects])
|
||||||
|
|
||||||
dnl Linux kernel KBuild style compile messages
|
dnl Linux kernel KBuild style compile messages
|
||||||
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
|
||||||
@@ -78,6 +78,11 @@ AC_ARG_WITH(neon-vfpv4, [
|
|||||||
[enable ARM NEON FMA support])
|
[enable ARM NEON FMA support])
|
||||||
])
|
])
|
||||||
|
|
||||||
|
AC_ARG_WITH(sse, [
|
||||||
|
AS_HELP_STRING([--with-sse],
|
||||||
|
[enable x86 SSE support (default)])
|
||||||
|
])
|
||||||
|
|
||||||
AS_IF([test "x$with_neon" = "xyes"], [
|
AS_IF([test "x$with_neon" = "xyes"], [
|
||||||
AC_DEFINE(HAVE_NEON, 1, Support ARM NEON)
|
AC_DEFINE(HAVE_NEON, 1, Support ARM NEON)
|
||||||
])
|
])
|
||||||
@@ -101,7 +106,9 @@ AS_IF([test "x$with_singledb" = "xyes"], [
|
|||||||
])
|
])
|
||||||
|
|
||||||
# Find and define supported SIMD extensions
|
# Find and define supported SIMD extensions
|
||||||
AX_EXT
|
AS_IF([test "x$with_sse" != "xno"], [
|
||||||
|
AX_EXT
|
||||||
|
])
|
||||||
|
|
||||||
AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])
|
AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])
|
||||||
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
|
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])
|
||||||
|
|||||||
Reference in New Issue
Block a user