Compare commits

...

32 Commits

Author SHA1 Message Date
Tom Tsou
2518186b45 sigproc: Setup downlink bursts at 156.25 duration with 4 SPS
Instead of extending 156/157 symbol sized bursts to 624/628 when 4
samples-per-symbol are used, use a fixed size of 625 samples, or
625.25 us.

This is a breaking timing change.
2015-06-05 23:40:27 -04:00
Alexander Chemeris
6512812e43 sigProcLib: Check for bogus TOA before using it. 2015-06-05 23:40:27 -04:00
Alexander Chemeris
ded68da44f Transceiver: Fix clipping detection.
There are two primary changes in this commit:

1) Return values of detect functions changed form bool to int to actually pass
the return value from the inner function and notify higher levels about clipping.
Previously the information was lost due to conversion to bool.

2) Clipping level is not the final verdict now. We still try to demod a burst
and mark it as clipped only if the level is above the clipping level AND we can't
demod it. The reasoning for this is that in real life we want to do as much as
possible to demod the burst, because we want to get as much from our dynamic
range as possible. So a little bit of clipping is fine and is expected. We just
don't want too much of it to break our demod.
2015-06-05 23:32:41 -04:00
Alexander Chemeris
37bbfa2125 Transceiver: Print noise level for each burst in debug mode. 2015-06-05 23:25:22 -04:00
Alexander Chemeris
fdbf914584 osmo-trx: Add a command line option for the dBFS to dBm offset. 2015-06-04 19:13:48 -04:00
Alexander Chemeris
bbef7e4d70 Common: Use a scoped lock in the Logger to avoid deadlock on thread cancel. 2015-06-04 19:13:48 -04:00
Alexander Chemeris
351fd76706 sigProcLib: Fix burst start phase.
R&S CMD57 complains about the start phase of bursts, particularly it shows
-15 to -30 deg of error for the bit 0.5 position (start tail bit). This patch
makes it happy. ETSI TS 145 004 section 2.2 describes this: "Before the first
bit of the bursts as defined in 3GPP TS 45.002 enters the modulator,
the modulator has an internal state as if a modulating bit stream consisting
of consecutive ones (di = 1) had entered the differential encoder."

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-06-01 16:06:11 -07:00
Alexander Chemeris
6a2bf0d87b transceiver: Drive clock indication form the receive thread.
Receive thread receives data from the device, which is a more stable source of
clocking than the transmit side. If transmit side has a hiccup, osmo-trx doesn't
send the clock indication, and transmit side is getting completely lost in time.
With this patch we ensure that clock indication keeps coming.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-06-01 16:06:02 -07:00
Alexander Chemeris
2966048b07 transceiver: Fix out-of-bounds acces in genRandNormalBurst().
We should read gTrainingSequence starting from 0 bit index, not 61 bit index.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-06-01 16:04:10 -07:00
Alexander Chemeris
f5fd578d60 osmo-trx: Fix random filler command line option.
Filler types was of "bool" type, which prevented it from taking values greter
than 1. And RAND filler type has integer value of 2, which was casted to 1 on
assigning, which led to a normal filler table being used instead of the RAND
one.
2015-05-24 20:35:13 -04:00
Alexander Chemeris
57ef87d061 Common: Log to console instead of loging to syslog by default. 2015-05-24 13:23:11 -04:00
Alexander Chemeris
5721920a08 Common: Introduce a global variable to disable syslog logging.
When we enable DEBUG logging level, syslog gets Gb's of data and can completely
exhaust the file system free space. Now we can just enable it. This is not to
say that logging to syslog it just not very useful in general.
2015-05-24 13:20:44 -04:00
Ivan Kluchnikov
194a9b1982 Transceiver52M: Change POWERON behavior to return success if the transceiver is already running, and only return fail on device
failure
2015-05-24 12:55:31 -04:00
Alexander Chemeris
1fe5282133 Transceiver: Check TSC values to be in [0..7] range.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-05-20 12:05:04 -07:00
Alexander Chemeris
4438a9fd8f Transceiver: Make error response to an unknown command on UDP command interface more understandable.
Previously we just repeated the last response which could confuse a command sender.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-05-20 12:03:30 -07:00
Tom Tsou
64ad712daa test: Add command line random normal burst option 2015-05-19 18:26:31 -07:00
Tom Tsou
5c7c178369 uhd: Pass UHD command line arguments to constructor
Previous behaviour used UHD command line args string for device search,
but did not apply the values to the device constructor. Now use the user
passed args string for both find and device construction.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-05-18 16:51:44 -07:00
Alexander Chemeris
90f7a01d1d umtrx: 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.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-05-18 16:49:24 -07:00
Alexander Chemeris
e171425b99 uhd: Set RF frontend bandwidth for UmTRX to improve signal quality.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-05-18 16:49:05 -07:00
Alexander Chemeris
4d029d8965 UmTRX: Manually set Tx gain stages for the best signal quality.
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.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-05-18 16:48:17 -07:00
Tom Tsou
6613331459 build: Provide option for disabling SSE autodetection
Setup '--with-sse' option to check system capabilities by default, but
allow disabling by the user. Selective SSE build options can be
controlled by the user by defining specific HAVE_SSE options.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-05-18 16:35:44 -07:00
Tom Tsou
577cd020c1 sigproc: Add clipping detection on RACH and TSC input
Alert user of overdriven burst input indicated by a positive
threshold detector result. This indication serves as notification
that the receive RF gain level is too high for the configured
transceiver setup.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-05-18 16:35:35 -07:00
Alexander Chemeris
88bbf1aafd uhd: Use full DAC scale with UmTRX to improve signal quality.
Signed-off-by: Tom Tsou <tom@tsou.cc>
2015-05-18 16:35:25 -07:00
Tom Tsou
2cc2ddda41 build: Add 'subdir-objects' to AM_INIT_AUTOMAKE
This will shutup automake and make it stop complaining about the
following subdirectory warnings.

