Compare commits

...

42 Commits

Author SHA1 Message Date
Alexander Chemeris
f8f000cca1 fixup 2016-03-26 14:43:37 +03:00
Alexander Chemeris
f1c1379e97 fixup 2016-03-26 10:57:41 +03:00
Alexander Chemeris
bb7b057ec3 transceiver: Send packets to a different port in case of external demodulation.
We want to have external demodulation which is transparent for the osmo-trx user,
so osmo-trx will send data to the external demodulator and it will send data to
the osmo-trx user.
2015-11-13 11:10:04 -08:00
Alexander Chemeris
9155d8da94 transceiver: Add an option to stream raw samples instead of demodulated softbits. 2015-11-13 11:00:21 -08:00
Alexander Chemeris
8b8e7ecf8a fixup 2015-11-13 10:08:36 -08:00
Alexander Chemeris
e587cdb213 fixup 2015-11-13 10:02:46 -08:00
Alexander Chemeris
5d1eaaffcc transceiver: Remove remainings of the equalizer, restructure driveReceiveFIFO() for better modularity.
Equalizer has never worked properly and was always disabled. Now is a good time
to remove it completely to make the code cleaner.
2015-11-13 09:38:56 -08:00
Alexander Chemeris
511a662394 Common: Make sure gLogEarly() log to the same facilities as the normal log. 2015-09-10 19:57:03 -04:00
Alexander Chemeris
7d2866164b Common: Get rid of a compilation warning.
debugLogEarly was replaced to an empty space and arguments of the function
became operators, grouped together by ():
Configuration.cpp: In member function 'bool ConfigurationTable::defines(const string&)':
Configuration.cpp:272:28: warning: left operand of comma operator has no effect [-Wunused-value]
   debugLogEarly(LOG_ALERT, "configuration parameter %s not found", key.c_str());
                            ^

This fix removes debugLogEarly together with its arguments.
2015-09-10 19:57:03 -04:00
Kirill Zakharenko
a0f8ed8ecb Bumped version to 0.1.9~1 2015-09-10 19:57:03 -04:00
Kirill Zakharenko
c579c071f4 debian: correct dependency on libuhd, throw away dependency on umtrx-uhd 2015-09-10 19:57:03 -04:00
Kirill Zakharenko
2337e8f8aa debian: compile for atom arch with SSE3 optimizations 2015-09-10 19:57:03 -04:00
Kirill Zakharenko
afa28be3ef transceiver/x86: don’t use -march=native to build x86 specialized code
when this option is used, it is impossible to cross-compile for x86
 variants like atom.
2015-09-10 19:57:03 -04:00
Kirill Zakharenko
cab15655f3 debian: whitespace changes, more correct hardening stanza 2015-09-10 19:57:03 -04:00
Kirill Zakharenko
f8e8e57568 bumped version to 0.1.9 2015-09-10 19:57:03 -04:00
Kirill Zakharenko
441dd35a6f build with instruction set/tuning for atom processors
fix non-working hardening
2015-09-10 19:57:03 -04:00
Ivan Kluchnikov
bf7ed547ac debian: make it possible to install osmo-trx dependencies manually on the system 2015-09-10 19:57:03 -04:00
Ivan Kluchnikov
ab31d70678 debian: update osmo-trx dependencies
Now we use uhd and umtrx-uhd instead of libuhd-dev.
2015-09-10 19:57:03 -04:00
Ivan Kluchnikov
341869feb1 debian: Add debug package for the osmo-trx 2015-09-10 19:57:03 -04:00
Ivan Kluchnikov
31862c5e4c debian: Add debian directory to ease building packages 2015-09-10 19:57:03 -04:00
Tom Tsou
f147b17447 sigproc: Make convolution and convert input buffers immutable
For good practice, use const specifier when applicable.

Signed-off-by: Tom Tsou <tom@tsou.cc>
2015-08-21 19:31:24 -07:00
Tom Tsou
d4d3daa12e uhd: Use internal UHD tick conversions
UHD handles built in tick and floating point timestamp conversion
since version 003.005.004. This removes the need for separate UHD
timespec to tick conversion.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-08-21 19:29:38 -07:00
Tom Tsou
c312905f43 uhd: Add version 3.9.0 support
New functionality includes B200-mini device support and updated
timing values to match FPGA changes.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-08-21 19:20:32 -07:00
Alexander Chemeris
c4eab8795f uhd: Output Rx/Tx gain limits to log to make it more transparent.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:25:17 -07:00
Alexander Chemeris
cc6f79b1c0 Logger: Output ERR log messages to stderr as well.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:24:20 -07:00
Alexander Chemeris
5a0680655f Transceiver: Add support for OsmoBTS style handover.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:24:08 -07:00
Alexander Chemeris
3722920100 Transceiver: Fix whitespace.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:23:32 -07:00
Alexander Chemeris
f3b9af65ed uhd: Fix UmTRX tuning broken in commit 90f7a01d.
Commit 90f7a01d lost "return" statement. We also should account the fact that
offset can be negative.
We should return the tuning request immediately after

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:23:24 -07:00
Alexander Chemeris
e692ce986c transceiver: Add a debug option to dump selected timeslots to disk.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:22:59 -07:00
Alexander Chemeris
81c6873205 Transceiver: Do not update state->SNRestimate if equalization is disabled.
This also fix a bug of using bool type for noise instead of float.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:22:41 -07:00
Alexander Chemeris
c052aa1d4c 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.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:22:28 -07:00
Alexander Chemeris
130a8007fa 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.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:22:13 -07:00
Alexander Chemeris
72e8619632 Checking in clockdump.sh utility.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:20:22 -07:00
Alexander Chemeris
2beb1adcea Checking in a more relevant README.
This READMY is from the OpenBTS's TRXManager and actually describes the transceiver
API and behavior.
2015-07-30 14:19:51 -07:00
Alexander Chemeris
2b542100a0 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.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:19:32 -07:00
Alexander Chemeris
2268c8558c 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.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:19:17 -07:00
Alexander Chemeris
50747dc65d osmo-trx: Add an option to swap channels on UmTRX.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:19:06 -07:00
Alexander Chemeris
1e9b4d57da sigProcLib: Check for bogus TOA before using it.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:18:31 -07:00
Alexander Chemeris
954b118bfa 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.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:17:59 -07:00
Alexander Chemeris
dbe26abcb9 Transceiver: Print noise level for each burst in debug mode.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:17:56 -07:00
Alexander Chemeris
e8905a03a5 osmo-trx: Add a command line option for the dBFS to dBm offset.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:14:33 -07:00
Alexander Chemeris
909ffbfd23 Common: Use a scoped lock in the Logger to avoid deadlock on thread cancel.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:14:23 -07:00
27 changed files with 774 additions and 826 deletions

View File

@@ -35,7 +35,7 @@
#ifdef DEBUG_CONFIG
#define debugLogEarly gLogEarly
#else
#define debugLogEarly
#define debugLogEarly(x,y,z)
#endif

View File

