Compare commits

...

14 Commits

Author SHA1 Message Date
Alexander Chemeris
ff9b59c223 Transceiver: Add an option to send RSSI to the GSM core even if the burst is not demodulated.
The feature is enabled when the GSM core sends "SENDEMPTY 1" command.
2015-06-10 23:14:11 -04:00
Alexander Chemeris
34e5a3807f Transceiver: Do not update state->SNRestimate if equalization is disabled.
This also fix a bug of using bool type for noise instead of float.
2015-06-10 23:13:33 -04:00
Alexander Chemeris
b49874aa64 uhd: Fix rounding error in timestamp conversion functions.
Rounding error introduced oscilating timing advance error by regularly
overwriting one bit and then skipping one bit.

This commit also adds an error message to show up in logs if this ever
happens again.
2015-06-10 23:13:33 -04:00
Alexander Chemeris
030951695c sigProcLib: Abstract out common part of Normal/RACH burst detection.
As a side change - get rid of passing toa and amp arguments as pointers and use
references instead.

The commit doesn't change behaviour, but makes the code cleaner.
2015-06-10 23:13:33 -04:00
Alexander Chemeris
b721d6104d Checking in clockdump.sh utility. 2015-06-10 23:03:00 -04:00
Alexander Chemeris
c19d1f6c36 Checking in a more relevant README.
This READMY is from the OpenBTS's TRXManager and actually describes the transceiver
API and behavior.
2015-06-10 23:03:00 -04:00
Alexander Chemeris
f0d8a581b4 Transceiver: Update noise level only when the timeslot is marked as IDLE.
We can't rely on an assumption that if we can't decode a burst - it's noise.
There are many rasons why we can't decode a burst even if it's well above the
noise level. Just one example is a RACH burst which can be overlapped with
another RACH burst up to a level both are completely unrecognizable. Another
example is when a burst is destroyed by bad multi-path.
2015-06-10 23:03:00 -04:00
Alexander Chemeris
37b445d4c8 transceiver: Remove noise/RSSI gating.
It does more harm than good. the current noise calculation is too error
prone, so we can't trust it. And we end up loosing perfectly good bursts
because of that.
2015-06-07 01:10:11 -04:00
Alexander Chemeris
df127bc74e osmo-trx: Add an option to swap channels on UmTRX. 2015-06-07 01:07:45 -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
12 changed files with 378 additions and 332 deletions

View File

@@ -206,7 +206,7 @@ Log::~Log()
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.
@@ -218,7 +218,6 @@ Log::~Log()
if (neednl) {fputc('\n',gLogToFile);}
fflush(gLogToFile);
}
gLogToLock.unlock();
}
}

260
README
View File