"warning: source file 'common/fft.c' is in a
subdirectory, but option 'subdir-objects' is disabled"

Signed-off-by: Tom Tsou <tom@tsou.cc>
2015-05-18 16:35:13 -07:00
Tom Tsou
d7610cf0b8 radioInterface: Reset sample buffer cursors on each start
Non-zero buffer indices may lead to uplink/downlink timing offset
during repeated start/stop cycles. Mainly affects USRP2 and other
resampled devices that rely on the buffer to absorb sample block
sizes that are not multiples of the burst size.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-05-07 17:47:17 -07:00
Tom Tsou
722d4f70a4 usrp1: Update device API for frequency offset tuning
Commit 8e17df7374 "Add option for baseband frequency offset",
modified the base device API to allow for RF tuning, which was never
updated for the USRP1.

Update the implementation to match the API, however, note actual offset
in the USRP1 remains unsupported.

Signed-off-by: Tom Tsou <tom@tsou.cc>
2015-01-19 09:59:41 -08:00
Tom Tsou
93b7f37309 b210: Lock dual-channel tuning access
Frequency tuning is a multi-step process with RF and DDC/DUC protoimns
that can be corrupted if both channels attempt to tune at the same time.

Signed-off-by: Tom Tsou <tom@tsou.cc>
2014-12-15 20:25:27 -08:00
Tom Tsou
4ad9ea69ab Transceiver52M: Add X300/X310 and E310 USRP support
Treat X300 similar to N200 and resample with 100 MHz base clocking,
which provides some amount of oversampling for reduced phase error
compared to the 1 sample per symbol receiver. Treat E310 similar to 13
MHz rate devices for the lowest computational use.

Signed-off-by: Tom Tsou <tom@tsou.cc>
2014-12-15 18:59:45 -08:00
Tom Tsou
eb54bddf47 Transceiver52M: Implement POWEROFF command
Add stop and restart capability through the POWEROFF and POWERON
commands. Calling stop causes receive streaming to cease, and I/O
threads to shutdown leaving only the control handling thread running.
Upon receiving a POWERON command, I/O threads and device streaming are
restarted.

Proper shutdown of the transceiver is now initiated by the destructor,
which calls the stop command internally to wind down and deallocate
threads.

Signed-off-by: Tom Tsou <tom@tsou.cc>
2014-12-15 16:20:15 -07:00
Tom Tsou
a4d1a41244 Transceiver52M: Allow setting gain before POWERON
There is no reason gain settings should not be modifiable when the radio
is running or not.

Signed-off-by: Tom Tsou <tom@tsou.cc>
2014-12-15 16:20:15 -07:00
Tom Tsou
b999759175 CommonLibs: Add thread cancellation capability
For clean shutdown in the transceiver we need to cancel and join
running threads for orderly unwinding. Thread cancellation points
already exist, so we just need to be able to call on the threads to
exit out when stopping or shutting down.

Don't error when joining a NULL thread, which would be the case if a
thread was stopped before ever being started to begin with.

Signed-off-by: Tom Tsou <tom@tsou.cc>
2014-12-15 16:20:15 -07:00
Tom Tsou
1ae25561fa uhd: Display current timestamp with buffer status errors
Existing implementation outputs sample buffer parameters, but it is
helpful to know the submitted timestamp that led to the errant
condition.

Signed-off-by: Tom Tsou <tom@tsou.cc>
2014-12-15 16:19:38 -07:00
14 changed files with 760 additions and 370 deletions

View File

@@ -67,7 +67,8 @@ const char *levelNames[] = {
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
};
int numLevels = 8;
bool gLogToConsole = 0;
bool gLogToConsole = true;
bool gLogToSyslog = false;
FILE *gLogToFile = NULL;
Mutex gLogToLock;
@@ -196,14 +197,16 @@ Log::~Log()
if (sLoggerInited) addAlarm(mStream.str().c_str());
cerr << mStream.str() << endl;
}
// Current logging level was already checked by the macro.
// So just log.
syslog(mPriority, "%s", mStream.str().c_str());
// pat added for easy debugging.
// Current logging level was already checked by the macro. So just log.
// Log to syslog
if (gLogToSyslog) {
syslog(mPriority, "%s", mStream.str().c_str());
}
// Log to file and console
if (gLogToConsole||gLogToFile) {
int mlen = mStream.str().size();
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
gLogToLock.lock();
ScopedLock lock(gLogToLock);
if (gLogToConsole) {
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
// so just use std::cout.
@@ -215,7 +218,6 @@ Log::~Log()
if (neednl) {fputc('\n',gLogToFile);}
fflush(gLogToFile);
}
gLogToLock.unlock();
}
}
@@ -243,10 +245,9 @@ void gLogInit(const char* name, const char* level, int facility)
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:
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();
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.