@@ -38,6 +38,14 @@
using namespace std;
// Switches to enable/disable logging targets
// MUST BE DEFINED BEFORE gConfig FOR gLogEarly() TO WORK CORRECTLY
bool gLogToConsole = true;
bool gLogToSyslog = false;
FILE *gLogToFile = NULL;
Mutex gLogToLock;
// Reference to a global config table, used all over the system.
extern ConfigurationTable gConfig;
@@ -67,10 +75,6 @@ const char *levelNames[] = {
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
};
int numLevels = 8;
bool gLogToConsole = true;
bool gLogToSyslog = false;
FILE *gLogToFile = NULL;
Mutex gLogToLock;
int levelStringToInt(const string& name)
@@ -193,7 +197,7 @@ Log::~Log()
if (mDummyInit) return;
// Anything at or above LOG_CRIT is an "alarm".
// Save alarms in the local list and echo them to stderr.
if (mPriority <= LOG_CRIT) {
if (mPriority <= LOG_ERR) {
if (sLoggerInited) addAlarm(mStream.str().c_str());
cerr << mStream.str() << endl;
}
@@ -206,7 +210,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 +222,6 @@ Log::~Log()
if (neednl) {fputc('\n',gLogToFile);}
fflush(gLogToFile);
}
gLogToLock.unlock();
}
}
@@ -270,9 +273,32 @@ void gLogInit(const char* name, const char* level, int facility)
void gLogEarly(int level, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vsyslog(level | LOG_USER, fmt, args);
if (gLogToSyslog) {
va_list args_copy;
va_copy(args_copy, args);
vsyslog(level | LOG_USER, fmt, args_copy);
va_end(args_copy);
}
if (gLogToConsole) {
va_list args_copy;
va_copy(args_copy, args);
vprintf(fmt, args_copy);
printf("\n");
va_end(args_copy);
}
if (gLogToFile) {
va_list args_copy;
va_copy(args_copy, args);
vfprintf(gLogToFile, fmt, args_copy);
fprintf(gLogToFile, "\n");
va_end(args_copy);
}
va_end(args);
}

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,8 @@
*/
#include <stdio.h>
#include <iomanip> // std::setprecision
#include <fstream>
#include "Transceiver.h"
#include <Logger.h>
@@ -48,9 +50,6 @@ TransceiverState::TransceiverState()
for (int i = 0; i < 8; i++) {
chanType[i] = Transceiver::NONE;
fillerModulus[i] = 26;
chanResponse[i] = NULL;
DFEForward[i] = NULL;
DFEFeedback[i] = NULL;
for (int n = 0; n < 102; n++)
fillerTable[n][i] = NULL;
@@ -60,10 +59,6 @@ TransceiverState::TransceiverState()
TransceiverState::~TransceiverState()
{
for (int i = 0; i < 8; i++) {
delete chanResponse[i];
delete DFEForward[i];
delete DFEFeedback[i];
for (int n = 0; n < 102; n++)
delete fillerTable[n][i];
}
@@ -139,19 +134,34 @@ bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc)
return false;
}
void Transceiver::reset()
{
for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
mTxPriorityQueues[i].clear();
}
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,
bool wExternalDemod)
: mBasePort(wBasePort), mAddr(wTRXAddress),
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
rssiOffset(wRssiOffset),
mExternalDemod(wExternalDemod),
mSPSTx(wSPS), mSPSRx(1), mChans(wChans), mOn(false),
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelay(0)
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelay(0), mWriteBurstToDiskMask(0)
{
txFullScale = mRadioInterface->fullScaleInputValue();
rxFullScale = mRadioInterface->fullScaleOutputValue();
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++)
mHandover[i][j] = false;
}
}
Transceiver::~Transceiver()
@@ -213,7 +223,7 @@ bool Transceiver::init(int filler, size_t rtsc)
c_srcport = mBasePort + 2 * i + 1;
c_dstport = mBasePort + 2 * i + 101;
d_srcport = mBasePort + 2 * i + 2;
d_dstport = mBasePort + 2 * i + 102;
d_dstport = mBasePort + 2 * i + (mExternalDemod?202:102);
mCtrlSockets[i] = new UDPSocket(c_srcport, mAddr.c_str(), c_dstport);
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
@@ -459,9 +469,15 @@ void Transceiver::setModulus(size_t timeslot, size_t chan)
Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
size_t chan)
{
static int tchh_subslot[26] = { 0,1,0,1,0,1,0,1,0,1,0,1,0,0,1,0,1,0,1,0,1,0,1,0,1,1 };
static int sdcch4_subslot[102] = { 3,3,3,3,0,0,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2,
3,3,3,3,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,0,0,2,2,2,2 };
static int sdcch8_subslot[102] = { 5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,0,0,0,0,
1,1,1,1,2,2,2,2,3,3,3,3,0,0,0,0,0,0,0,1,1,1,1,2,2,2,2,3,3,3,3,4,4,4,4,5,5,5,5,6,6,6,6,7,7,7,7,4,4,4,4 };
TransceiverState *state = &mStates[chan];
unsigned burstTN = currTime.TN();
unsigned burstFN = currTime.FN();
int subch;
switch (state->chanType[burstTN]) {
case NONE:
@@ -471,16 +487,25 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
return IDLE;
break;
case I:
// TODO: Are we expecting RACH on an IDLE frame?
/* if (burstFN % 26 == 25)
return IDLE;*/
if (mHandover[burstTN][0])
return RACH;
return TSC;
/*if (burstFN % 26 == 25)
return IDLE;
else
return TSC;*/
break;
case II:
subch = tchh_subslot[burstFN % 26];
if (subch == 1)
return IDLE;
if (mHandover[burstTN][0])
return RACH;
return TSC;
break;
case III:
subch = tchh_subslot[burstFN % 26];
if (mHandover[burstTN][subch])
return RACH;
return TSC;
break;
case IV:
@@ -495,6 +520,8 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
return RACH;
else if ((mod51 == 45) || (mod51 == 46))
return RACH;
else if (mHandover[burstTN][sdcch4_subslot[burstFN % 102]])
return RACH;
else
return TSC;
break;
@@ -502,6 +529,8 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
case VII:
if ((burstFN % 51 <= 14) && (burstFN % 51 >= 12))
return IDLE;
else if (mHandover[burstTN][sdcch8_subslot[burstFN % 102]])
return RACH;
else
return TSC;
break;
@@ -528,118 +557,84 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
}
/*
* Detect RACH synchronization sequence within a burst. No equalization
* is used or available on the RACH channel.
* Detect RACH synchronization sequence within a burst.
*/
bool Transceiver::detectRACH(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa)
int Transceiver::detectRACH(signalVector &burst,
complex &amp, float &toa)
{
float threshold = 6.0;
return detectRACHBurst(burst, threshold, mSPSRx, &amp, &toa);
return detectRACHBurst(burst, threshold, mSPSRx, amp, toa);
}
/*
* Detect normal burst training sequence midamble. Update equalization
* state information and channel estimate if necessary. Equalization
* is currently disabled.
* Detect normal burst training sequence midamble.
*/
bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
complex &amp, float &toa, GSM::Time &time)
int Transceiver::detectTSC(signalVector &burst,
complex &amp, float &toa)
{
int tn = time.TN();
float chanOffset, threshold = 5.0;
bool noise, needDFE = false, estimateChan = false;
double elapsed = time - state->chanEstimateTime[tn];
signalVector *chanResp;
/* Check equalization update state */
if (needDFE && ((elapsed > 50) || (!state->chanResponse[tn]))) {
delete state->DFEForward[tn];
delete state->DFEFeedback[tn];
state->DFEForward[tn] = NULL;
state->DFEFeedback[tn] = NULL;
estimateChan = true;
}
float threshold = 5.0;
/* Detect normal burst midambles */
if (!analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, &amp,
&toa, mMaxExpectedDelay, estimateChan,
&chanResp, &chanOffset)) {
return false;
}
noise = state->mNoiseLev;
state->SNRestimate[tn] = amp.norm2() / (noise * noise + 1.0);
/* Set equalizer if unabled */
if (needDFE && estimateChan) {
state->chanResponse[tn] = chanResp;
state->chanRespOffset[tn] = chanOffset;
state->chanRespAmplitude[tn] = amp;
scaleVector(*chanResp, complex(1.0, 0.0) / amp);
designDFE(*chanResp, state->SNRestimate[tn],
7, &state->DFEForward[tn], &state->DFEFeedback[tn]);
state->chanEstimateTime[tn] = time;
}
return true;;
return analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, amp,
toa, mMaxExpectedDelay);
}
/*
* Demodulate GMSK burst using equalization if requested. Otherwise
* demodulate by direct rotation and soft slicing.
*/
SoftVector *Transceiver::demodulate(TransceiverState *state,
signalVector &burst, complex amp,
float toa, size_t tn, bool equalize)
void writeToFile(radioVector *radio_burst, size_t chan)
{
if (equalize) {
scaleVector(burst, complex(1.0, 0.0) / amp);
return equalizeBurst(burst,
toa - state->chanRespOffset[tn],
mSPSRx,
*state->DFEForward[tn],
*state->DFEFeedback[tn]);
}
return demodulateBurst(burst, mSPSRx, amp, toa);
GSM::Time time = radio_burst->getTime();
std::ostringstream fname;
fname << chan << "_" << time.FN() << "_" << time.TN() << ".fc";
std::ofstream outfile (fname.str().c_str(), std::ofstream::binary);
outfile.write((char*)radio_burst->getVector()->begin(), radio_burst->getVector()->size() * 2 * sizeof(float));
outfile.close();
}
/*
* 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::demodSignalVector(signalVector *burst,
CorrType type,
double &timingOffset)
{
bool success, equalize = false;
int success;
complex amp;
float toa, pow, max = -1.0, avg = 0.0;
int max_i = -1;
signalVector *burst;
float toa;
SoftVector *bits = NULL;
TransceiverState *state = &mStates[chan];
/* Blocking FIFO read */
radioVector *radio_burst = mReceiveFIFO[chan]->read();
if (!radio_burst)
return NULL;
/* Detect normal or RACH bursts */
if (type == TSC)
success = detectTSC(*burst, amp, toa);
else
success = detectRACH(*burst, amp, toa);
/* Set time and determine correlation type */
GSM::Time time = radio_burst->getTime();
CorrType type = expectedCorrType(time, chan);
/* Alert an error and exit */
if (success <= 0) {
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";
}
if ((type == OFF) || (type == IDLE)) {
delete radio_burst;
return NULL;
}
timingOffset = toa / mSPSRx;
bits = demodulateBurst(*burst, mSPSRx, amp, toa);
return bits;
}
signalVector *Transceiver::chooseDiversityPath(radioVector *radio_burst, double &avg)
{
signalVector *burst;
int max_i = -1;
float pow, max = -1.0;
avg = 0.0;
/* Select the diversity channel with highest energy */
for (size_t i = 0; i < radio_burst->chans(); i++) {
energyDetect(*radio_burst->getVector(i), 20 * mSPSRx, 0.0, &pow);
@@ -652,58 +647,23 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
if (max_i < 0) {
LOG(ALERT) << "Received empty burst";
delete radio_burst;
return NULL;
}
/* 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();
/* Detect normal or RACH bursts */
if (type == TSC)
success = detectTSC(state, *burst, amp, toa, time);
else
success = detectRACH(state, *burst, amp, toa);
/* Update noise average if no bust detected or alert on error */
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";
}
delete radio_burst;
return NULL;
}
/* 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);
delete radio_burst;
return bits;
return burst;
}
void Transceiver::reset()
void TransceiverState::updateNoiseEstimates(double avg)
{
for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
mTxPriorityQueues[i].clear();
/* Update noise levels */
mNoises.insert(avg);
mNoiseLev = mNoises.avg();
}
void Transceiver::driveControl(size_t chan)
{
int MAX_PACKET_LENGTH = 100;
@@ -743,6 +703,24 @@ void Transceiver::driveControl(size_t chan)
sprintf(response,"RSP POWERON 1");
else
sprintf(response,"RSP POWERON 0");
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++)
mHandover[i][j] = false;
}
}
else if (strcmp(command,"HANDOVER")==0){
int ts=0,ss=0;
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss);
mHandover[ts][ss] = true;
LOG(WARNING) << "HANDOVER RACH at timeslot " << ts << " subslot " << ss;
sprintf(response,"RSP HANDOVER 0 %d %d",ts,ss);
}
else if (strcmp(command,"NOHANDOVER")==0){
int ts=0,ss=0;
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&ts,&ss);
mHandover[ts][ss] = false;
LOG(WARNING) << "NOHANDOVER at timeslot " << ts << " subslot " << ss;
sprintf(response,"RSP NOHANDOVER 0 %d %d",ts,ss);
}
else if (strcmp(command,"SETMAXDLY")==0) {
//set expected maximum time-of-arrival
@@ -760,9 +738,8 @@ void Transceiver::driveControl(size_t chan)
}
else if (strcmp(command,"NOISELEV")==0) {
if (mOn) {
float lev = mStates[chan].mNoiseLev;
sprintf(response,"RSP NOISELEV 0 %d",
(int) round(20.0 * log10(rxFullScale / lev)));
(int) round(dB2(rxFullScale / mStates[chan].mNoiseLev)));
}
else {
sprintf(response,"RSP NOISELEV 1 0");
@@ -836,6 +813,14 @@ void Transceiver::driveControl(size_t chan)
sprintf(response,"RSP SETSLOT 0 %d %d",timeslot,corrCode);
}
else if (strcmp(command,"_SETBURSTTODISKMASK")==0) {
// debug command! may change or disapear without notice
// set a mask which bursts to dump to disk
int mask;
sscanf(buffer,"%3s %s %d",cmdcheck,command,&mask);
mWriteBurstToDiskMask = mask;
sprintf(response,"RSP _SETBURSTTODISKMASK 0 %d",mask);
}
else {
LOG(WARNING) << "bogus command " << command << " on control interface.";
sprintf(response,"RSP ERR 1");
@@ -891,38 +876,133 @@ void Transceiver::driveReceiveRadio()
void Transceiver::driveReceiveFIFO(size_t chan)
{
SoftVector *rxBurst = NULL;
int RSSI;
int TOA; // in 1/256 of a symbol
radioVector *radio_burst = NULL;
signalVector *burst = NULL;
double burst_power; // sqr(amp)
double RSSI; // in dBFS
double dBm; // in dBm
double TOA; // in symbols
double noise; // noise level in dBFS
GSM::Time burstTime;
CorrType burstType;
char burstString[3000];
int pktLen;
rxBurst = pullRadioVector(burstTime, RSSI, TOA, chan);
/* Blocking FIFO read */
radio_burst = mReceiveFIFO[chan]->read();
if (!radio_burst)
return;
if (rxBurst) {
/* Set time and determine correlation type */
burstTime = radio_burst->getTime();
burstType = expectedCorrType(burstTime, chan);
LOG(DEBUG) << "burst parameters: "
<< " time: " << burstTime
<< " RSSI: " << RSSI
<< " TOA: " << TOA
<< " bits: " << *rxBurst;
char burstString[gSlotLen+10];
burstString[0] = burstTime.TN();
for (int i = 0; i < 4; i++)
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
burstString[5] = RSSI;
burstString[6] = (TOA >> 8) & 0x0ff;
burstString[7] = TOA & 0x0ff;
SoftVector::iterator burstItr = rxBurst->begin();
/* Debug: dump bursts to disk */
/* bits 0-7 - chan 0 timeslots
* bits 8-15 - chan 1 timeslots */
if (mWriteBurstToDiskMask & ((1<<burstTime.TN()) << (8*chan)))
writeToFile(radio_burst, chan);
for (unsigned int i = 0; i < gSlotLen; i++) {
burstString[8+i] =(char) round((*burstItr++)*255.0);
}
burstString[gSlotLen+9] = '\0';
delete rxBurst;
mDataSockets[chan]->write(burstString,gSlotLen+10);
/* No processing if the timeslot is off. */
if (burstType == OFF) {
delete radio_burst;
return;
}
/* Choose a diversity channel to use */
/* Returned value is a pointer to the radio_burst internal structure */
burst = chooseDiversityPath(radio_burst, burst_power);
if (!burst) {
delete radio_burst;
return;
}
/* We use idle timeslots to calculate noise levels for informational purposes.
* Otherwise we ignore them. */
if (burstType == IDLE) {
mStates[chan].updateNoiseEstimates(burst_power);
return;
}
/* Update/calculate burst info */
noise = dB2(rxFullScale / mStates[chan].mNoiseLev);
RSSI = dB2(rxFullScale / burst_power);
dBm = RSSI+rssiOffset;
if (!mExternalDemod) {
/* Pre-process and demodulate radio vector */
SoftVector *rxBurst = demodSignalVector(burst, burstType, TOA);
if (!rxBurst)
return;
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;
pktLen = formatDemodPacket(burstTime, dBm, TOA, rxBurst, burstString);
delete rxBurst;
} else {
/* Send radio vector as is */
pktLen = formatRawPacket(burstTime, dBm, TOA, burstType, mTSC, burst, burstString);
}
mDataSockets[chan]->write(burstString, pktLen);
delete radio_burst;
}
int Transceiver::formatCommonPacketHeader(GSM::Time burstTime, double dBm, double TOA,
char *burstString)
{
int TOAint; // in 1/256 symbols
TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
burstString[0] = burstTime.TN();
for (int i = 0; i < 4; i++)
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
burstString[5] = (int)dBm;
burstString[6] = (TOAint >> 8) & 0x0ff;
burstString[7] = TOAint & 0x0ff;
return 8;
}
int Transceiver::formatDemodPacket(GSM::Time burstTime, double dBm, double TOA,
SoftVector *rxBurst, char *burstString)
{
int headerSize = formatCommonPacketHeader(burstTime, dBm, TOA, burstString);
SoftVector::iterator burstItr = rxBurst->begin();
for (unsigned int i = 0; i < gSlotLen; i++) {
burstString[headerSize+i] =(char) round((*burstItr++)*255.0);
}
burstString[gSlotLen+headerSize+1] = '\0';
return gSlotLen+headerSize+2;
}
int Transceiver::formatRawPacket(GSM::Time burstTime, double dBm, double TOA,
CorrType burstType, unsigned tsc,
signalVector *rxBurst, char *burstString)
{
int headerSize = formatCommonPacketHeader(burstTime, dBm, TOA, burstString);
burstString[headerSize++] = burstType;
burstString[headerSize++] = tsc;
burstString[headerSize++] = 0; // alignment
burstString[headerSize++] = 0; // alignment
signalVector::iterator burstItr = rxBurst->begin();
float *signalItr = (float*)(&burstString[headerSize]);
for (unsigned int i = 0; i < gSlotLen; i++, burstItr++) {
signalItr[2*i] = (*burstItr).real();
signalItr[2*i+1] = (*burstItr).imag();
}
return headerSize + 2*gSlotLen*sizeof(float);
}
void Transceiver::driveTxFIFO()

View File

@@ -55,29 +55,15 @@ struct TransceiverState {
/* Initialize a multiframe slot in the filler table */
bool init(int filler, size_t sps, float scale, size_t rtsc);
void updateNoiseEstimates(double avg);
int chanType[8];
/* Last timestamp of each timeslot's channel estimate */
GSM::Time chanEstimateTime[8];
/* The filler table */
signalVector *fillerTable[102][8];
int fillerModulus[8];
bool mRetrans;
/* Most recent channel estimate of all timeslots */
signalVector *chanResponse[8];
/* Most recent DFE feedback filter of all timeslots */
signalVector *DFEForward[8];
signalVector *DFEFeedback[8];
/* Most recent SNR, timing, and channel amplitude estimates */
float SNRestimate[8];
float chanRespOffset[8];
complex chanRespAmplitude[8];
/* Received noise energy levels */
float mNoiseLev;
noiseVector mNoises;
@@ -97,10 +83,12 @@ 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,
bool wExternalDemod);
/** Destructor */
~Transceiver();
@@ -181,6 +169,10 @@ private:
double txFullScale; ///< full scale input to radio
double rxFullScale; ///< full scale output to radio
double rssiOffset; ///< RSSI to dBm conversion offset
bool mExternalDemod; ///< Should we internal or external demod
/** modulate and add a burst to the transmit queue */
void addRadioVector(size_t chan, BitVector &bits,
int RSSI, GSM::Time &wTime);
@@ -192,8 +184,15 @@ 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 *demodSignalVector(signalVector *burst,
CorrType type,
double &timingOffset);
/** choose the channel to use */
signalVector *chooseDiversityPath(radioVector *radio_burst, double &avg);
/** update noise estimate */
double updateNoiseEstimates(TransceiverState *state, double avg);
/** Set modulus for specific timeslot */
void setModulus(size_t timeslot, size_t chan);
@@ -205,29 +204,24 @@ private:
void writeClockInterface(void);
/** Detect RACH bursts */
bool detectRACH(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa);
int detectRACH(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 detectTSC(signalVector &burst,
complex &amp, float &toa);
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
bool mOn; ///< flag to indicate that transceiver is powered on
bool mHandover[8][8]; ///< expect handover to the timeslot/subslot
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
unsigned mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
unsigned mWriteBurstToDiskMask; ///< debug: bitmask to indicate which timeslots to dump to disk
std::vector<TransceiverState> mStates;
@@ -245,6 +239,19 @@ protected:
/** drive demodulation of GSM bursts */
void driveReceiveFIFO(size_t chan);
/** format a common header for packets with sent over the network */
int formatCommonPacketHeader(GSM::Time burstTime, double dBm, double TOA,
char *burstString);
/** format a packet of soft-bits to be sent over the network */
int formatDemodPacket(GSM::Time burstTime, double dBm, double TOA,
SoftVector *rxBurst, char *burstString);
/** format a packet of raw samples to be sent over the network */
int formatRawPacket(GSM::Time burstTime, double dBm, double TOA,
CorrType burstType, unsigned tsc,
signalVector *rxBurst, char *burstString);
/** drive transmission of GSM bursts */
void driveTxFIFO();

View File

@@ -58,6 +58,7 @@ enum uhd_dev_type {
USRP2,
B100,
B200,
B205,
B210,
E1XX,
E3XX,
@@ -73,6 +74,17 @@ struct uhd_dev_offset {
const std::string desc;
};
/*
* USRP version dependent device timings
*/
#ifdef USE_UHD_3_9
#define B2XX_TIMING_1SPS 1.7153e-4
#define B2XX_TIMING_4SPS 1.1696e-4
#else
#define B2XX_TIMING_1SPS 9.9692e-5
#define B2XX_TIMING_4SPS 6.9248e-5
#endif
/*
* Tx / Rx sample offset values. In a perfect world, there is no group delay
* though analog components, and behaviour through digital filters exactly
@@ -90,10 +102,12 @@ static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
{ USRP2, 4, 8.0230e-5, "N2XX 4 SPS" },
{ B100, 1, 1.2104e-4, "B100 1 SPS" },
{ B100, 4, 7.9307e-5, "B100 4 SPS" },
{ B200, 1, 9.9692e-5, "B200 1 SPS" },
{ B200, 4, 6.9248e-5, "B200 4 SPS" },
{ B210, 1, 9.9692e-5, "B210 1 SPS" },
{ B210, 4, 6.9248e-5, "B210 4 SPS" },
{ B200, 1, B2XX_TIMING_1SPS, "B200 1 SPS" },
{ B200, 4, B2XX_TIMING_4SPS, "B200 4 SPS" },
{ B205, 1, B2XX_TIMING_1SPS, "B200-mini 1 SPS" },
{ B205, 4, B2XX_TIMING_4SPS, "B200-mini 4 SPS" },
{ B210, 1, B2XX_TIMING_1SPS, "B210 1 SPS" },
{ B210, 4, B2XX_TIMING_4SPS, "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" },
@@ -184,6 +198,7 @@ static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
case B100:
return B100_BASE_RT * sps;
case B200:
case B205:
case B210:
case E1XX:
case E3XX:
@@ -197,23 +212,6 @@ static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
return -9999.99;
}
/** Timestamp conversion
@param timestamp a UHD or OpenBTS timestamp
@param rate sample rate
@return the converted timestamp
*/
uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
{
double secs = (double) ticks / rate;
return uhd::time_spec_t(secs);
}
TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
{
TIMESTAMP ticks = ts.get_full_secs() * rate;
return ts.get_tick_count(rate) + ticks;
}
/*
Sample Buffer - Allows reading and writing of timed samples using OpenBTS
or UHD style timestamps. Time conversions are handled
@@ -290,7 +288,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();
@@ -473,18 +471,24 @@ void uhd_device::init_gains()
tx_gain_min = range.start();
tx_gain_max = range.stop();
}
LOG(INFO) << "Supported Tx gain range [" << tx_gain_min << "; " << tx_gain_max << "]";
range = usrp_dev->get_rx_gain_range();
rx_gain_min = range.start();
rx_gain_max = range.stop();
LOG(INFO) << "Supported Rx gain range [" << rx_gain_min << "; " << rx_gain_max << "]";
for (size_t i = 0; i < tx_gains.size(); i++) {
usrp_dev->set_tx_gain((tx_gain_min + tx_gain_max) / 2, i);
double gain = (tx_gain_min + tx_gain_max) / 2;
LOG(INFO) << "Default setting Tx gain for channel " << i << " to " << gain;
usrp_dev->set_tx_gain(gain, i);
tx_gains[i] = usrp_dev->get_tx_gain(i);
}
for (size_t i = 0; i < rx_gains.size(); i++) {
usrp_dev->set_rx_gain((rx_gain_min + rx_gain_max) / 2, i);
double gain = (rx_gain_min + rx_gain_max) / 2;
LOG(INFO) << "Default setting Rx gain for channel " << i << " to " << gain;
usrp_dev->set_rx_gain(gain, i);
rx_gains[i] = usrp_dev->get_rx_gain(i);
}
@@ -523,7 +527,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) || (dev_type == E3XX)) {
if ((dev_type == B200) || (dev_type == B205) || (dev_type == B210) || (dev_type == E3XX)) {
if (set_master_clk(B2XX_CLK_RT) < 0)
return -1;
}
@@ -582,7 +586,7 @@ double uhd_device::setTxGain(double db, size_t chan)
tx_gains[chan] = usrp_dev->get_tx_gain(chan);
LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB";
LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
return tx_gains[chan];
}
@@ -597,7 +601,7 @@ double uhd_device::setRxGain(double db, size_t chan)
usrp_dev->set_rx_gain(db, chan);
rx_gains[chan] = usrp_dev->get_rx_gain(chan);
LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB";
LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
return rx_gains[chan];
}
@@ -623,7 +627,7 @@ 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, e310_str,
b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str;
b100_str, b200_str, b205_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();
@@ -633,6 +637,7 @@ bool uhd_device::parse_dev_type()
usrp2_str = dev_str.find("USRP2");
b100_str = mboard_str.find("B100");
b200_str = mboard_str.find("B200");
b205_str = mboard_str.find("B205");
b210_str = mboard_str.find("B210");
e100_str = mboard_str.find("E100");
e110_str = mboard_str.find("E110");
@@ -654,6 +659,9 @@ bool uhd_device::parse_dev_type()
} else if (b200_str != std::string::npos) {
tx_window = TX_WINDOW_USRP1;
dev_type = B200;
} else if (b205_str != std::string::npos) {
tx_window = TX_WINDOW_USRP1;
dev_type = B205;
} else if (b210_str != std::string::npos) {
tx_window = TX_WINDOW_USRP1;
dev_type = B210;
@@ -694,7 +702,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 +728,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) {
@@ -802,6 +810,7 @@ int uhd_device::open(const std::string &args, bool extref)
case X3XX:
return RESAMP_100M;
case B200:
case B205:
case B210:
case E1XX:
case E3XX:
@@ -839,7 +848,7 @@ bool uhd_device::flush_recv(size_t num_pkts)
}
}
ts_initial = convert_time(md.time_spec, rx_rate);
ts_initial = md.time_spec.to_ticks(rx_rate);
}
LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
@@ -976,7 +985,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
// Shift read time with respect to transmit clock
timestamp += ts_offset;
ts = convert_time(timestamp, rx_rate);
ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
// Check that timestamp is valid
@@ -1058,7 +1067,7 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
metadata.has_time_spec = true;
metadata.start_of_burst = false;
metadata.end_of_burst = false;
metadata.time_spec = convert_time(timestamp, tx_rate);
metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
*underrun = false;
@@ -1115,7 +1124,7 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
uhd::tune_request_t treq(freq);
if (dev_type == UMTRX) {
if (offset > 0.0)
if (offset != 0.0)
return uhd::tune_request_t(freq, offset);
// Don't use DSP tuning, because LMS6002D PLL steps are small enough.
@@ -1126,6 +1135,7 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
treq.rf_freq = freq;
treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
treq.dsp_freq = 0.0;
return treq;
} else if (chans == 1) {
if (offset == 0.0)
return treq;
@@ -1371,7 +1381,7 @@ ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
{
return avail_smpls(convert_time(timespec, clk_rt));
return avail_smpls(timespec.to_ticks(clk_rt));
}
ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
@@ -1417,7 +1427,7 @@ ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
{
return read(buf, len, convert_time(ts, clk_rt));
return read(buf, len, ts.to_ticks(clk_rt));
}
ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
@@ -1430,6 +1440,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 = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(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 = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(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;
@@ -1461,7 +1484,7 @@ ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
{
return write(buf, len, convert_time(ts, clk_rt));
return write(buf, len, ts.to_ticks(clk_rt));
}
std::string smpl_buf::str_status(size_t ts) const

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

@@ -1,7 +1,7 @@
#ifndef _CONVERT_H_
#define _CONVERT_H_
void convert_float_short(short *out, float *in, float scale, int len);
void convert_short_float(float *out, short *in, int len);
void convert_float_short(short *out, const float *in, float scale, int len);
void convert_short_float(float *out, const short *in, int len);
#endif /* _CONVERT_H_ */

View File

@@ -3,26 +3,26 @@
void *convolve_h_alloc(int num);
int convolve_real(float *x, int x_len,
float *h, int h_len,
int convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset);
int convolve_complex(float *x, int x_len,
float *h, int h_len,
int convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset);
int base_convolve_real(float *x, int x_len,
float *h, int h_len,
int base_convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset);
int base_convolve_complex(float *x, int x_len,
float *h, int h_len,
int base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset);

View File

@@ -26,21 +26,21 @@
#endif
/* Base multiply and accumulate complex-real */
static void mac_real(float *x, float *h, float *y)
static void mac_real(const float *x, const float *h, float *y)
{
y[0] += x[0] * h[0];
y[1] += x[1] * h[0];
}
/* Base multiply and accumulate complex-complex */
static void mac_cmplx(float *x, float *h, float *y)
static void mac_cmplx(const float *x, const float *h, float *y)
{
y[0] += x[0] * h[0] - x[1] * h[1];
y[1] += x[0] * h[1] + x[1] * h[0];
}
/* Base vector complex-complex multiply and accumulate */
static void mac_real_vec_n(float *x, float *h, float *y,
static void mac_real_vec_n(const float *x, const float *h, float *y,
int len, int step, int offset)
{
for (int i = offset; i < len; i += step)
@@ -48,7 +48,7 @@ static void mac_real_vec_n(float *x, float *h, float *y,
}
/* Base vector complex-complex multiply and accumulate */
static void mac_cmplx_vec_n(float *x, float *h, float *y,
static void mac_cmplx_vec_n(const float *x, const float *h, float *y,
int len, int step, int offset)
{
for (int i = offset; i < len; i += step)
@@ -56,8 +56,8 @@ static void mac_cmplx_vec_n(float *x, float *h, float *y,
}
/* Base complex-real convolution */
int _base_convolve_real(float *x, int x_len,
float *h, int h_len,
int _base_convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset)
@@ -73,8 +73,8 @@ int _base_convolve_real(float *x, int x_len,
}
/* Base complex-complex convolution */
int _base_convolve_complex(float *x, int x_len,
float *h, int h_len,
int _base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset)
@@ -110,8 +110,8 @@ int bounds_check(int x_len, int h_len, int y_len,
}
/* API: Non-aligned (no SSE) complex-real */
int base_convolve_real(float *x, int x_len,
float *h, int h_len,
int base_convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset)
@@ -128,8 +128,8 @@ int base_convolve_real(float *x, int x_len,
}
/* API: Non-aligned (no SSE) complex-complex */
int base_convolve_complex(float *x, int x_len,
float *h, int h_len,
int base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset)

View File

@@ -70,6 +70,9 @@ struct trx_config {
Transceiver::FillerType filler;
bool diversity;
double offset;
double rssi_offset;
bool swap_channels;
bool external_demod;
};
ConfigurationTable gConfig;
@@ -185,6 +188,9 @@ 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;
ost << " External demodulator.... " << config->external_demod << std::endl;
std::cout << ost << std::endl;
return true;
@@ -240,7 +246,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, config->external_demod);
if (!trx->init(config->filler, config->rtsc)) {
LOG(ALERT) << "Failed to initialize transceiver";
delete trx;
@@ -292,8 +298,11 @@ 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",
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
" -r Random burst test mode with TSC\n"
" -R RSSI to dBm offset in dB (default=0)\n"
" -S Swap channels (UmTRX only)\n"
" -e External demodulator - stream raw samples instead of soft bits (default=internal)\n",
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
}
static void handle_options(int argc, char **argv, struct trx_config *config)
@@ -308,8 +317,11 @@ 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;
config->external_demod = 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:Se")) != -1) {
switch (option) {
case 'h':
print_help();
@@ -349,6 +361,15 @@ 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;
case 'e':
config->external_demod = true;
break;
default:
print_help();
exit(0);
@@ -393,7 +414,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"
@@ -146,64 +147,8 @@ void sigProcLibDestroy()
GSMPulse1 = NULL;
}
// dB relative to 1.0.
// if > 1.0, then return 0 dB
float dB(float x) {
float arg = 1.0F;
float dB = 0.0F;
if (x >= 1.0F) return 0.0F;
if (x <= 0.0F) return -200.0F;
float prevArg = arg;
float prevdB = dB;
float stepSize = 16.0F;
float dBstepSize = 12.0F;
while (stepSize > 1.0F) {
do {
prevArg = arg;
prevdB = dB;
arg /= stepSize;
dB -= dBstepSize;
} while (arg > x);
arg = prevArg;
dB = prevdB;
stepSize *= 0.5F;
dBstepSize -= 3.0F;
}
return ((arg-x)*(dB-3.0F) + (x-arg*0.5F)*dB)/(arg - arg*0.5F);
}
// 10^(-dB/10), inverse of dB func.
float dBinv(float x) {
float arg = 1.0F;
float dB = 0.0F;
if (x >= 0.0F) return 1.0F;
if (x <= -200.0F) return 0.0F;
float prevArg = arg;
float prevdB = dB;
float stepSize = 16.0F;
float dBstepSize = 12.0F;
while (stepSize > 1.0F) {
do {
prevArg = arg;
prevdB = dB;
arg /= stepSize;
dB -= dBstepSize;
} while (dB > x);
arg = prevArg;
dB = prevdB;
stepSize *= 0.5F;
dBstepSize -= 3.0F;
}
return ((dB-x)*(arg*0.5F)+(x-(dB-3.0F))*(arg))/3.0F;
double dB2(double x) {
return 20.0 * log10(x);
}
float vectorNorm2(const signalVector &x)
@@ -1284,12 +1229,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 +1317,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 +1394,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 +1422,23 @@ 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,
bool chan_req, signalVector **chan, float *chan_offset)
int sps, complex &amp, float &toa, unsigned max_toa)
{
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;
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 */
_toa -= head * sps;
if (toa)
*toa = _toa;
if (amp)
*amp = _amp;
/* Equalization not currently supported */
if (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)
@@ -1560,166 +1494,6 @@ SoftVector *demodulateBurst(signalVector &rxBurst, int sps,
return bits;
}
// Assumes symbol-spaced sampling!!!
// Based upon paper by Al-Dhahir and Cioffi
bool designDFE(signalVector &channelResponse,
float SNRestimate,
int Nf,
signalVector **feedForwardFilter,
signalVector **feedbackFilter)
{
signalVector G0(Nf);
signalVector G1(Nf);
signalVector::iterator G0ptr = G0.begin();
signalVector::iterator G1ptr = G1.begin();
signalVector::iterator chanPtr = channelResponse.begin();
int nu = channelResponse.size()-1;
*G0ptr = 1.0/sqrtf(SNRestimate);
for(int j = 0; j <= nu; j++) {
*G1ptr = chanPtr->conj();
G1ptr++; chanPtr++;
}
signalVector *L[Nf];
signalVector::iterator Lptr;
float d = 1.0;
for(int i = 0; i < Nf; i++) {
d = G0.begin()->norm2() + G1.begin()->norm2();
L[i] = new signalVector(Nf+nu);
Lptr = L[i]->begin()+i;
G0ptr = G0.begin(); G1ptr = G1.begin();
while ((G0ptr < G0.end()) && (Lptr < L[i]->end())) {
*Lptr = (*G0ptr*(G0.begin()->conj()) + *G1ptr*(G1.begin()->conj()) )/d;
Lptr++;
G0ptr++;
G1ptr++;
}
complex k = (*G1.begin())/(*G0.begin());
if (i != Nf-1) {
signalVector G0new = G1;
scaleVector(G0new,k.conj());
addVector(G0new,G0);
signalVector G1new = G0;
scaleVector(G1new,k*(-1.0));
addVector(G1new,G1);
delayVector(&G1new, &G1new, -1.0);
scaleVector(G0new,1.0/sqrtf(1.0+k.norm2()));
scaleVector(G1new,1.0/sqrtf(1.0+k.norm2()));
G0 = G0new;
G1 = G1new;
}
}
*feedbackFilter = new signalVector(nu);
L[Nf-1]->segmentCopyTo(**feedbackFilter,Nf,nu);
scaleVector(**feedbackFilter,(complex) -1.0);
conjugateVector(**feedbackFilter);
signalVector v(Nf);
signalVector::iterator vStart = v.begin();
signalVector::iterator vPtr;
*(vStart+Nf-1) = (complex) 1.0;
for(int k = Nf-2; k >= 0; k--) {
Lptr = L[k]->begin()+k+1;
vPtr = vStart + k+1;
complex v_k = 0.0;
for (int j = k+1; j < Nf; j++) {
v_k -= (*vPtr)*(*Lptr);
vPtr++; Lptr++;
}
*(vStart + k) = v_k;
}
*feedForwardFilter = new signalVector(Nf);
signalVector::iterator w = (*feedForwardFilter)->end();
for (int i = 0; i < Nf; i++) {
delete L[i];
complex w_i = 0.0;
int endPt = ( nu < (Nf-1-i) ) ? nu : (Nf-1-i);
vPtr = vStart+i;
chanPtr = channelResponse.begin();
for (int k = 0; k < endPt+1; k++) {
w_i += (*vPtr)*(chanPtr->conj());
vPtr++; chanPtr++;
}
*--w = w_i/d;
}
return true;
}
// Assumes symbol-rate sampling!!!!
SoftVector *equalizeBurst(signalVector &rxBurst,
float TOA,
int sps,
signalVector &w, // feedforward filter
signalVector &b) // feedback filter
{
signalVector *postForwardFull;
if (!delayVector(&rxBurst, &rxBurst, -TOA))
return NULL;
postForwardFull = convolve(&rxBurst, &w, NULL,
CUSTOM, 0, rxBurst.size() + w.size() - 1);
if (!postForwardFull)
return NULL;
signalVector* postForward = new signalVector(rxBurst.size());
postForwardFull->segmentCopyTo(*postForward,w.size()-1,rxBurst.size());
delete postForwardFull;
signalVector::iterator dPtr = postForward->begin();
signalVector::iterator dBackPtr;
signalVector::iterator rotPtr = GMSKRotationN->begin();
signalVector::iterator revRotPtr = GMSKReverseRotationN->begin();
signalVector *DFEoutput = new signalVector(postForward->size());
signalVector::iterator DFEItr = DFEoutput->begin();
// NOTE: can insert the midamble and/or use midamble to estimate BER
for (; dPtr < postForward->end(); dPtr++) {
dBackPtr = dPtr-1;
signalVector::iterator bPtr = b.begin();
while ( (bPtr < b.end()) && (dBackPtr >= postForward->begin()) ) {
*dPtr = *dPtr + (*bPtr)*(*dBackPtr);
bPtr++;
dBackPtr--;
}
*dPtr = *dPtr * (*revRotPtr);
*DFEItr = *dPtr;
// make decision on symbol
*dPtr = (dPtr->real() > 0.0) ? 1.0 : -1.0;
//*DFEItr = *dPtr;
*dPtr = *dPtr * (*rotPtr);
DFEItr++;
rotPtr++;
revRotPtr++;
}
vectorSlicer(DFEoutput);
SoftVector *burstBits = new SoftVector(postForward->size());
SoftVector::iterator burstItr = burstBits->begin();
DFEItr = DFEoutput->begin();
for (; DFEItr < DFEoutput->end(); DFEItr++)
*burstItr++ = DFEItr->real();
delete postForward;
delete DFEoutput;
return burstBits;
}
bool sigProcLibSetup(int sps)
{
if ((sps != 1) && (sps != 4))

View File

@@ -36,8 +36,8 @@ enum signalError {
SIGERR_INTERNAL,
};
/** Convert a linear number to a dB value */
float dB(float x);
/** Convert a power value to a dB value */
double dB2(double x);
/** Convert a dB value into a linear value */
float dBinv(float x);
@@ -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.
@@ -204,21 +204,15 @@ int detectRACHBurst(signalVector &rxBurst,
@param amplitude The estimated amplitude of received TSC burst.
@param TOA The estimate time-of-arrival of received TSC burst.
@param maxTOA The maximum expected time-of-arrival
@param requestChannel Set to true if channel estimation is desired.
@param channelResponse The estimated channel.
@param channelResponseOffset The time offset b/w the first sample of the channel response and the reported TOA.
@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 maxTOA,
bool requestChannel = false,
signalVector** channelResponse = NULL,
float *channelResponseOffset = NULL);
unsigned TSC,
float detectThreshold,
int sps,
complex &amplitude,
float &TOA,
unsigned maxTOA);
/**
Decimate a vector.
@@ -240,34 +234,4 @@ signalVector *decimateVector(signalVector &wVector, size_t factor);
SoftVector *demodulateBurst(signalVector &rxBurst, int sps,
complex channel, float TOA);
/**
Design the necessary filters for a decision-feedback equalizer.
@param channelResponse The multipath channel that we're mitigating.
@param SNRestimate The signal-to-noise estimate of the channel, a linear value
@param Nf The number of taps in the feedforward filter.
@param feedForwardFilter The designed feed forward filter.
@param feedbackFilter The designed feedback filter.
@return True if DFE can be designed.
*/
bool designDFE(signalVector &channelResponse,
float SNRestimate,
int Nf,
signalVector **feedForwardFilter,
signalVector **feedbackFilter);
/**
Equalize/demodulate a received burst via a decision-feedback equalizer.
@param rxBurst The received burst to be demodulated.
@param TOA The time-of-arrival of the received burst.
@param sps The number of samples per GSM symbol.
@param w The feed forward filter of the DFE.
@param b The feedback filter of the DFE.
@return The demodulated bit sequence.
*/
SoftVector *equalizeBurst(signalVector &rxBurst,
float TOA,
int sps,
signalVector &w,
signalVector &b);
#endif /* SIGPROCLIB_H */

View File

@@ -1,5 +1,5 @@
if !ARCH_ARM
AM_CFLAGS = -Wall -std=gnu99 -march=native -I../common
AM_CFLAGS = -Wall -std=gnu99 -I../common
noinst_LTLIBRARIES = libarch.la

View File

@@ -34,7 +34,7 @@
/* 16*N 16-bit signed integer converted to single precision floats */
static void _sse_convert_si16_ps_16n(float *restrict out,
short *restrict in,
const short *restrict in,
int len)
{
__m128i m0, m1, m2, m3, m4, m5;
@@ -69,7 +69,7 @@ static void _sse_convert_si16_ps_16n(float *restrict out,
/* 16*N 16-bit signed integer conversion with remainder */
static void _sse_convert_si16_ps(float *restrict out,
short *restrict in,
const short *restrict in,
int len)
{
int start = len / 16 * 16;
@@ -83,7 +83,7 @@ static void _sse_convert_si16_ps(float *restrict out,
/* 8*N single precision floats scaled and converted to 16-bit signed integer */
static void _sse_convert_scale_ps_si16_8n(short *restrict out,
float *restrict in,
const float *restrict in,
float scale, int len)
{
__m128 m0, m1, m2;
@@ -111,7 +111,7 @@ static void _sse_convert_scale_ps_si16_8n(short *restrict out,
/* 8*N single precision floats scaled and converted with remainder */
static void _sse_convert_scale_ps_si16(short *restrict out,
float *restrict in,
const float *restrict in,
float scale, int len)
{
int start = len / 8 * 8;
@@ -124,7 +124,7 @@ static void _sse_convert_scale_ps_si16(short *restrict out,
/* 16*N single precision floats scaled and converted to 16-bit signed integer */
static void _sse_convert_scale_ps_si16_16n(short *restrict out,
float *restrict in,
const float *restrict in,
float scale, int len)
{
__m128 m0, m1, m2, m3, m4;
@@ -158,7 +158,8 @@ static void _sse_convert_scale_ps_si16_16n(short *restrict out,
}
}
#else /* HAVE_SSE3 */
static void convert_scale_ps_si16(short *out, float *in, float scale, int len)
static void convert_scale_ps_si16(short *out, const float *in,
float scale, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i] * scale;
@@ -166,14 +167,14 @@ static void convert_scale_ps_si16(short *out, float *in, float scale, int len)
#endif
#ifndef HAVE_SSE4_1
static void convert_si16_ps(float *out, short *in, int len)
static void convert_si16_ps(float *out, const short *in, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i];
}
#endif
void convert_float_short(short *out, float *in, float scale, int len)
void convert_float_short(short *out, const float *in, float scale, int len)
{
#ifdef HAVE_SSE3
if (!(len % 16))
@@ -187,7 +188,7 @@ void convert_float_short(short *out, float *in, float scale, int len)
#endif
}
void convert_short_float(float *out, short *in, int len)
void convert_short_float(float *out, const short *in, int len)
{
#ifdef HAVE_SSE4_1
if (!(len % 16))

View File

@@ -27,14 +27,14 @@
#endif
/* Forward declarations from base implementation */
int _base_convolve_real(float *x, int x_len,
float *h, int h_len,
int _base_convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset);
int _base_convolve_complex(float *x, int x_len,
float *h, int h_len,
int _base_convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset);
@@ -47,8 +47,8 @@ int bounds_check(int x_len, int h_len, int y_len,
#include <pmmintrin.h>
/* 4-tap SSE complex-real convolution */
static void sse_conv_real4(float *restrict x,
float *restrict h,
static void sse_conv_real4(const float *restrict x,
const float *restrict h,
float *restrict y,
int len)
{
@@ -81,8 +81,8 @@ static void sse_conv_real4(float *restrict x,
}
/* 8-tap SSE complex-real convolution */
static void sse_conv_real8(float *restrict x,
float *restrict h,
static void sse_conv_real8(const float *restrict x,
const float *restrict h,
float *restrict y,
int len)
{
@@ -128,8 +128,8 @@ static void sse_conv_real8(float *restrict x,
}
/* 12-tap SSE complex-real convolution */
static void sse_conv_real12(float *restrict x,
float *restrict h,
static void sse_conv_real12(const float *restrict x,
const float *restrict h,
float *restrict y,
int len)
{
@@ -190,8 +190,8 @@ static void sse_conv_real12(float *restrict x,
}
/* 16-tap SSE complex-real convolution */
static void sse_conv_real16(float *restrict x,
float *restrict h,
static void sse_conv_real16(const float *restrict x,
const float *restrict h,
float *restrict y,
int len)
{
@@ -265,8 +265,8 @@ static void sse_conv_real16(float *restrict x,
}
/* 20-tap SSE complex-real convolution */
static void sse_conv_real20(float *restrict x,
float *restrict h,
static void sse_conv_real20(const float *restrict x,
const float *restrict h,
float *restrict y,
int len)
{
@@ -351,7 +351,10 @@ static void sse_conv_real20(float *restrict x,
}
/* 4*N-tap SSE complex-real convolution */
static void sse_conv_real4n(float *x, float *h, float *y, int h_len, int len)
static void sse_conv_real4n(const float *x,
const float *h,
float *y,
int h_len, int len)
{
__m128 m0, m1, m2, m4, m5, m6, m7;
@@ -391,7 +394,10 @@ static void sse_conv_real4n(float *x, float *h, float *y, int h_len, int len)
}
/* 4*N-tap SSE complex-complex convolution */
static void sse_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
static void sse_conv_cmplx_4n(const float *x,
const float *h,
float *y,
int h_len, int len)
{
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
@@ -439,7 +445,10 @@ static void sse_conv_cmplx_4n(float *x, float *h, float *y, int h_len, int len)
}
/* 8*N-tap SSE complex-complex convolution */
static void sse_conv_cmplx_8n(float *x, float *h, float *y, int h_len, int len)
static void sse_conv_cmplx_8n(const float *x,
const float *h,
float *y,
int h_len, int len)
{
__m128 m0, m1, m2, m3, m4, m5, m6, m7;
__m128 m8, m9, m10, m11, m12, m13, m14, m15;
@@ -511,14 +520,16 @@ static void sse_conv_cmplx_8n(float *x, float *h, float *y, int h_len, int len)
#endif
/* API: Aligned complex-real */
int convolve_real(float *x, int x_len,
float *h, int h_len,
int convolve_real(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset)
{
void (*conv_func)(float *, float *, float *, int) = NULL;
void (*conv_func_n)(float *, float *, float *, int, int) = NULL;
void (*conv_func)(const float *, const float *,
float *, int) = NULL;
void (*conv_func_n)(const float *, const float *,
float *, int, int) = NULL;
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
return -1;
@@ -566,13 +577,14 @@ int convolve_real(float *x, int x_len,
}
/* API: Aligned complex-complex */
int convolve_complex(float *x, int x_len,
float *h, int h_len,
int convolve_complex(const float *x, int x_len,
const float *h, int h_len,
float *y, int y_len,
int start, int len,
int step, int offset)
{
void (*conv_func)(float *, float *, float *, int, int) = NULL;
void (*conv_func)(const float *, const float *,
float *, int, int) = NULL;
if (bounds_check(x_len, h_len, y_len, start, len, step) < 0)
return -1;

View File

@@ -97,8 +97,11 @@ AS_IF([test "x$with_usrp1" = "xyes"], [
])
AS_IF([test "x$with_usrp1" != "xyes"],[
PKG_CHECK_MODULES(UHD, uhd >= 003.004.000)
AC_DEFINE(USE_UHD, 1, Define to 1 if using UHD)
PKG_CHECK_MODULES(UHD, uhd >= 003.009,
[AC_DEFINE(USE_UHD_3_9, 1, UHD version 3.9.0 or higher)],
[PKG_CHECK_MODULES(UHD, uhd >= 003.005.004)]
)
AC_DEFINE(USE_UHD, 1, All UHD versions)
])
AS_IF([test "x$with_singledb" = "xyes"], [

19
debian/changelog vendored Normal file
View File

@@ -0,0 +1,19 @@
osmo-trx (0.1.9~1) trusty; urgency=medium
* minor debian changes
* correct atom cross-compilation
* corrected build-time and run-time dependencies
-- Kirill Zakharenko <earwin@gmail.com> Thu, 23 Jul 2015 00:01:07 +0000
osmo-trx (0.1.9) trusty; urgency=medium
* Ask Ivan, really
-- Kirill Zakharenko <earwin@gmail.com> Thu, 16 Jul 2015 12:13:46 +0000
osmo-trx (0.1.8) precise; urgency=low
* Initial release (Closes: #nnnn) <nnnn is the bug number of your ITP
-- Ivan Klyuchnikov <Ivan.Kluchnikov@fairwaves.ru> Sun, 9 Mar 2014 14:10:10 +0400

1
debian/compat vendored Normal file
View File

@@ -0,0 +1 @@
9

24
debian/control vendored Normal file
View File

@@ -0,0 +1,24 @@
Source: osmo-trx
Maintainer: Ivan Klyuchnikov <ivan.kluchnikov@fairwaves.ru>
Section: net
Priority: optional
Standards-Version: 3.9.3
Build-Depends: debhelper (>= 9), autotools-dev, libdbd-sqlite3, pkg-config, dh-autoreconf, libuhd-dev, libusb-1.0-0-dev, libboost-all-dev, hardening-wrapper
Homepage: http://openbsc.osmocom.org/trac/wiki/OsmoTRX
Vcs-Git: git://git.osmocom.org/osmo-trx
Vcs-Browser: http://cgit.osmocom.org/osmo-trx
Package: osmo-trx
Architecture: any
Depends: ${shlibs:Depends}, ${misc:Depends}, libdbd-sqlite3
Description: OsmoTRX is a software-defined radio transceiver that implements the Layer 1 physical layer of a BTS
Package: osmo-trx-dbg
Architecture: any
Section: debug
Priority: extra
Depends: osmo-trx (= ${binary:Version}), ${misc:Depends}
Description: Debug symbols for the osmo-trx
Make debugging possible

25
debian/copyright vendored Normal file
View File

@@ -0,0 +1,25 @@
The Debian packaging is:
Copyright (C) 2014 Max <max.suraev@fairwaves.ru>
It was downloaded from:
git://git.osmocom.org/osmo-trx
Upstream Authors:
Thomas Tsou <tom@tsou.cc>
David A. Burgess <dburgess@kestrelsp.com>
Harvind S. Samra <hssamra@kestrelsp.com>
Raffi Sevlian <raffisev@gmail.com>
Copyright:
Copyright (C) 2012-2013 Thomas Tsou <tom@tsou.cc>
Copyright (C) 2011 Range Networks, Inc.
Copyright (C) 2008-2011 Free Software Foundation, Inc.
License:
GNU Affero General Public License, Version 3

1
debian/osmo-trx.install vendored Normal file
View File

@@ -0,0 +1 @@
/usr/bin/osmo-trx

15
debian/rules vendored Executable file
View File

@@ -0,0 +1,15 @@
#!/usr/bin/make -f
DEB_BUILD_HARDENING=1
%:
dh $@ --with autoreconf
override_dh_auto_configure:
dh_auto_configure -- --without-sse CFLAGS="-DHAVE_SSE3 -march=atom -mtune=atom -O2" CXXFLAGS="-DHAVE_SSE3 -march=atom -mtune=atom -O2"
override_dh_shlibdeps:
dh_shlibdeps --dpkg-shlibdeps-params=--ignore-missing-info
override_dh_strip:
dh_strip --dbg-package=osmo-trx-dbg

1
debian/source/format vendored Normal file
View File

@@ -0,0 +1 @@
3.0 (native)

3
utils/clockdump.sh Executable file
View File

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