@@ -1,168 +1,116 @@
Welcome to the OpenBTS source code.
This is the interface to the transcevier.
For free support, please subscribe to openbts-discuss@lists.sourceforge.net.
See http://sourceforge.net/mailarchive/forum.php?forum_name=openbts-discuss
and https://lists.sourceforge.net/lists/listinfo/openbts-discuss for details.
For additional information, refer to http://openbts.org.
These are the directories:
AsteriskConfig Asterisk configuration files for use with OpenBTS.
CommonLib Common-use libraries, mostly C++ wrappers for basic facilities.
Control Control-layer functions for the protocols of GSM 04.08 and SIP.
GSM The GSM stack.
SIP Components of the SIP state machines ued by the control layer.
SMS The SMS stack.
SR The subscriber registry.
TRXManager The interface between the GSM stack and the radio.
Transceiver The software transceiver and specific installation tests.
apps OpenBTS application binaries.
doc Project documentation.
tests Test fixtures for subsets of OpenBTS components.
smqueue RFC-3428 store-and-forward server for SMS
Each TRX Manager UDP socket interface represents a single ARFCN.
Each of these per-ARFCN interfaces is a pair of UDP sockets, one for control and one for data.
Give a base port B (5700), the master clock interface is at port P=B.
The TRX-side control interface for C(N) is on port P=B+2N+1 and the data interface is on an odd numbered port P=B+2N+2.
The corresponding core-side interface for every socket is at P+100.
For any given build, the number of ARFCN interfaces can be fixed.
By default, OpenBTS assumes the following UDP port assignments:
Indications on the Master Clock Interface
5060 -- Asterisk SIP interface
5061 -- local SIP softphone
5062 -- OpenBTS SIP interface
5063 -- smqueue SIP interface
5064 -- subscriber registry SIP interface
5700-range -- OpenBTS-transceiver interface
The master clock interface is output only (from the radio).
Messages are "indications".
These can be controlled in the CONFIG table in /etc/OpenBTS.db.
CLOCK gives the current value of the transceiver clock to be used by the core.
This message is sent whenever a trasmission packet arrives that is too late or too early. The clock value is NOT the current transceiver time. It is a time setting the the core should use to give better packet arrival times.
IND CLOCK <totalFrames>
Standrd paths:
/OpenBTS -- Binary installation.
/etc/OpenBTS -- Configuration databases.
/var/run/OpenBTS -- Real-time reporting databases.
The script apps/setUpFiles.sh will create these directories and install the
correct files in them.
Commands on the Per-ARFCN Control Interface
The per-ARFCN control interface uses a command-reponse protocol.
Commands are NULL-terminated ASCII strings, one per UDP socket.
Each command has a corresponding response.
Every command is of the form:
CMD <cmdtype> [params]
The <cmdtype> is the actual command.
Parameters are optional depending on the commands type.
Every response is of the form:
RSP <cmdtype> <status> [result]
The <status> is 0 for success and a non-zero error code for failure.
Successful responses may include results, depending on the command type.
Power Control
POWEROFF shuts off transmitter power and stops the demodulator.
CMD POWEROFF
RSP POWEROFF <status>
POWERON starts the transmitter and starts the demodulator. Initial power level is very low.
This command fails if the transmitter and receiver are not yet tuned.
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
If the transceiver is already on, it response with success to this command.
CMD POWERON
RSP POWERON <status>
SETPOWER sets output power in dB wrt full scale.
This command fails if the transmitter and receiver are not running.
CMD SETPOWER <dB>
RSP SETPOWER <status> <dB>
ADJPOWER adjusts power by the given dB step. Response returns resulting power level wrt full scale.
This command fails if the transmitter and receiver are not running.
CMD ADJPOWER <dBStep>
RSP ADJPOWER <status> <dBLevel>
Tuning Control
RXTUNE tunes the receiver to a given frequency in kHz.
This command fails if the receiver is already running.
(To re-tune you stop the radio, re-tune, and restart.)
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
CMD RXTUNE <kHz>
RSP RXTUNE <status> <kHz>
TXTUNE tunes the transmitter to a given frequency in kHz.
This command fails if the transmitter is already running.
(To re-tune you stop the radio, re-tune, and restart.)
This command fails if the transmit or receive frequency creates a conflict with another ARFCN that is already runnng.
CMD TXTUNE <kHz>
RSP TXTUNE <status> <kHz>
Timeslot Control
SETSLOT sets the format of the uplink timeslots in the ARFCN.
The <timeslot> indicates the timeslot of interest.
The <chantype> indicates the type of channel that occupies the timeslot.
A chantype of zero indicates the timeslot is off.
CMD SETSLOT <timeslot> <chantype>
RSP SETSLOT <status> <timeslot> <chantype>
Messages on the per-ARFCN Data Interface
Messages on the data interface carry one radio burst per UDP message.
Received Data Burst
1 byte timeslot index
4 bytes GSM frame number, big endian
1 byte RSSI in -dBm
2 bytes correlator timing offset in 1/256 symbol steps, 2's-comp, big endian
148 bytes soft symbol estimates, 0 -> definite "0", 255 -> definite "1"
Transmit Data Burst
1 byte timeslot index
4 bytes GSM frame number, big endian
1 byte transmit level wrt ARFCN max, -dB (attenuation)
148 bytes output symbol values, 0 & 1
Release history:
Release Name SVN Reposiory SVN Rev Comments
1.0 (none) SF.net ?? completed L1, L2
1.1 Arnaudville GNU Radio r10019 (trunk)
1.2 Breaux Bridge GNU Radio r10088 (trunk) GNU Build, very early assignment
1.3 Carencro KSP r1 (trunk) first post-injunction release
1.4 Donaldsonville KSP r23 (trunk) fixed Ubuntu build error
1.5 Eunice KSP r39 (trunk) fixed L2 bugs related to segmentation
removed incomplete SMS directory
moved "abort" calls into L3 subclasses
1.6 New Iberia KSP r130 (trunk) import of all 2.2 improvements to non-SMS release
2.0 St. Francisville KSP r54 (smswork) SMS support
file-based configuration
2.1 Grand Coteau KSP r70 (smswork) DTMF support
fixed more Linux-related build errors
-lpthread
TLMessage constructor
expanded stack to prevent overflows in Linux
moved gSIPInterface to main app
fixed iterator bug in Pager
2.2 Houma KSP r122 (smswork) added LEGAL notice
removed Assert classes
stop paging on page response
fixed Pager-spin bug
fixed Transceiver spin bugs
fixed 2^32 microsecond rollover bug
reduced stack footprints in Transceiver
fixed SMS timestamps
check LAI before using TMSI in LUR
reduced memory requirement by 75%
removed PagerTest
fixed stale-transaction bug in paging handler
fixed USRP clock rollover bug
faster call connection
new USRPDevice design
2.3 Jean Lafitte KSP r190? (trunk) check for out-of-date RACH bursts
better TRX-GSM clock sync
formal logging system
command line interface
emergency call setup
2.4 Kinder KSP r208? (trunk) fixed BCCH neighbor list bug
support for neighbor lists
fixed support for non-local Asterisk servers
cleaner configuration management
more realtime control of BCCH parameters
proper rejection of Hold messages
fixed L3 hanging bug in MTDCheckBYE
2.4.1 Kinder KSP r462 fixed lots of valgrind errors
2.4.2 Kinder KSP r482 zero-length calling party number bug
g++ 4.4 #includes
2.5 Lacassine KSP r551 imported Joshua Lackey patches
SIP fixes from Anne Kwong
SIP fixes from testing with SMS server
L3 TI handling fixes
SMS server support
GNU Radio 3.2 compatibility
configurable max range and LU-reject cause
"page" & "testcall" CLI features
2.5.1 Lacassine KSP r595 fixed some build bugs for some Linux distros
2.5.2 Lacassine KSP r630 fixed channel assignment bug for Nokia DCT4+ handsets
2.5.3 Lacassine KSP r756 merged fix for transceiver startup crash
due to use of uninitialized variables (r646)
merged fix for fusb bug from trunk (r582)
2.5.4 Lacassine KSP r812 merged fixes to build under latest Fedora and
to build with git GnuRadio (r814)
2.6 Mamou KSP r886 fixed infamous fusb bug (r582)
fixed idle-filling table size bug
smoother uplink power control
load-limiting downlink power control
new "config" features (optional, static)
IMEI interrogation
fixed MOD "missing FIFO" bug
configurable short code features
fixed transceiver startup crash (r646)
readline support is back
fixed timing advance bug (r844)
added CLI "chans" command
track time-of-use in TMSI table (r844)
added CLI "noise" command (r844)
added CLI "rxpower" command (r844)
added CLI "unconfig" command
2.7 Natchitoches Range rxxx (never released publicly)
converted TMSITable to sqlite3 (r902)
sqlite3-based configuration (r???)
converted Logger to syslogd (r903)
added support for rest octets (r1022)
external database for transaction reporting (r1184)
external database for channel status reporting (r1203)
in-call delivery and submission of text messages (r1231)
RFC-2833 DMTF (r1249)
2.8 Opelousas Range rxxx move databases to /etc and /var
RRLP aiding support