View File

@@ -116,7 +116,8 @@ class Log {
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

View File

@@ -172,8 +172,15 @@ class Thread {
void start(void *(*task)(void*), void *arg);
/** 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); }
};

View File

@@ -22,6 +22,7 @@
*/
#include <stdio.h>
#include <iomanip> // std::setprecision
#include "Transceiver.h"
#include <Logger.h>
@@ -43,7 +44,7 @@ using namespace GSM;
#define NOISE_CNT 20
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++) {
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++) {
if (fill)
filler = new signalVector(*burst);
else
filler = new signalVector(burst->size());
BitVector *bits = new BitVector(148);
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,
const char *TRXAddress,
size_t wSPS, size_t wChans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface)
: mBasePort(wBasePort), mAddr(TRXAddress),
mTransmitLatency(wTransmitLatency), mClockSocket(NULL),
mRadioInterface(wRadioInterface), mSPSTx(wSPS), mSPSRx(1), mChans(wChans),
mOn(false), mTxFreq(0.0), mRxFreq(0.0), mPower(-10), mMaxExpectedDelay(0)
const char *wTRXAddress,
size_t wSPS, size_t wChans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface,
double wRssiOffset)
: mBasePort(wBasePort), mAddr(wTRXAddress),
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
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();
rxFullScale = mRadioInterface->fullScaleOutputValue();
}
Transceiver::~Transceiver()
{
stop();
sigProcLibDestroy();
delete mClockSocket;
for (size_t i = 0; i < mChans; i++) {
mControlServiceLoopThreads[i]->cancel();
mControlServiceLoopThreads[i]->join();
delete mControlServiceLoopThreads[i];
mTxPriorityQueues[i].clear();
delete mCtrlSockets[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;
signalVector *burst;
if (!mChans) {
LOG(ALERT) << "No channels assigned";
@@ -137,7 +199,6 @@ bool Transceiver::init(bool filler)
mDataSockets.resize(mChans);
mCtrlSockets.resize(mChans);
mControlServiceLoopThreads.resize(mChans);
mTxPriorityQueueServiceLoopThreads.resize(mChans);
mRxServiceLoopThreads.resize(mChans);
@@ -147,11 +208,10 @@ bool Transceiver::init(bool filler)
mStates.resize(mChans);
/* Filler table retransmissions - support only on channel 0 */
if (filler)
if (filler == FILLER_DUMMY)
mStates[0].mRetrans = true;
mClockSocket = new UDPSocket(mBasePort, mAddr.c_str(), mBasePort + 100);
/* Setup sockets */
for (size_t i = 0; i < mChans; i++) {
c_srcport = mBasePort + 2 * i + 1;
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);
}
for (size_t i = 0; i < mChans; i++) {
mControlServiceLoopThreads[i] = new Thread(32768);
mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
mRxServiceLoopThreads[i] = new Thread(32768);
/* Randomize the central clock */
GSM::Time startTime(random() % gHyperframe, 0);
mRadioInterface->getClock()->set(startTime);
mTransmitDeadlineClock = startTime;
mLastClockUpdateTime = startTime;
mLatencyUpdateTime = startTime;
for (size_t n = 0; n < 8; n++) {
burst = modulateBurst(gDummyBurst, 8 + (n % 4 == 0), mSPSTx);
scaleVector(*burst, txFullScale);
mStates[i].init(n, burst, filler && !i);
delete burst;
}
/* Start control threads */
for (size_t i = 0; i < mChans; i++) {
TransceiverChannel *chan = new TransceiverChannel(this, i);
mControlServiceLoopThreads[i] = new Thread(32768);
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;
}
/*
* 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,
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
* is used or available on the RACH channel.
*/
bool Transceiver::detectRACH(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa)
int Transceiver::detectRACH(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa)
{
float threshold = 6.0;
@@ -381,9 +548,10 @@ bool Transceiver::detectRACH(TransceiverState *state,
* state information and channel estimate if necessary. Equalization
* is currently disabled.
*/
bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
complex &amp, float &toa, GSM::Time &time)
int Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
complex &amp, float &toa, GSM::Time &time)
{
int success;
int tn = time.TN();
float chanOffset, threshold = 5.0;
bool noise, needDFE = false, estimateChan = false;
@@ -401,10 +569,11 @@ bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
}
/* Detect normal burst midambles */
if (!analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, &amp,
&toa, mMaxExpectedDelay, estimateChan,
&chanResp, &chanOffset)) {
return false;
success = analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, &amp,
&toa, mMaxExpectedDelay, estimateChan,
&chanResp, &chanOffset);
if (success <= 0) {
return success;
}
noise = state->mNoiseLev;
@@ -424,7 +593,7 @@ bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
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
* and burst correlation type. Equalzation is currently disabled.
*/
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
int &timingOffset, size_t chan)
SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, double &RSSI,
double &timingOffset, double &noise,
size_t chan)
{
bool success, equalize = false;
int success;
bool equalize = false;
complex amp;
float toa, pow, max = -1.0, avg = 0.0;
int max_i = -1;
@@ -503,8 +674,16 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
else
success = detectRACH(state, *burst, amp, toa);
if (!success) {
state->mNoises.insert(avg);
/* Update noise average if no bust detected or alert on error */
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;
return NULL;
}
@@ -517,25 +696,15 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
wTime = time;
RSSI = (int) floor(20.0 * log10(rxFullScale / avg));
timingOffset = (int) round(toa * 256.0 / mSPSRx);
RSSI = 20.0 * log10(rxFullScale / avg);
timingOffset = toa / mSPSRx;
noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
delete radio_burst;
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()
{
for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
@@ -574,40 +743,14 @@ void Transceiver::driveControl(size_t chan)
LOG(INFO) << "command is " << buffer;
if (strcmp(command,"POWEROFF")==0) {
// turn off transmitter/demod
sprintf(response,"RSP POWEROFF 0");
stop();
sprintf(response,"RSP POWEROFF 0");
}
else if (strcmp(command,"POWERON")==0) {
// turn on transmitter/demod
if (!mTxFreq || !mRxFreq)
if (!start())
sprintf(response,"RSP POWERON 1");
else {
else
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) {
//set expected maximum time-of-arrival
@@ -634,28 +777,19 @@ void Transceiver::driveControl(size_t chan)
}
}
else if (!strcmp(command, "SETPOWER")) {
// set output power in dB
int dbPwr;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbPwr);
if (!mOn)
sprintf(response, "RSP SETPOWER 1 %d", dbPwr);
else {
mPower = dbPwr;
mRadioInterface->setPowerAttenuation(mPower, chan);
sprintf(response, "RSP SETPOWER 0 %d", dbPwr);
}
int power;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &power);
power = mRadioInterface->setPowerAttenuation(power, chan);
mStates[chan].mPower = power;
sprintf(response, "RSP SETPOWER 0 %d", power);
}
else if (!strcmp(command,"ADJPOWER")) {
// adjust power in dB steps
int dbStep;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbStep);
if (!mOn)
sprintf(response, "RSP ADJPOWER 1 %d", mPower);
else {
mPower += dbStep;
mRadioInterface->setPowerAttenuation(mPower, chan);
sprintf(response, "RSP ADJPOWER 0 %d", mPower);
}
int power, step;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &step);
power = mStates[chan].mPower + step;
power = mRadioInterface->setPowerAttenuation(power, chan);
mStates[chan].mPower = power;
sprintf(response, "RSP ADJPOWER 0 %d", power);
}
else if (strcmp(command,"RXTUNE")==0) {
// tune receiver
@@ -685,7 +819,7 @@ void Transceiver::driveControl(size_t chan)
// set TSC
unsigned TSC;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC);
if (mOn)
if (mOn || (TSC < 0) || (TSC > 7))
sprintf(response, "RSP SETTSC 1 %d", TSC);
else if (chan && (TSC != mTSC))
sprintf(response, "RSP SETTSC 1 %d", TSC);
@@ -696,7 +830,7 @@ void Transceiver::driveControl(size_t chan)
}
}
else if (strcmp(command,"SETSLOT")==0) {
// set TSC
// set slot type
int corrCode;
int timeslot;
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&timeslot,&corrCode);
@@ -712,6 +846,7 @@ void Transceiver::driveControl(size_t chan)
}
else {
LOG(WARNING) << "bogus command " << command << " on control interface.";
sprintf(response,"RSP ERR 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++)
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);
int RSSI = (int) buffer[5];
@@ -763,34 +889,44 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
void Transceiver::driveReceiveRadio()
{
if (!mRadioInterface->driveReceiveRadio())
if (!mRadioInterface->driveReceiveRadio()) {
usleep(100000);
} else {
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
writeClockInterface();
}
}
void Transceiver::driveReceiveFIFO(size_t chan)
{
SoftVector *rxBurst = NULL;
int RSSI;
int TOA; // in 1/256 of a symbol
double RSSI; // in dBFS
double dBm; // in dBm
double TOA; // in symbols
int TOAint; // in 1/256 symbols
double noise; // noise level in dBFS
GSM::Time burstTime;
rxBurst = pullRadioVector(burstTime, RSSI, TOA, chan);
rxBurst = pullRadioVector(burstTime, RSSI, TOA, noise, chan);
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];
burstString[0] = burstTime.TN();
for (int i = 0; i < 4; i++)
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
burstString[5] = RSSI;
burstString[6] = (TOA >> 8) & 0x0ff;
burstString[7] = TOA & 0x0ff;
burstString[5] = (int)dBm;
burstString[6] = (TOAint >> 8) & 0x0ff;
burstString[7] = TOAint & 0x0ff;
SoftVector::iterator burstItr = rxBurst->begin();
for (unsigned int i = 0; i < gSlotLen; i++) {
@@ -865,7 +1001,7 @@ void Transceiver::writeClockInterface()
LOG(INFO) << "ClockInterface: sending " << command;
mClockSocket->write(command, strlen(command) + 1);
mClockSocket.write(command, strlen(command) + 1);
mLastClockUpdateTime = mTransmitDeadlineClock;
@@ -933,15 +1069,7 @@ void *TxUpperLoopAdapter(TransceiverChannel *chan)
trx->setPriority(0.40);
while (1) {
bool stale = false;
// 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();
}
trx->driveTxPriorityQueue(num);
pthread_testcancel();
}
return NULL;

View File

@@ -54,7 +54,7 @@ struct TransceiverState {
~TransceiverState();
/* 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];
@@ -81,98 +81,14 @@ struct TransceiverState {
/* Received noise energy levels */
float mNoiseLev;
noiseVector mNoises;
/* Shadowed downlink attenuation */
int mPower;
};
/** The Transceiver class, responsible for physical layer of basestation */
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 &amp, float &toa);
/** Detect normal bursts */
bool detectTSC(TransceiverState *state,
signalVector &burst,
complex &amp, 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:
/** Transceiver constructor
@param wBasePort base port number of UDP sockets
@param TRXAddress IP address of the TRX manager, as a string
@@ -181,17 +97,17 @@ public:
@param radioInterface associated radioInterface object
*/
Transceiver(int wBasePort,
const char *TRXAddress,
size_t wSPS, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface);
const char *TRXAddress,
size_t wSPS, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface,
double wRssiOffset);
/** Destructor */
~Transceiver();
/** start the Transceiver */
void start();
bool init(bool filler);
/** Start the control loop */
bool init(int filler, size_t rtsc);
/** attach the radioInterface receive FIFO */
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
@@ -226,6 +142,106 @@ public:
LOOPBACK ///< similar go VII, used in loopback testing
} 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 &amp, float &toa);
/** Detect normal bursts */
int detectTSC(TransceiverState *state,
signalVector &burst,
complex &amp, 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:
/** drive lower receive I/O and burst generation */
void driveReceiveRadio();

View File

@@ -34,12 +34,25 @@
#define B2XX_CLK_RT 26e6
#define E1XX_CLK_RT 52e6
#define B2XX_BASE_RT GSMRATE
#define B100_BASE_RT 400000
#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)
/*
* 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 {
USRP1,
USRP2,
@@ -47,6 +60,8 @@ enum uhd_dev_type {
B200,
B210,
E1XX,
E3XX,
X3XX,
UMTRX,
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" },
{ E1XX, 1, 9.5192e-5, "E1XX 1 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, 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,
int sps, bool diversity = false)
{
struct uhd_dev_offset *offset;
struct uhd_dev_offset *offset = NULL;
/* Reject USRP1 */
if (type == USRP1) {
@@ -121,17 +140,21 @@ static double get_dev_offset(enum uhd_dev_type type,
offset = &special_offsets[1];
}
} else {
/* Normal operation */
switch (sps) {
case 1:
offset = &uhd_offsets[2 * type + 0];
break;
case 4:
default:
offset = &uhd_offsets[2 * type + 1];
/* Search for matching offset value */
for (int i = 0; i < NUM_USRP_TYPES * 2; i++) {
if ((type == uhd_offsets[i].type) &&
(sps == uhd_offsets[i].sps)) {
offset = &uhd_offsets[i];
break;
}
}
}
if (!offset) {
LOG(ERR) << "Invalid device configuration";
return 0.0;
}
std::cout << "-- Setting " << offset->desc << std::endl;
return offset->offset;
@@ -156,12 +179,14 @@ static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
switch (type) {
case USRP2:
case X3XX:
return USRP2_BASE_RT * sps;
case B100:
return B100_BASE_RT * sps;
case B200:
case B210:
case E1XX:
case E3XX:
case UMTRX:
return GSMRATE * sps;
default:
@@ -225,7 +250,7 @@ public:
/** Buffer status string
@return a formatted string describing internal buffer state
*/
std::string str_status() const;
std::string str_status(size_t ts) const;
/** Formatted error string
@param code an error code
@@ -268,7 +293,7 @@ public:
int open(const std::string &args, bool extref);
bool start();
bool stop();
void restart();
bool restart();
void setPriority(float prio);
enum TxWindowType getWindowType() { return tx_window; }
@@ -286,8 +311,8 @@ public:
inline TIMESTAMP initialWriteTimestamp() { return ts_initial * sps; }
inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
inline double fullScaleInputValue() { return 32000 * TX_AMPL; }
inline double fullScaleOutputValue() { return 32000; }
double fullScaleInputValue();
double fullScaleOutputValue();
double setRxGain(double db, size_t chan);
double getRxGain(size_t chan);
@@ -313,8 +338,9 @@ public:
enum err_code {
ERROR_TIMING = -1,
ERROR_UNRECOVERABLE = -2,
ERROR_UNHANDLED = -3,
ERROR_TIMEOUT = -2,
ERROR_UNRECOVERABLE = -3,
ERROR_UNHANDLED = -4,
};
private:
@@ -358,8 +384,9 @@ private:
uhd::tune_request_t select_freq(double wFreq, 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;
Mutex tune_lock;
};
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)
: tx_gain_min(0.0), tx_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;
range = usrp_dev->get_tx_gain_range();
tx_gain_min = range.start();
tx_gain_max = range.stop();
if (dev_type == UMTRX) {
std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
if (gain_stages[0] == "VGA") {
LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
}
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();
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;
/* 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)
return -1;
}
@@ -514,7 +563,23 @@ double uhd_device::setTxGain(double db, size_t chan)
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);
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;
uhd::property_tree::sptr prop_tree;
size_t usrp1_str, usrp2_str, e100_str, e110_str,
b100_str, b200_str, b210_str, umtrx_str;
size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str,
b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str;
prop_tree = usrp_dev->get_device()->get_tree();
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");
e100_str = mboard_str.find("E100");
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");
if (usrp1_str != std::string::npos) {
@@ -598,6 +666,15 @@ bool uhd_device::parse_dev_type()
} else if (usrp2_str != std::string::npos) {
tx_window = TX_WINDOW_FIXED;
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) {
tx_window = TX_WINDOW_FIXED;
dev_type = UMTRX;
@@ -630,9 +707,9 @@ int uhd_device::open(const std::string &args, bool extref)
// Use the first found device
LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
try {
usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
usrp_dev = uhd::usrp::multi_usrp::make(addr);
} catch(...) {
LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
LOG(ALERT) << "UHD make failed, device " << args;
return -1;
}
@@ -673,6 +750,16 @@ int uhd_device::open(const std::string &args, bool extref)
if (set_rates(_tx_rate, _rx_rate) < 0)
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 */
uhd::stream_args_t stream_args("sc16");
for (size_t i = 0; i < chans; i++)
@@ -712,10 +799,12 @@ int uhd_device::open(const std::string &args, bool extref)
case B100:
return RESAMP_64M;
case USRP2:
case X3XX:
return RESAMP_100M;
case B200:
case B210:
case E1XX:
case E3XX:
default:
break;
}
@@ -727,7 +816,7 @@ bool uhd_device::flush_recv(size_t num_pkts)
{
uhd::rx_metadata_t md;
size_t num_smpls;
float timeout = 0.1f;
float timeout = UHD_RESTART_TIMEOUT;
std::vector<std::vector<short> >
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) {
switch (md.error_code) {
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
LOG(ALERT) << "Device timed out";
return false;
default:
continue;
}
@@ -756,7 +847,7 @@ bool uhd_device::flush_recv(size_t num_pkts)
return true;
}
void uhd_device::restart()
bool uhd_device::restart()
{
/* Allow 100 ms delay to align multi-channel streams */
double delay = 0.1;
@@ -771,7 +862,7 @@ void uhd_device::restart()
usrp_dev->issue_stream_cmd(cmd);
flush_recv(1);
return flush_recv(10);
}
bool uhd_device::start()
@@ -787,10 +878,12 @@ bool uhd_device::start()
uhd::msg::register_handler(&uhd_msg_handler);
// 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
restart();
if (!restart())
return false;
// Display usrp time
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);
async_event_thrd->cancel();
async_event_thrd->join();
delete async_event_thrd;
started = false;
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) {
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
LOG(ALERT) << "UHD: Receive timed out";
return ERROR_TIMEOUT;
case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
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);
if (rc < 0) {
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;
}
@@ -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
while (rx_buffers[0]->avail_smpls(timestamp) < len) {
thread_enable_cancel(false);
size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
metadata, 0.1, true);
thread_enable_cancel(true);
rx_pkt_cnt++;
// 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: Unrecoverable error, exiting...";
exit(-1);
case ERROR_TIMEOUT:
// Assume stopping condition
return 0;
case ERROR_TIMING:
restart();
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
if ((rc < 0)) {
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)
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);
if ((rc < 0) || (rc != len)) {
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;
}
}
@@ -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);
thread_enable_cancel(true);
if (num_smpls != (unsigned) len) {
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;
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)
return treq;
@@ -1087,6 +1206,7 @@ bool uhd_device::setTxFreq(double wFreq, size_t chan)
LOG(ALERT) << "Requested non-existent channel " << chan;
return false;
}
ScopedLock lock(tune_lock);
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;
return false;
}
ScopedLock lock(tune_lock);
return set_freq(wFreq, chan, false);
}
@@ -1121,10 +1242,27 @@ double uhd_device::getRxFreq(size_t 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()
{
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;
// 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));
}
std::string smpl_buf::str_status() const
std::string smpl_buf::str_status(size_t ts) const
{
std::ostringstream ost("Sample buffer: ");
ost << "length = " << buf_len;
ost << "timestamp = " << ts;
ost << ", length = " << buf_len;
ost << ", time_start = " << time_start;
ost << ", time_end = " << time_end;
ost << ", data_start = " << data_start;

View File

@@ -600,7 +600,7 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
bool USRPDevice::setRxFreq(double wFreq) { return true;};
#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);
}

View File

@@ -65,10 +65,12 @@ struct trx_config {
unsigned port;
unsigned sps;
unsigned chans;
unsigned rtsc;
bool extref;
bool filler;
Transceiver::FillerType filler;
bool diversity;
double offset;
double rssi_offset;
};
ConfigurationTable gConfig;
@@ -154,19 +156,23 @@ bool trx_setup_config(struct trx_config *config)
config->diversity = DEFAULT_DIVERSITY;
}
if (!config->sps)
config->sps = DEFAULT_SPS;
if (!config->chans)
config->chans = DEFAULT_CHANS;
/* Diversity only supported on 2 channels */
if (config->diversity)
config->chans = 2;
refstr = config->extref ? "Enabled" : "Disabled";
fillstr = config->filler ? "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("");
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 << " Diversity............... " << divstr << std::endl;
ost << " Tuning offset........... " << config->offset << std::endl;
ost << " RSSI to dBm offset...... " << config->rssi_offset << std::endl;
std::cout << ost << std::endl;
return true;
@@ -235,8 +242,8 @@ Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
VectorFIFO *fifo;
trx = new Transceiver(config->port, config->addr.c_str(), config->sps,
config->chans, GSM::Time(3,0), radio);
if (!trx->init(config->filler)) {
config->chans, GSM::Time(3,0), radio, config->rssi_offset);
if (!trx->init(config->filler, config->rtsc)) {
LOG(ALERT) << "Failed to initialize transceiver";
delete trx;
return NULL;
@@ -286,7 +293,9 @@ static void print_help()
" -s Samples-per-symbol (1 or 4)\n"
" -c Number of ARFCN channels (default=1)\n"
" -f Enable C0 filler table\n"
" -o Set baseband frequency offset (default=auto)\n",
" -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");
}
@@ -295,14 +304,16 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
int option;
config->port = 0;
config->sps = 0;
config->chans = 0;
config->sps = DEFAULT_SPS;
config->chans = DEFAULT_CHANS;
config->rtsc = 0;
config->extref = false;
config->filler = false;
config->filler = Transceiver::FILLER_ZERO;
config->diversity = false;
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) {
case 'h':
print_help();
@@ -330,24 +341,38 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
config->extref = true;
break;
case 'f':
config->filler = true;
config->filler = Transceiver::FILLER_DUMMY;
break;
case 'o':
config->offset = atof(optarg);
break;
case 's':
config->sps = atoi(optarg);
if ((config->sps != 1) && (config->sps != 4)) {
printf("Unsupported samples-per-symbol\n\n");
print_help();
exit(0);
}
break;
case 'r':
config->rtsc = atoi(optarg);
config->filler = Transceiver::FILLER_RAND;
break;
case 'R':
config->rssi_offset = atof(optarg);
break;
default:
print_help();
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[])
@@ -391,8 +416,6 @@ int main(int argc, char *argv[])
if (!trx)
goto shutdown;
trx->start();
chans = trx->numChans();
std::cout << "-- Transceiver active with "
<< chans << " channel(s)" << std::endl;

View File

@@ -23,32 +23,27 @@
void RadioClock::set(const GSM::Time& wTime)
{
mLock.lock();
ScopedLock lock(mLock);
mClock = wTime;
updateSignal.signal();
mLock.unlock();
}
void RadioClock::incTN()
{
mLock.lock();
ScopedLock lock(mLock);
mClock.incTN();
updateSignal.signal();
mLock.unlock();
}
GSM::Time RadioClock::get()
{
mLock.lock();
ScopedLock lock(mLock);
GSM::Time retVal = mClock;
mLock.unlock();
return retVal;
}
void RadioClock::wait()
{
mLock.lock();
ScopedLock lock(mLock);
updateSignal.wait(mLock,1);
mLock.unlock();
}

View File

@@ -106,23 +106,27 @@ double RadioInterface::fullScaleOutputValue(void) {
return mRadio->fullScaleOutputValue();
}
void RadioInterface::setPowerAttenuation(double atten, size_t chan)
int RadioInterface::setPowerAttenuation(int atten, size_t chan)
{
double rfGain, digAtten;
if (chan >= mChans) {
LOG(ALERT) << "Invalid channel requested";
return;
return -1;
}
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten, chan);
digAtten = atten - mRadio->maxTxGain() + rfGain;
if (atten < 0.0)
atten = 0.0;
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - (double) atten, chan);
digAtten = (double) atten - mRadio->maxTxGain() + rfGain;
if (digAtten < 1.0)
powerScaling[chan] = 1.0;
else
powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
return atten;
}
int RadioInterface::radioifyVector(signalVector &wVector,
@@ -167,15 +171,23 @@ bool RadioInterface::tuneRx(double freq, size_t chan)
return mRadio->setRxFreq(freq, chan);
}
void RadioInterface::start()
bool RadioInterface::start()
{
LOG(INFO) << "Starting radio";
if (mOn)
return true;
LOG(INFO) << "Starting radio device";
#ifdef USRP1
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
(void*)this);
#endif
mRadio->start();
if (!mRadio->start())
return false;
recvCursor = 0;
sendCursor = 0;
writeTimestamp = mRadio->initialWriteTimestamp();
readTimestamp = mRadio->initialReadTimestamp();
@@ -184,6 +196,23 @@ void RadioInterface::start()
mOn = true;
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