View File

@@ -22,6 +22,7 @@
*/
#include <stdio.h>
#include <iomanip> // std::setprecision
#include "Transceiver.h"
#include <Logger.h>
@@ -140,13 +141,16 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc)
}
Transceiver::Transceiver(int wBasePort,
const char *wTRXAddress,
size_t wSPS, size_t wChans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface)
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),
mSendEmptyBursts(false),
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
rssiOffset(wRssiOffset),
mSPSTx(wSPS), mSPSRx(1), mChans(wChans), mOn(false),
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelay(0)
{
@@ -531,13 +535,13 @@ 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;
return detectRACHBurst(burst, threshold, mSPSRx, &amp, &toa);
return detectRACHBurst(burst, threshold, mSPSRx, amp, toa);
}
/*
@@ -545,12 +549,13 @@ 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;
bool needDFE = false, estimateChan = false;
double elapsed = time - state->chanEstimateTime[tn];
signalVector *chanResp;
@@ -565,17 +570,18 @@ 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;
state->SNRestimate[tn] = amp.norm2() / (noise * noise + 1.0);
/* Set equalizer if unabled */
if (needDFE && estimateChan) {
float noise = state->mNoiseLev;
state->SNRestimate[tn] = amp.norm2() / (noise * noise + 1.0);
state->chanResponse[tn] = chanResp;
state->chanRespOffset[tn] = chanOffset;
state->chanRespAmplitude[tn] = amp;
@@ -588,7 +594,7 @@ bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
state->chanEstimateTime[tn] = time;
}
return true;;
return 1;
}
/*
@@ -615,16 +621,19 @@ 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, bool &isRssiValid,
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;
signalVector *burst;
SoftVector *bits = NULL;
TransceiverState *state = &mStates[chan];
isRssiValid = false;
/* Blocking FIFO read */
radioVector *radio_burst = mReceiveFIFO[chan]->read();
@@ -635,7 +644,9 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
GSM::Time time = radio_burst->getTime();
CorrType type = expectedCorrType(time, chan);
if ((type == OFF) || (type == IDLE)) {
/* No processing if the timeslot is off.
* Not even power level or noise calculation. */
if (type == OFF) {
delete radio_burst;
return NULL;
}
@@ -659,7 +670,25 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
/* Average noise on diversity paths and update global levels */
burst = radio_burst->getVector(max_i);
avg = sqrt(avg / radio_burst->chans());
state->mNoiseLev = state->mNoises.avg();
wTime = time;
RSSI = 20.0 * log10(rxFullScale / avg);
/* RSSI estimation are valid */
isRssiValid = true;
if (type == IDLE) {
/* Update noise levels */
state->mNoises.insert(avg);
state->mNoiseLev = state->mNoises.avg();
noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
delete radio_burst;
return NULL;
} else {
/* Do not update noise levels */
noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
}
/* Detect normal or RACH bursts */
if (type == TSC)
@@ -667,33 +696,27 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
else
success = detectRACH(state, *burst, amp, toa);
/* Update noise average if no bust detected or alert on error */
/* Alert an error and exit */
if (success <= 0) {
if (!success) {
state->mNoises.insert(avg);
} else if (success == -SIGERR_CLIP) {
LOG(ALERT) << "Clipping detected on RACH input";
} else if (success < 0) {
LOG(ALERT) << "Unhandled RACH error";
if (success == -SIGERR_CLIP) {
LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
} else if (success != SIGERR_NONE) {
LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
}
delete radio_burst;
return NULL;
}
timingOffset = toa / mSPSRx;
/* Demodulate and set output info */
if (equalize && (type != TSC))
equalize = false;
if (avg - state->mNoiseLev > 0.0)
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);
bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
delete radio_burst;
return bits;
}
@@ -836,6 +859,16 @@ void Transceiver::driveControl(size_t chan)
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
}
else if (strcmp(command,"SENDEMPTY")==0) {
int sendEmptyBursts;
sscanf(buffer,"%3s %s %d",cmdcheck,command,&sendEmptyBursts);
if (sendEmptyBursts == 0 || sendEmptyBursts == 1) {
mSendEmptyBursts = sendEmptyBursts;
sprintf(response,"RSP SENDEMPTY 0 %d",sendEmptyBursts);
} else {
sprintf(response,"RSP SENDEMPTY 1 %d",sendEmptyBursts);
}
}
else {
LOG(WARNING) << "bogus command " << command << " on control interface.";
sprintf(response,"RSP ERR 1");
@@ -892,36 +925,69 @@ void Transceiver::driveReceiveRadio()
void Transceiver::driveReceiveFIFO(size_t chan)
{
SoftVector *rxBurst = NULL;
int RSSI;
int TOA; // in 1/256 of a symbol
double RSSI; // RSSI in dBFS
double RSSIdBm; // RSSI in dBm
double TOA; // in symbols
double noise; // noise level in dBFS
double noisedBm; // noise level in dBm
GSM::Time burstTime;
bool isRssiValid; // are RSSI, noise and burstTime valid
char burstString[gSlotLen+10];
rxBurst = pullRadioVector(burstTime, RSSI, TOA, chan);
rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan);
if (rxBurst) {
// If mSendEmptyBursts, then send burst data even if it is not demodulated
if ((rxBurst || mSendEmptyBursts) && isRssiValid) {
RSSIdBm = RSSI+rssiOffset;
noisedBm = noise+rssiOffset;
LOG(DEBUG) << "burst parameters: "
<< " time: " << burstTime
<< " RSSI: " << RSSI
<< " TOA: " << TOA
<< " bits: " << *rxBurst;
char burstString[gSlotLen+10];
if (rxBurst) {
LOG(DEBUG) << std::fixed << std::right
<< " time: " << burstTime
<< " RSSI: " << std::setw(5) << std::setprecision(1) << RSSI << "dBFS/" << std::setw(6) << -RSSIdBm << "dBm"
<< " noise: " << std::setw(5) << std::setprecision(1) << noise << "dBFS/" << std::setw(6) << -noisedBm << "dBm"
<< " TOA: " << std::setw(5) << std::setprecision(2) << TOA
<< " bits: " << *rxBurst;
} else {
LOG(DEBUG) << std::fixed << std::right
<< " time: " << burstTime
<< " RSSI: " << std::setw(5) << std::setprecision(1) << RSSI << "dBFS/" << std::setw(6) << -RSSIdBm << "dBm"
<< " noise: " << std::setw(5) << std::setprecision(1) << noise << "dBFS/" << std::setw(6) << -noisedBm << "dBm"
<< " no burst decoded";
}
// Burst time
burstString[0] = burstTime.TN();
for (int i = 0; i < 4; i++)
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
burstString[5] = RSSI;
burstString[6] = (TOA >> 8) & 0x0ff;
burstString[7] = TOA & 0x0ff;
SoftVector::iterator burstItr = rxBurst->begin();
for (unsigned int i = 0; i < gSlotLen; i++) {
burstString[8+i] =(char) round((*burstItr++)*255.0);
// Burst RSSI
burstString[5] = (int)RSSIdBm;
// Time Of Arrival and actual bits
if (rxBurst) {
// Convert TOA to 1/256 symbol parts, round to closest integer
int TOAint = (int) (TOA * 256.0 + 0.5);
burstString[6] = (TOAint >> 8) & 0x0ff;
burstString[7] = TOAint & 0x0ff;
SoftVector::iterator burstItr = rxBurst->begin();
for (unsigned int i = 0; i < gSlotLen; i++) {
burstString[8+i] =(char) round((*burstItr++)*255.0);
}
delete rxBurst;
// Unused, but we check for the packet length in osmo-bts,
// so leave it as is for now
burstString[gSlotLen+8] = 0;
burstString[gSlotLen+9] = 0;
mDataSockets[chan]->write(burstString,gSlotLen+10);
} else {
// Only header
mDataSockets[chan]->write(burstString,6);
}
burstString[gSlotLen+9] = '\0';
delete rxBurst;
mDataSockets[chan]->write(burstString,gSlotLen+10);
}
}

View File

@@ -97,10 +97,11 @@ 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();
@@ -162,6 +163,7 @@ private:
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
bool mSendEmptyBursts; ///< send RSSI to the GSM core even if burst has not been demodulated
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
@@ -181,6 +183,8 @@ private:
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);
@@ -192,8 +196,9 @@ private:
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);
SoftVector *pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
double &timingOffset, double &noise,
size_t chan = 0);
/** Set modulus for specific timeslot */
void setModulus(size_t timeslot, size_t chan);
@@ -205,14 +210,14 @@ private:
void writeClockInterface(void);
/** Detect RACH bursts */
bool detectRACH(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa);
int 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);
int detectTSC(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa, GSM::Time &time);
/** Demodulat burst and output soft bits */
SoftVector *demodulate(TransceiverState *state,

View File

@@ -210,8 +210,7 @@ uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
{
TIMESTAMP ticks = ts.get_full_secs() * rate;
return ts.get_tick_count(rate) + ticks;
return (TIMESTAMP)(ts.get_real_secs() * rate + 0.5);
}
/*
@@ -290,7 +289,7 @@ public:
uhd_device(size_t sps, size_t chans, bool diversity, double offset);
~uhd_device();
int open(const std::string &args, bool extref);
int open(const std::string &args, bool extref, bool swap_channels);
bool start();
bool stop();
bool restart();
@@ -694,7 +693,7 @@ bool uhd_device::parse_dev_type()
return true;
}
int uhd_device::open(const std::string &args, bool extref)
int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
{
// Find UHD devices
uhd::device_addr_t addr(args);
@@ -720,7 +719,7 @@ int uhd_device::open(const std::string &args, bool extref)
// Verify and set channels
if ((dev_type == B210) && (chans == 2)) {
} else if ((dev_type == UMTRX) && (chans == 2)) {
uhd::usrp::subdev_spec_t subdev_spec("A:0 B:0");
uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
usrp_dev->set_tx_subdev_spec(subdev_spec);
usrp_dev->set_rx_subdev_spec(subdev_spec);
} else if (chans != 1) {
@@ -1430,6 +1429,19 @@ ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
if ((timestamp + len) <= time_end)
return ERROR_TIMESTAMP;
if (timestamp < time_end) {
LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
uhd::time_spec_t ts = convert_time(timestamp, clk_rt);
LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << convert_time(ts, clk_rt) << ") rate=" << clk_rt;
// Do not return error here, because it's a rounding error and is not fatal
}
if (timestamp > time_end && time_end != 0) {
LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
uhd::time_spec_t ts = convert_time(timestamp, clk_rt);
LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << convert_time(ts, clk_rt) << ") rate=" << clk_rt;
// Do not return error here, because it's a rounding error and is not fatal
}
// Starting index
size_t write_start = (data_start + (timestamp - time_start)) % buf_len;

View File

@@ -89,7 +89,7 @@ USRPDevice::USRPDevice(size_t sps, size_t, bool)
#endif
}
int USRPDevice::open(const std::string &, bool)
int USRPDevice::open(const std::string &, bool, bool)
{
writeLock.unlock();

View File

@@ -99,7 +99,7 @@ private:
USRPDevice(size_t sps, size_t chans = 1, bool diversity = false);
/** Instantiate the USRP */
int open(const std::string &, bool);
int open(const std::string &, bool, bool);
/** Start the USRP */
bool start();

View File

@@ -70,6 +70,8 @@ struct trx_config {
Transceiver::FillerType filler;
bool diversity;
double offset;
double rssi_offset;
bool swap_channels;
};
ConfigurationTable gConfig;
@@ -185,6 +187,8 @@ 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;
ost << " Swap channels........... " << config->swap_channels << std::endl;
std::cout << ost << std::endl;
return true;
@@ -240,7 +244,7 @@ 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);
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;
@@ -292,7 +296,9 @@ static void print_help()
" -c Number of ARFCN channels (default=1)\n"
" -f Enable C0 filler table\n"
" -o Set baseband frequency offset (default=auto)\n"
" -r Random burst test mode with TSC\n",
" -r Random burst test mode with TSC\n"
" -R RSSI to dBm offset in dB (default=0)\n"
" -S Swap channels (UmTRX only)\n",
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
}
@@ -308,8 +314,10 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
config->filler = Transceiver::FILLER_ZERO;
config->diversity = false;
config->offset = 0.0;
config->rssi_offset = 0.0;
config->swap_channels = false;
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:")) != -1) {
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:R:S")) != -1) {
switch (option) {
case 'h':
print_help();
@@ -349,6 +357,12 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
config->rtsc = atoi(optarg);
config->filler = Transceiver::FILLER_RAND;
break;
case 'R':
config->rssi_offset = atof(optarg);
break;
case 'S':
config->swap_channels = true;
break;
default:
print_help();
exit(0);
@@ -393,7 +407,7 @@ int main(int argc, char *argv[])
/* Create the low level device object */
usrp = RadioDevice::make(config.sps, config.chans,
config.diversity, config.offset);
type = usrp->open(config.dev_args, config.extref);
type = usrp->open(config.dev_args, config.extref, config.swap_channels);
if (type < 0) {
LOG(ALERT) << "Failed to create radio device" << std::endl;
goto shutdown;

View File

@@ -41,7 +41,7 @@ class RadioDevice {
bool diversity = false, double offset = 0.0);
/** Initialize the USRP */
virtual int open(const std::string &args = "", bool extref = false)=0;
virtual int open(const std::string &args = "", bool extref = false, bool swap_channels = false)=0;
virtual ~RadioDevice() { }

View File

@@ -28,6 +28,7 @@
#include "sigProcLib.h"
#include "GSMCommon.h"
#include "Logger.h"
extern "C" {
#include "convolve.h"
@@ -1284,12 +1285,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();
@@ -1372,18 +1373,74 @@ static int detectBurst(signalVector &burst,
return 1;
}
static int detectClipping(signalVector &burst, float thresh)
static float maxAmplitude(signalVector &burst)
{
for (size_t i = 0; i < burst.size(); i++) {
if (fabs(burst[i].real()) > thresh)
return 1;
if (fabs(burst[i].imag()) > thresh)
return 1;
}
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 0;
return max;
}
/*
* RACH/Normal burst detection with clipping detection
*
* Correlation window parameters:
* target: Tail bits + burst length
* head: Search symbols before target
* tail: Search symbols after target
*/
int detectGeneralBurst(signalVector &rxBurst,
float thresh,
int sps,
complex &amp,
float &toa,
int target, int head, int tail,
CorrelationSequence *sync)
{
int rc, start, len;
bool clipping = false;
signalVector *corr;
if ((sps != 1) && (sps != 4))
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;
}
start = (target - head) * sps - 1;
len = (head + tail) * sps;
corr = new signalVector(len);
rc = detectBurst(rxBurst, *corr, sync,
thresh, sps, &amp, &toa, start, len);
delete corr;
if (rc < 0) {
return -SIGERR_INTERNAL;
} else if (!rc) {
amp = 0.0f;
toa = 0.0f;
return clipping?-SIGERR_CLIP:SIGERR_NONE;
}
/* Subtract forward search bits from delay */
toa -= head * sps;
return 1;
}
/*
* RACH burst detection
*
@@ -1393,53 +1450,23 @@ static int detectClipping(signalVector &burst, float thresh)
* tail: Search 10 symbols after target
*/
int detectRACHBurst(signalVector &rxBurst,
float thresh,
int sps,
complex *amp,
float *toa)
float thresh,
int sps,
complex &amp,
float &toa)
{
int rc, start, target, head, tail, len;
float _toa;
complex _amp;
signalVector *corr;
int rc, target, head, tail;
CorrelationSequence *sync;
if ((sps != 1) && (sps != 4))
return -SIGERR_UNSUPPORTED;
if (detectClipping(rxBurst, CLIP_THRESH))
return -SIGERR_CLIP;
target = 8 + 40;
head = 4;
tail = 10;
start = (target - head) * sps - 1;
len = (head + tail) * sps;
sync = gRACHSequence;
corr = new signalVector(len);
rc = detectBurst(rxBurst, *corr, sync,
thresh, sps, &_amp, &_toa, start, len);
delete corr;
rc = detectGeneralBurst(rxBurst, thresh, sps, amp, toa,
target, head, tail, sync);
if (rc < 0) {
return -SIGERR_INTERNAL;
} else if (!rc) {
if (amp)
*amp = 0.0f;
if (toa)
*toa = 0.0f;
return 0;
}
/* Subtract forward search bits from delay */
if (toa)
*toa = _toa - head * sps;
if (amp)
*amp = _amp;
return 1;
return rc;
}
/*
@@ -1451,60 +1478,32 @@ int detectRACHBurst(signalVector &rxBurst,
* tail: Search 4 symbols + maximum expected delay
*/
int analyzeTrafficBurst(signalVector &rxBurst, unsigned tsc, float thresh,
int sps, complex *amp, float *toa, unsigned max_toa,
int sps, complex &amp, float &toa, unsigned max_toa,
bool chan_req, signalVector **chan, float *chan_offset)
{
int rc, start, target, head, tail, len;
complex _amp;
float _toa;
signalVector *corr;
int rc, target, head, tail;
CorrelationSequence *sync;
if ((tsc < 0) || (tsc > 7) || ((sps != 1) && (sps != 4)))
if ((tsc < 0) || (tsc > 7))
return -SIGERR_UNSUPPORTED;
if (detectClipping(rxBurst, CLIP_THRESH))
return -SIGERR_CLIP;
target = 3 + 58 + 16 + 5;
head = 4;
tail = 4 + max_toa;
start = (target - head) * sps - 1;
len = (head + tail) * sps;
sync = gMidambles[tsc];
corr = new signalVector(len);
rc = detectBurst(rxBurst, *corr, sync,
thresh, sps, &_amp, &_toa, start, len);
delete corr;
if (rc < 0) {
return -SIGERR_INTERNAL;
} else if (!rc) {
if (amp)
*amp = 0.0f;
if (toa)
*toa = 0.0f;
return 0;
}
/* Subtract forward search bits from delay */
_toa -= head * sps;
if (toa)
*toa = _toa;
if (amp)
*amp = _amp;
rc = detectGeneralBurst(rxBurst, thresh, sps, amp, toa,
target, head, tail, sync);
/* Equalization not currently supported */
if (chan_req) {
if (rc > 0 && chan_req) {
*chan = new signalVector(6 * sps);
if (chan_offset)
*chan_offset = 0.0;
}
return 1;
return rc;
}
signalVector *decimateVector(signalVector &wVector, size_t factor)

View File

@@ -192,8 +192,8 @@ bool energyDetect(signalVector &rxBurst,
int detectRACHBurst(signalVector &rxBurst,
float detectThreshold,
int sps,
complex *amplitude,
float* TOA);
complex &amplitude,
float &TOA);
/**
Normal burst correlator, detector, channel estimator.
@@ -210,15 +210,15 @@ int detectRACHBurst(signalVector &rxBurst,
@return positive if threshold value is reached, negative on error, zero otherwise
*/
int analyzeTrafficBurst(signalVector &rxBurst,
unsigned TSC,
float detectThreshold,
int sps,
complex *amplitude,
float *TOA,
unsigned TSC,
float detectThreshold,
int sps,
complex &amplitude,
float &TOA,
unsigned maxTOA,
bool requestChannel = false,
signalVector** channelResponse = NULL,
float *channelResponseOffset = NULL);
signalVector** channelResponse = NULL,
float *channelResponseOffset = NULL);
/**
Decimate a vector.

3
utils/clockdump.sh Executable file
View File

@@ -0,0 +1,3 @@
#!/bin/sh
sudo tcpdump -i lo0 -A udp port 5700