View File

@@ -78,7 +78,8 @@ private:
public:
/** start the interface */
void start();
bool start();
bool stop();
/** intialization */
virtual bool init(int type);
@@ -120,7 +121,7 @@ public:
/** drive reception of GSM bursts */
bool driveReceiveRadio();
void setPowerAttenuation(double atten, size_t chan = 0);
int setPowerAttenuation(int atten, size_t chan = 0);
/** returns the full-scale transmit amplitude **/
double fullScaleInputValue();

View File

@@ -28,6 +28,7 @@
#include "sigProcLib.h"
#include "GSMCommon.h"
#include "Logger.h"
extern "C" {
#include "convolve.h"
@@ -40,6 +41,12 @@ using namespace GSM;
#define TABLESIZE 1024
#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 */
float cosTable[TABLESIZE+1]; // add 1 element for wrap around
float sinTable[TABLESIZE+1];
@@ -693,27 +700,22 @@ static signalVector *rotateBurst(const BitVector &wBurst,
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;
signalVector *c0_pulse, *c1_pulse, *c0_burst;
signalVector *c1_burst, *c0_shaped, *c1_shaped;
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;
c1_pulse = GSMPulse->c1;
burst_len = sps * (bits.size() + guard_len);
c0_burst = new signalVector(burst_len, c0_pulse->size());
c0_burst->isReal(true);
c0_itr = c0_burst->begin();
@@ -723,7 +725,7 @@ static signalVector *modulateBurstLaurent(const BitVector &bits,
c1_itr = c1_burst->begin();
/* Padded differential start bits */
*c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
*c0_itr = 2.0 * (0x01 & 0x01) - 1.0;
c0_itr += sps;
/* Main burst bits */
@@ -822,7 +824,7 @@ signalVector *modulateBurst(const BitVector &wBurst, int guardPeriodLength,
if (emptyPulse)
return rotateBurst(wBurst, guardPeriodLength, sps);
else if (sps == 4)
return modulateBurstLaurent(wBurst, guardPeriodLength, sps);
return modulateBurstLaurent(wBurst);
else
return modulateBurstBasic(wBurst, guardPeriodLength, sps);
}
@@ -1281,12 +1283,12 @@ static float computePeakRatio(signalVector *corr,
complex *peak;
float rms, avg = 0.0;
peak = corr->begin() + (int) rint(toa);
/* Check for bogus results */
if ((toa < 0.0) || (toa > corr->size()))
return 0.0;
peak = corr->begin() + (int) rint(toa);
for (int i = 2 * sps; i <= 5 * sps; i++) {
if (peak - i >= corr->begin()) {
avg += (peak - i)->norm2();
@@ -1369,6 +1371,19 @@ static int detectBurst(signalVector &burst,
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
*
@@ -1384,13 +1399,23 @@ int detectRACHBurst(signalVector &rxBurst,
float *toa)
{
int rc, start, target, head, tail, len;
bool clipping = false;
float _toa;
complex _amp;
signalVector *corr;
CorrelationSequence *sync;
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;
head = 4;
@@ -1406,13 +1431,13 @@ int detectRACHBurst(signalVector &rxBurst,
delete corr;
if (rc < 0) {
return -1;
return -SIGERR_INTERNAL;
} else if (!rc) {
if (amp)
*amp = 0.0f;
if (toa)
*toa = 0.0f;
return 0;
return clipping?-SIGERR_CLIP:SIGERR_NONE;
}
/* 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)
{
int rc, start, target, head, tail, len;
bool clipping = false;
complex _amp;
float _toa;
signalVector *corr;
CorrelationSequence *sync;
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;
head = 4;
@@ -1459,13 +1494,13 @@ int analyzeTrafficBurst(signalVector &rxBurst, unsigned tsc, float thresh,
delete corr;
if (rc < 0) {
return -1;
return -SIGERR_INTERNAL;
} else if (!rc) {
if (amp)
*amp = 0.0f;
if (toa)
*toa = 0.0f;
return 0;
return clipping?-SIGERR_CLIP:SIGERR_NONE;
}
/* Subtract forward search bits from delay */

View File

@@ -28,6 +28,14 @@ enum ConvType {
UNDEFINED,
};
enum signalError {
SIGERR_NONE,
SIGERR_BOUNDS,
SIGERR_CLIP,
SIGERR_UNSUPPORTED,
SIGERR_INTERNAL,
};
/** Convert a linear number to a dB value */
float dB(float x);

View File

@@ -29,7 +29,7 @@ AC_CANONICAL_BUILD
AC_CANONICAL_HOST
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE
AM_INIT_AUTOMAKE([subdir-objects])
dnl Linux kernel KBuild style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -78,6 +78,11 @@ AC_ARG_WITH(neon-vfpv4, [
[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"], [
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
AX_EXT
AS_IF([test "x$with_sse" != "xno"], [
AX_EXT
])
AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])