Compare commits

...

46 Commits

Author SHA1 Message Date
Kirill Zakharenko
a336f254dc bump version to 0.1.10~1 2016-03-28 11:46:20 +02:00
Kirill Zakharenko
43fa3cab67 debian: compile for atom arch with SSE3 optimizations 2016-03-28 11:34:23 +02:00
Alexander Chemeris
aa65b020e7 transceiver: WIP: Set default max delay to 2 samples.
Default value of 0 may be too harsh, especially given random Rx/Tx delay
in 1 SPS receive mode.
2016-03-25 18:48:28 +03:00
Alexander Chemeris
72d4404534 transceiver: Add an option to emulate a RACH delay in random filler mode. 2016-03-25 18:48:28 +03:00
Alexander Chemeris
7ed93551ba sigProcLib: Change number of head bits in detectRACHBurst() from 4 to 8.
To match GSM 05.02 Access Burst definition.
2016-03-25 18:48:28 +03:00
Alexander Chemeris
9460154b36 transceiver: Add an option to generate random Access Bursts. 2016-03-25 18:48:28 +03:00
Alexander Chemeris
2f09d69063 transceiver: Properly handle MAXDLY.
Previously MAXDLY value was applied to Normal Bursts, which was nice
when working with sloppy test equipment like CMD57, but useless for
real world usage. At the same time documentation and de facto usage
of MAXDLY in OsmoBTS and OpenBTS assumed that it actually applies to
Access Bursts (RACH). So this patch changes osmo-rx behavior to apply
MAXDLY to RACH bursts and introduces a new command MAXDLYNB for the
old behavior.
2016-03-25 18:48:28 +03:00
Alexander Chemeris
140a2076d9 transceiver: Log channel number in DEBUG output of demoded bursts. 2016-03-25 18:47:27 +03:00
Alexander Chemeris
67a8fb9d51 Common: Make sure gLogEarly() log to the same facilities as the normal log. 2016-03-25 18:47:27 +03:00
Alexander Chemeris
cd3d0e616d 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.
2016-03-25 18:47:27 +03:00
Tom Tsou
9bd649ec73 EDGE: Fix USRP B210 device support
Commit 871b8782 "EDGE: Add support for UmTRX" disabled B210 support
using EDGE. Add B210 explicitly to the timing offset table to avoid
this issue.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-23 17:20:08 -07:00
Alexander Chemeris
871b87829f EDGE: Add support for UmTRX.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-22 11:01:34 -07:00
Holger Hans Peter Freyther
d17b189cbc debian: Add packaging to master taken from fairwaves/master
There doesn't seem to be a reason why this shouldn't be in master.
The fairwaves/master branch is removing --march=native as well that
looks like a good idea as well.
2016-03-18 20:22:58 +01:00
Tom Tsou
7fec3030d4 EDGE: Combine shared GMSK and 8-PSK demodulator sections
Timing recovery and single tap channel compensation are identical
in both GMSK and EDGE receivers. This is the section ahead of and
including the optional 4-1 downsampler. GMSK and EDGE specific
sections operate at 1 SPS.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-08 17:45:53 -08:00
Tom Tsou
af717b2d3c EDGE: Add random burst generator filler option
When EDGE is enabled with the '-e' option, the random burst generator
switches from GMSK normal bursts to 8-PSK EDGE bursts.

  $ ./osmo-trx -e -r 7

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-08 17:45:53 -08:00
Tom Tsou
8ee2f38a87 sigproc: Add various GSM burst generators
Setup generators for empty, random, and dummy bursts. This moves error
prone burst length handling out of the Transceiver and into the signal
processing core.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-08 17:45:53 -08:00
Tom Tsou
4dfd64aa9e sigproc: Always use 625 sample length bursts with 4 SPS
At 4 samples per symbol, we don't need to maintain the 156/157 sample
slot structure to account for the GSM 156.25 sample burst length.
Set the 4 SPS Laurent modulator to ignore the guard interval setting
and always output 625 sample sized bursts. The EDGE 8-PSK modulator
already has this behavior.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-08 17:45:53 -08:00
Tom Tsou
b0aefcbf47 EDGE: Add interfaces to enable EDGE transceiver
Create EDGE slot type in the Transceiver. When EDGE mode is enabled
for a particular slot, blind detection will be performed by
correlating against EDGE followed by normal bursts if no EDGE burst
is found.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-08 17:44:53 -08:00
Tom Tsou
d325343ecc EDGE: Add 8-PSK modulator and demodulator
Setup correlator and detection process similar to the GMSK
receiver chain. Require 4 SPS sampling on both Rx and Tx paths
as 1 SPS sampling adds too much distoration for 8-PSK recovery.
Core receiver operations still run at 1 SPS with the exception
of fractional delay filtering, which runs at the higher rate.

Perform linear equalization to handle the Gaussian pulse
induced ISI. The fixed impulse response used for equalizer tap
calculation consists of combined EDGE pulse shape filter and
effects of the downsampling filter. Note that the non-adaptive
equalizer corrects for modulation induced band limiting and
does not account for or compensate for fading channel effects.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-06 20:29:27 -08:00
Tom Tsou
5cd70dc4ec EDGE: Setup variable sampling on receive path
Allow setting the device to non single SPS sample rates - mainly
running at 4 SPS as the signal processing library does not support
other rates. Wider bandwith support is required on the receive path
to avoid 8-PSK bandlimiting distortion for EDGE.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-06 19:11:05 -08:00
Tom Tsou
465694027b sigproc: Remove normal burst DFE equalizer
DFE equalizer is unused and has been experiencing code rot for
multiple years. The effect is a significant amount of baggage being
carried in the Transceiver and interfaces.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-06 19:10:59 -08:00
Tom Tsou
2079a3c664 sigproc: Remove dynamic SPS configuration
Samples per symbol used by the transceiver is not configurable through
the socket interface once running, so stop pretending like it could be.
Initialize all tables and midambles at start.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2016-03-06 19:10:46 -08:00
Tom Tsou
99cf930f9a Transceiver52M: Fix ARM build issues
Patch f147b174 "sigproc: Make convolution and convert input buffers
immutable" changed the internal conversion interface with the addition
of the const type qualifier. This change was not reflected on ARM builds
which led to build failure. Add const qualifier to resolve build issue.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-11-09 12:06:33 -08:00
Tom Tsou
283b22dbce uhd: Remove references to USRP B205
Certain pre-release versions of the B200mini used the B205 naming, which no
longer exists. Update device naming and detection to reflect current UHD
product names.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-10-21 17:13:24 -07: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
33 changed files with 1639 additions and 942 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);
}

View File

@@ -41,10 +41,24 @@ const BitVector GSM::gTrainingSequence[] = {
BitVector("11101111000100101110111100"),
};
const BitVector GSM::gEdgeTrainingSequence[] = {
BitVector("111111001111111001111001001001111111111111001111111111001111111001111001001001"),
BitVector("111111001111001001111001001001111001001001001111111111001111001001111001001001"),
BitVector("111001111111111111001001001111001001001111001111111001111111111111001001001111"),
BitVector("111001111111111001001001001111001001111001111111111001111111111001001001001111"),
BitVector("111111111001001111001111001001001111111001111111111111111001001111001111001001"),
BitVector("111001111111001001001111001111001001111111111111111001111111001001001111001111"),
BitVector("001111001111111001001001001001111001001111111111001111001111111001001001001001"),
BitVector("001001001111001001001001111111111001111111001111001001001111001001001001111111"),
};
const BitVector GSM::gDummyBurst("0001111101101110110000010100100111000001001000100000001111100011100010111000101110001010111010010100011001100111001111010011111000100101111101010000");
const BitVector GSM::gRACHSynchSequence("01001011011111111001100110101010001111000");
// |-head-||---------midamble----------------------||--------------data----------------||t|
const BitVector GSM::gRACHBurst("0011101001001011011111111001100110101010001111000110111101111110000111001001010110011000");
int32_t GSM::FNDelta(int32_t v1, int32_t v2)
{

View File

@@ -46,12 +46,15 @@ namespace GSM {
/** GSM Training sequences from GSM 05.02 5.2.3. */
extern const BitVector gTrainingSequence[];
extern const BitVector gEdgeTrainingSequence[];
/** C0T0 filler burst, GSM 05.02, 5.2.6 */
extern const BitVector gDummyBurst;
/** Random access burst synch. sequence */
extern const BitVector gRACHSynchSequence;
/** Random access burst synch. sequence, GSM 05.02 5.2.7 */
extern const BitVector gRACHBurst;
/**@name Modulus operations for frame numbers. */

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

@@ -173,10 +173,15 @@ int Resampler::rotate(float *in, size_t in_len, float *out, size_t out_len)
int hist_len = filt_len - 1;
if (!check_vec_len(in_len, out_len, p, q))
return -1;
return -1;
/* Insert history */
memcpy(&in[-2 * hist_len], history, hist_len * 2 * sizeof(float));
if (history_on) {
memcpy(&in[-2 * hist_len],
history, hist_len * 2 * sizeof(float));
} else {
memset(&in[-2 * hist_len], 0,
hist_len * 2 * sizeof(float));
}
/* Generate output from precomputed input/output paths */
for (size_t i = 0; i < out_len; i++) {
@@ -190,8 +195,10 @@ int Resampler::rotate(float *in, size_t in_len, float *out, size_t out_len)
}
/* Save history */
memcpy(history, &in[2 * (in_len - hist_len)],
hist_len * 2 * sizeof(float));
if (history_on) {
memcpy(history, &in[2 * (in_len - hist_len)],
hist_len * 2 * sizeof(float));
}
return out_len;
}
@@ -221,8 +228,14 @@ size_t Resampler::len()
return filt_len;
}
void Resampler::enableHistory(bool on)
{
history_on = on;
}
Resampler::Resampler(size_t p, size_t q, size_t filt_len)
: in_index(NULL), out_path(NULL), partitions(NULL), history(NULL)
: in_index(NULL), out_path(NULL), partitions(NULL),
history(NULL), history_on(true)
{
this->p = p;
this->q = q;

View File

@@ -59,6 +59,11 @@ public:
*/
size_t len();
/*
* Enable/disable history
*/
void enableHistory(bool on);
private:
size_t p;
size_t q;
@@ -68,6 +73,7 @@ private:
float **partitions;
float *history;
bool history_on;
bool initFilters(float bw);
void releaseFilters();

View File

@@ -22,6 +22,8 @@
*/
#include <stdio.h>
#include <iomanip> // std::setprecision
#include <fstream>
#include "Transceiver.h"
#include <Logger.h>
@@ -69,89 +71,67 @@ TransceiverState::~TransceiverState()
}
}
static BitVector *genRandNormalBurst(size_t tsc)
bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay)
{
if (tsc > 7)
return NULL;
BitVector *bits = new BitVector(148);
size_t i = 0;
/* Tail bits */
for (; i < 4; i++)
(*bits)[i] = 0;
/* Random bits */
for (; i < 61; i++)
(*bits)[i] = rand() % 2;
/* Training sequence */
for (int j = 0; i < 87; i++, j++)
(*bits)[i] = GSM::gTrainingSequence[tsc][j];
/* Random bits */
for (; i < 144; i++)
(*bits)[i] = rand() % 2;
/* Tail bits */
for (; i < 148; i++)
(*bits)[i] = 0;
return bits;
}
bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc)
{
BitVector *bits;
signalVector *burst;
if ((sps != 1) && (sps != 4))
return false;
for (size_t n = 0; n < 8; n++) {
size_t guard = 8 + !(n % 4);
size_t len = sps == 4 ? 625 : 148 + guard;
for (size_t i = 0; i < 102; i++) {
switch (filler) {
case Transceiver::FILLER_DUMMY:
burst = modulateBurst(gDummyBurst, guard, sps);
burst = generateDummyBurst(sps, n);
break;
case Transceiver::FILLER_RAND:
bits = genRandNormalBurst(rtsc);
burst = modulateBurst(*bits, guard, sps);
delete bits;
case Transceiver::FILLER_NORM_RAND:
burst = genRandNormalBurst(rtsc, sps, n);
break;
case Transceiver::FILLER_EDGE_RAND:
burst = generateEdgeBurst(rtsc);
break;
case Transceiver::FILLER_ACCESS_RAND:
burst = genRandAccessBurst(rach_delay, sps, n);
break;
case Transceiver::FILLER_ZERO:
default:
burst = new signalVector(len);
burst = generateEmptyBurst(sps, n);
}
scaleVector(*burst, scale);
fillerTable[i][n] = burst;
}
if (filler == Transceiver::FILLER_RAND)
chanType[n] = Transceiver::TSC;
if ((filler == Transceiver::FILLER_NORM_RAND) ||
(filler == Transceiver::FILLER_EDGE_RAND)) {
chanType[n] = Transceiver::TSC;
}
}
return false;
}
Transceiver::Transceiver(int wBasePort,
const char *wTRXAddress,
size_t wSPS, size_t wChans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface)
const char *wTRXAddress,
size_t tx_sps, size_t rx_sps, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface,
double wRssiOffset)
: mBasePort(wBasePort), mAddr(wTRXAddress),
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
mSPSTx(wSPS), mSPSRx(1), mChans(wChans), mOn(false),
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelay(0)
rssiOffset(wRssiOffset),
mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans), mOn(false),
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelayAB(2*rx_sps), mMaxExpectedDelayNB(2*rx_sps),
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()
@@ -180,7 +160,7 @@ Transceiver::~Transceiver()
* are still expected to report clock indications through control channel
* activity.
*/
bool Transceiver::init(int filler, size_t rtsc)
bool Transceiver::init(int filler, size_t rtsc, unsigned rach_delay)
{
int d_srcport, d_dstport, c_srcport, c_dstport;
@@ -189,7 +169,7 @@ bool Transceiver::init(int filler, size_t rtsc)
return false;
}
if (!sigProcLibSetup(mSPSTx)) {
if (!sigProcLibSetup()) {
LOG(ALERT) << "Failed to initialize signal processing library";
return false;
}
@@ -236,7 +216,7 @@ bool Transceiver::init(int filler, size_t rtsc)
if (i && filler == FILLER_DUMMY)
filler = FILLER_ZERO;
mStates[i].init(filler, mSPSTx, txFullScale, rtsc);
mStates[i].init(filler, mSPSTx, txFullScale, rtsc, rach_delay);
}
return true;
@@ -358,7 +338,12 @@ void Transceiver::addRadioVector(size_t chan, BitVector &bits,
return;
}
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
/* Use the number of bits as the EDGE burst indicator */
if (bits.size() == EDGE_BURST_NBITS)
burst = modulateEdgeBurst(bits, mSPSTx);
else
burst = modulateBurst(bits, 8 + (wTime.TN() % 4 == 0), mSPSTx);
scaleVector(*burst, txFullScale * pow(10, -RSSI / 10));
radio_burst = new radioVector(wTime, burst);
@@ -459,9 +444,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 +462,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 +495,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 +504,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;
@@ -527,104 +531,78 @@ 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::detectBurst(TransceiverState *state, signalVector &burst,
complex &amp, float &toa, CorrType type)
{
float threshold = 6.0;
float threshold = 5.0, rc = 0;
return detectRACHBurst(burst, threshold, mSPSRx, &amp, &toa);
switch (type) {
case EDGE:
rc = detectEdgeBurst(burst, mTSC, threshold, mSPSRx,
amp, toa, mMaxExpectedDelayNB);
if (rc > 0)
break;
else
type = TSC;
case TSC:
rc = analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx,
amp, toa, mMaxExpectedDelayNB);
break;
case RACH:
threshold = 6.0;
rc = detectRACHBurst(burst, threshold, mSPSRx, amp, toa,
mMaxExpectedDelayAB);
break;
default:
LOG(ERR) << "Invalid correlation type";
}
if (rc > 0)
return type;
return rc;
}
/*
* Detect normal burst training sequence midamble. Update equalization
* 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 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;
}
/* 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;;
}
/*
* Demodulate GMSK burst using equalization if requested. Otherwise
* demodulate by direct rotation and soft slicing.
* Demodulate GMSK by direct rotation and soft slicing.
*/
SoftVector *Transceiver::demodulate(TransceiverState *state,
signalVector &burst, complex amp,
float toa, size_t tn, bool equalize)
float toa, CorrType type)
{
if (equalize) {
scaleVector(burst, complex(1.0, 0.0) / amp);
return equalizeBurst(burst,
toa - state->chanRespOffset[tn],
mSPSRx,
*state->DFEForward[tn],
*state->DFEFeedback[tn]);
}
if (type == EDGE)
return demodEdgeBurst(burst, mSPSRx, amp, toa);
return demodulateBurst(burst, mSPSRx, amp, toa);
}
void writeToFile(radioVector *radio_burst, size_t chan)
{
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::pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
double &timingOffset, double &noise,
size_t chan)
{
bool success, equalize = false;
int rc;
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 +613,15 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
GSM::Time time = radio_burst->getTime();
CorrType type = expectedCorrType(time, chan);
if ((type == OFF) || (type == IDLE)) {
/* Debug: dump bursts to disk */
/* bits 0-7 - chan 0 timeslots
* bits 8-15 - chan 1 timeslots */
if (mWriteBurstToDiskMask & ((1<<time.TN()) << (8*chan)))
writeToFile(radio_burst, chan);
/* No processing if the timeslot is off.
* Not even power level or noise calculation. */
if (type == OFF) {
delete radio_burst;
return NULL;
}
@@ -659,41 +645,47 @@ 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)
success = detectTSC(state, *burst, amp, toa, time);
else
success = detectRACH(state, *burst, amp, toa);
rc = detectBurst(state, *burst, amp, toa, type);
/* 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";
if (rc > 0) {
type = (CorrType) rc;
} else if (rc <= 0) {
if (rc == -SIGERR_CLIP) {
LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
} else if (rc != SIGERR_NONE) {
LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
}
delete radio_burst;
return NULL;
}
/* Demodulate and set output info */
if (equalize && (type != TSC))
equalize = false;
timingOffset = toa / mSPSRx;
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, type);
delete radio_burst;
return bits;
}
@@ -743,14 +735,39 @@ 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
int maxDelay;
sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
mMaxExpectedDelay = maxDelay; // 1 GSM symbol is approx. 1 km
mMaxExpectedDelayAB = maxDelay; // 1 GSM symbol is approx. 1 km
sprintf(response,"RSP SETMAXDLY 0 %d",maxDelay);
}
else if (strcmp(command,"SETMAXDLYNB")==0) {
//set expected maximum time-of-arrival
int maxDelay;
sscanf(buffer,"%3s %s %d",cmdcheck,command,&maxDelay);
mMaxExpectedDelayNB = maxDelay; // 1 GSM symbol is approx. 1 km
sprintf(response,"RSP SETMAXDLYNB 0 %d",maxDelay);
}
else if (strcmp(command,"SETRXGAIN")==0) {
//set expected maximum time-of-arrival
int newGain;
@@ -817,7 +834,6 @@ void Transceiver::driveControl(size_t chan)
sprintf(response, "RSP SETTSC 1 %d", TSC);
else {
mTSC = TSC;
generateMidamble(mSPSRx, TSC);
sprintf(response,"RSP SETTSC 0 %d", TSC);
}
}
@@ -836,6 +852,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");
@@ -889,40 +913,63 @@ void Transceiver::driveReceiveRadio()
}
}
void Transceiver::logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
double rssi, double noise, double toa)
{
LOG(DEBUG) << std::fixed << std::right
<< " chan: " << chan
<< " time: " << time
<< " 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: " << *burst;
}
void Transceiver::driveReceiveFIFO(size_t chan)
{
SoftVector *rxBurst = NULL;
int RSSI;
int TOA; // in 1/256 of a symbol
double RSSI; // in dBFS
double dBm; // in dBm
double TOA; // in symbols
int TOAint; // in 1/256 symbols
double noise; // noise level in dBFS
GSM::Time burstTime;
bool isRssiValid; // are RSSI, noise and burstTime valid
unsigned nbits = gSlotLen;
rxBurst = pullRadioVector(burstTime, RSSI, TOA, chan);
rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan);
if (!rxBurst)
return;
if (rxBurst) {
/*
* EDGE demodulator returns 444 (148 * 3) bits
*/
if (rxBurst->size() == gSlotLen * 3)
nbits = gSlotLen * 3;
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();
dBm = RSSI + rssiOffset;
logRxBurst(chan, rxBurst, burstTime, dBm, RSSI, noise, TOA);
for (unsigned int i = 0; i < gSlotLen; i++) {
burstString[8+i] =(char) round((*burstItr++)*255.0);
}
burstString[gSlotLen+9] = '\0';
delete rxBurst;
TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
mDataSockets[chan]->write(burstString,gSlotLen+10);
}
char burstString[nbits + 10];
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;
SoftVector::iterator burstItr = rxBurst->begin();
for (unsigned i = 0; i < nbits; i++)
burstString[8 + i] = (char) round((*burstItr++) * 255.0);
burstString[nbits + 9] = '\0';
delete rxBurst;
mDataSockets[chan]->write(burstString, nbits + 10);
}
void Transceiver::driveTxFIFO()

View File

@@ -54,7 +54,7 @@ struct TransceiverState {
~TransceiverState();
/* Initialize a multiframe slot in the filler table */
bool init(int filler, size_t sps, float scale, size_t rtsc);
bool init(int filler, size_t sps, float scale, size_t rtsc, unsigned rach_delay);
int chanType[8];
@@ -97,16 +97,17 @@ public:
@param radioInterface associated radioInterface object
*/
Transceiver(int wBasePort,
const char *TRXAddress,
size_t wSPS, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface);
const char *TRXAddress,
size_t tx_sps, size_t rx_sps, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface,
double wRssiOffset);
/** Destructor */
~Transceiver();
/** Start the control loop */
bool init(int filler, size_t rtsc);
bool init(int filler, size_t rtsc, unsigned rach_delay);
/** attach the radioInterface receive FIFO */
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
@@ -146,13 +147,16 @@ public:
OFF, ///< timeslot is off
TSC, ///< timeslot should contain a normal burst
RACH, ///< timeslot should contain an access burst
EDGE, ///< timeslot should contain an EDGE burst
IDLE ///< timeslot is an idle (or dummy) burst
} CorrType;
enum FillerType {
FILLER_DUMMY,
FILLER_ZERO,
FILLER_RAND,
FILLER_NORM_RAND,
FILLER_EDGE_RAND,
FILLER_ACCESS_RAND,
};
private:
@@ -181,6 +185,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 +198,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);
@@ -204,30 +211,26 @@ private:
/** send messages over the clock socket */
void writeClockInterface(void);
/** Detect RACH bursts */
bool detectRACH(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa);
/** Detectbursts */
int detectBurst(TransceiverState *state, signalVector &burst,
complex &amp, float &toa, CorrType type);
/** 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);
/** Demodulate burst and output soft bits */
SoftVector *demodulate(TransceiverState *state, signalVector &burst,
complex amp, float toa, CorrType type);
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 mMaxExpectedDelayAB; ///< maximum expected time-of-arrival offset in GSM symbols for Access Bursts (RACH)
unsigned mMaxExpectedDelayNB; ///< maximum expected time-of-arrival offset in GSM symbols for Normal Bursts
unsigned mWriteBurstToDiskMask; ///< debug: bitmask to indicate which timeslots to dump to disk
std::vector<TransceiverState> mStates;
@@ -273,6 +276,8 @@ protected:
/** set priority on current thread */
void setPriority(float prio = 0.5) { mRadioInterface->setPriority(prio); }
void logRxBurst(size_t chan, SoftVector *burst, GSM::Time time, double dbm,
double rssi, double noise, double toa);
};
void *RxUpperLoopAdapter(TransceiverChannel *);

View File

@@ -68,11 +68,25 @@ enum uhd_dev_type {
struct uhd_dev_offset {
enum uhd_dev_type type;
int sps;
int tx_sps;
int rx_sps;
double 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
#define B2XX_TIMING_4_4SPS 5.89578e-5
#else
#define B2XX_TIMING_1SPS 9.9692e-5
#define B2XX_TIMING_4SPS 6.9248e-5
#define B2XX_TIMING_4_4SPS 4.19034e-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
@@ -83,38 +97,42 @@ struct uhd_dev_offset {
* Notes:
* USRP1 with timestamps is not supported by UHD.
*/
static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
{ USRP1, 1, 0.0, "USRP1 not supported" },
{ USRP1, 4, 0.0, "USRP1 not supported"},
{ USRP2, 1, 1.2184e-4, "N2XX 1 SPS" },
{ 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" },
{ E1XX, 1, 9.5192e-5, "E1XX 1 SPS" },
{ E1XX, 4, 6.5571e-5, "E1XX 4 SPS" },
{ E3XX, 1, 1.5000e-4, "E3XX 1 SPS" },
{ E3XX, 4, 1.2740e-4, "E3XX 4 SPS" },
{ X3XX, 1, 1.5360e-4, "X3XX 1 SPS"},
{ X3XX, 4, 1.1264e-4, "X3XX 4 SPS"},
{ UMTRX, 1, 9.9692e-5, "UmTRX 1 SPS" },
{ UMTRX, 4, 7.3846e-5, "UmTRX 4 SPS" },
static struct uhd_dev_offset uhd_offsets[] = {
{ USRP1, 1, 1, 0.0, "USRP1 not supported" },
{ USRP1, 4, 1, 0.0, "USRP1 not supported"},
{ USRP2, 1, 1, 1.2184e-4, "N2XX 1 SPS" },
{ USRP2, 4, 1, 8.0230e-5, "N2XX 4 SPS" },
{ B100, 1, 1, 1.2104e-4, "B100 1 SPS" },
{ B100, 4, 1, 7.9307e-5, "B100 4 SPS" },
{ B200, 1, 1, B2XX_TIMING_1SPS, "B200 1 SPS" },
{ B200, 4, 1, B2XX_TIMING_4SPS, "B200 4 SPS" },
{ B210, 1, 1, B2XX_TIMING_1SPS, "B210 1 SPS" },
{ B210, 4, 1, B2XX_TIMING_4SPS, "B210 4 SPS" },
{ E1XX, 1, 1, 9.5192e-5, "E1XX 1 SPS" },
{ E1XX, 4, 1, 6.5571e-5, "E1XX 4 SPS" },
{ E3XX, 1, 1, 1.5000e-4, "E3XX 1 SPS" },
{ E3XX, 4, 1, 1.2740e-4, "E3XX 4 SPS" },
{ X3XX, 1, 1, 1.5360e-4, "X3XX 1 SPS"},
{ X3XX, 4, 1, 1.1264e-4, "X3XX 4 SPS"},
{ UMTRX, 1, 1, 9.9692e-5, "UmTRX 1 SPS" },
{ UMTRX, 4, 1, 7.3846e-5, "UmTRX 4 SPS" },
{ B200, 4, 4, B2XX_TIMING_4_4SPS, "B200/B210 EDGE mode (4 SPS TX/RX)" },
{ B210, 4, 4, B2XX_TIMING_4_4SPS, "B200/B210 EDGE mode (4 SPS TX/RX)" },
{ UMTRX, 4, 4, 5.1503e-5, "UmTRX EDGE mode (4 SPS TX/RX)" },
};
#define NUM_UHD_OFFSETS (sizeof(uhd_offsets)/sizeof(uhd_offsets[0]))
/*
* Offset handling for special cases. Currently used for UmTRX dual channel
* diversity receiver only.
*/
static struct uhd_dev_offset special_offsets[] = {
{ UMTRX, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
{ UMTRX, 4, 5.2103e-5, "UmTRX diversity, 4 SPS" },
{ UMTRX, 1, 1, 8.0875e-5, "UmTRX diversity, 1 SPS" },
{ UMTRX, 4, 1, 5.2103e-5, "UmTRX diversity, 4 SPS" },
};
static double get_dev_offset(enum uhd_dev_type type,
int sps, bool diversity = false)
static double get_dev_offset(enum uhd_dev_type type, int tx_sps, int rx_sps,
bool edge = false, bool diversity = false)
{
struct uhd_dev_offset *offset = NULL;
@@ -124,6 +142,16 @@ static double get_dev_offset(enum uhd_dev_type type,
return 0.0;
}
if (edge && diversity) {
LOG(ERR) << "Unsupported configuration";
return 0.0;
}
if (edge && (type != B200) && (type != B210) && (type != UMTRX)) {
LOG(ALERT) << "EDGE is supported on B200/B210 and UmTRX only";
return 0.0;
}
/* Special cases (e.g. diversity receiver) */
if (diversity) {
if (type != UMTRX) {
@@ -131,7 +159,7 @@ static double get_dev_offset(enum uhd_dev_type type,
return 0.0;
}
switch (sps) {
switch (tx_sps) {
case 1:
offset = &special_offsets[0];
break;
@@ -141,9 +169,10 @@ static double get_dev_offset(enum uhd_dev_type type,
}
} else {
/* Search for matching offset value */
for (int i = 0; i < NUM_USRP_TYPES * 2; i++) {
for (size_t i = 0; i < NUM_UHD_OFFSETS; i++) {
if ((type == uhd_offsets[i].type) &&
(sps == uhd_offsets[i].sps)) {
(tx_sps == uhd_offsets[i].tx_sps) &&
(rx_sps == uhd_offsets[i].rx_sps)) {
offset = &uhd_offsets[i];
break;
}
@@ -197,23 +226,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
@@ -287,10 +299,11 @@ private:
*/
class uhd_device : public RadioDevice {
public:
uhd_device(size_t sps, size_t chans, bool diversity, double offset);
uhd_device(size_t tx_sps, size_t rx_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();
@@ -308,8 +321,8 @@ public:
bool setTxFreq(double wFreq, size_t chan);
bool setRxFreq(double wFreq, size_t chan);
inline TIMESTAMP initialWriteTimestamp() { return ts_initial * sps; }
inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
inline TIMESTAMP initialWriteTimestamp();
inline TIMESTAMP initialReadTimestamp();
double fullScaleInputValue();
double fullScaleOutputValue();
@@ -350,7 +363,7 @@ private:
enum TxWindowType tx_window;
enum uhd_dev_type dev_type;
size_t sps, chans;
size_t tx_sps, rx_sps, chans;
double tx_rate, rx_rate;
double tx_gain_min, tx_gain_max;
@@ -429,14 +442,16 @@ static void thread_enable_cancel(bool cancel)
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
}
uhd_device::uhd_device(size_t sps, size_t chans, bool diversity, double offset)
uhd_device::uhd_device(size_t tx_sps, size_t rx_sps,
size_t chans, bool diversity, double offset)
: tx_gain_min(0.0), tx_gain_max(0.0),
rx_gain_min(0.0), rx_gain_max(0.0),
tx_spp(0), rx_spp(0),
started(false), aligned(false), rx_pkt_cnt(0), drop_cnt(0),
prev_ts(0,0), ts_initial(0), ts_offset(0)
{
this->sps = sps;
this->tx_sps = tx_sps;
this->rx_sps = rx_sps;
this->chans = chans;
this->offset = offset;
this->diversity = diversity;
@@ -473,18 +488,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);
}
@@ -582,7 +603,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 +618,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];
}
@@ -694,7 +715,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 +741,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) {
@@ -738,12 +759,8 @@ int uhd_device::open(const std::string &args, bool extref)
usrp_dev->set_clock_source("external");
// Set rates
double _rx_rate;
double _tx_rate = select_rate(dev_type, sps);
if (diversity)
_rx_rate = select_rate(dev_type, 1, true);
else
_rx_rate = _tx_rate / sps;
double _tx_rate = select_rate(dev_type, tx_sps);
double _rx_rate = select_rate(dev_type, rx_sps, diversity);
if ((_tx_rate < 0.0) || (_rx_rate < 0.0))
return -1;
@@ -777,8 +794,14 @@ int uhd_device::open(const std::string &args, bool extref)
for (size_t i = 0; i < rx_buffers.size(); i++)
rx_buffers[i] = new smpl_buf(buf_len, rx_rate);
// Set receive chain sample offset
double offset = get_dev_offset(dev_type, sps, diversity);
// Set receive chain sample offset. Trigger the EDGE offset
// table by checking for 4 SPS on the receive path. No other
// configuration supports using 4 SPS.
bool edge = false;
if (rx_sps == 4)
edge = true;
double offset = get_dev_offset(dev_type, tx_sps, rx_sps, edge, diversity);
if (offset == 0.0) {
LOG(ERR) << "Unsupported configuration, no correction applied";
ts_offset = 0;
@@ -839,7 +862,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 +999,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 +1081,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 +1138,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 +1149,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;
@@ -1242,6 +1266,24 @@ double uhd_device::getRxFreq(size_t chan)
return rx_freqs[chan];
}
/*
* Only allow sampling the Rx path lower than Tx and not vice-versa.
* Using Tx with 4 SPS and Rx at 1 SPS is the only allowed mixed
* combination.
*/
TIMESTAMP uhd_device::initialWriteTimestamp()
{
if (rx_sps == tx_sps)
return ts_initial;
else
return ts_initial * tx_sps;
}
TIMESTAMP uhd_device::initialReadTimestamp()
{
return ts_initial;
}
double uhd_device::fullScaleInputValue()
{
if (dev_type == UMTRX)
@@ -1371,7 +1413,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 +1459,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 +1472,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 +1516,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
@@ -1494,8 +1549,8 @@ std::string smpl_buf::str_code(ssize_t code)
}
}
RadioDevice *RadioDevice::make(size_t sps, size_t chans,
bool diversity, double offset)
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
size_t chans, bool diversity, double offset)
{
return new uhd_device(sps, chans, diversity, offset);
return new uhd_device(tx_sps, rx_sps, chans, diversity, offset);
}

View File

@@ -59,7 +59,7 @@ const dboardConfigType dboardConfig = TXA_RXB;
const double USRPDevice::masterClockRate = 52.0e6;
USRPDevice::USRPDevice(size_t sps, size_t, bool)
USRPDevice::USRPDevice(size_t sps)
{
LOG(INFO) << "creating USRP device...";
@@ -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();
@@ -600,7 +600,8 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
bool USRPDevice::setRxFreq(double wFreq) { return true;};
#endif
RadioDevice *RadioDevice::make(size_t sps, size_t chans, bool diversity, double)
RadioDevice *RadioDevice::make(size_t tx_sps, size_t rx_sps,
size_t chans, bool diversity, double)
{
return new USRPDevice(sps, chans, diversity);
return new USRPDevice(tx_sps);
}

View File

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

View File

@@ -25,25 +25,25 @@
#include "config.h"
#endif
void neon_convert_ps_si16_4n(short *, float *, float *, int);
void neon_convert_si16_ps_4n(float *, short *, int);
void neon_convert_ps_si16_4n(short *, const float *, const float *, int);
void neon_convert_si16_ps_4n(float *, const short *, int);
#ifndef HAVE_NEON
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];
}
static void convert_ps_si16(short *out, float *in, float scale, int len)
static void convert_ps_si16(short *out, const float *in, float scale, int len)
{
for (int i = 0; i < len; i++)
out[i] = in[i] * scale;
}
#else
/* 4*N 16-bit signed integer conversion with remainder */
static void neon_convert_si16_ps(float *restrict out,
short *restrict in,
static void neon_convert_si16_ps(float *out,
const short *in,
int len)
{
int start = len / 4 * 4;
@@ -55,9 +55,9 @@ static void neon_convert_si16_ps(float *restrict out,
}
/* 4*N 16-bit signed integer conversion with remainder */
static void neon_convert_ps_si16(short *restrict out,
float *restrict in,
float *restrict scale,
static void neon_convert_ps_si16(short *out,
const float *in,
const float *scale,
int len)
{
int start = len / 4 * 4;
@@ -69,7 +69,7 @@ static void neon_convert_ps_si16(short *restrict out,
}
#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_NEON
float q[4] = { scale, scale, scale, scale };
@@ -83,7 +83,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_NEON
if (len % 4)

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

@@ -41,11 +41,19 @@
* ARM and non-SIMD enabled architectures.
*/
#if defined(HAVE_NEON) || !defined(HAVE_SSE3)
#define DEFAULT_SPS 1
#define DEFAULT_TX_SPS 1
#else
#define DEFAULT_SPS 4
#define DEFAULT_TX_SPS 4
#endif
/*
* Samples-per-symbol for uplink (receiver) path
* Do not modify this value. EDGE configures 4 sps automatically on
* B200/B210 devices only. Use of 4 sps on the receive path for other
* configurations is not supported.
*/
#define DEFAULT_RX_SPS 1
/* Default configuration parameters
* Note that these values are only used if the particular key does not
* exist in the configuration database. IP port and address values will
@@ -63,13 +71,18 @@ struct trx_config {
std::string addr;
std::string dev_args;
unsigned port;
unsigned sps;
unsigned tx_sps;
unsigned rx_sps;
unsigned chans;
unsigned rtsc;
unsigned rach_delay;
bool extref;
Transceiver::FillerType filler;
bool diversity;
double offset;
double rssi_offset;
bool swap_channels;
bool edge;
};
ConfigurationTable gConfig;
@@ -119,7 +132,7 @@ bool testConfig()
*/
bool trx_setup_config(struct trx_config *config)
{
std::string refstr, fillstr, divstr;
std::string refstr, fillstr, divstr, edgestr;
if (!testConfig())
return false;
@@ -159,6 +172,7 @@ bool trx_setup_config(struct trx_config *config)
if (config->diversity)
config->chans = 2;
edgestr = config->edge ? "Enabled" : "Disabled";
refstr = config->extref ? "Enabled" : "Disabled";
divstr = config->diversity ? "Enabled" : "Disabled";
switch (config->filler) {
@@ -168,9 +182,15 @@ bool trx_setup_config(struct trx_config *config)
case Transceiver::FILLER_ZERO:
fillstr = "Disabled";
break;
case Transceiver::FILLER_RAND:
case Transceiver::FILLER_NORM_RAND:
fillstr = "Normal busrts with random payload";
break;
case Transceiver::FILLER_EDGE_RAND:
fillstr = "EDGE busrts with random payload";
break;
case Transceiver::FILLER_ACCESS_RAND:
fillstr = "Access busrts with random payload";
break;
}
std::ostringstream ost("");
@@ -180,11 +200,14 @@ bool trx_setup_config(struct trx_config *config)
ost << " TRX Base Port........... " << config->port << std::endl;
ost << " TRX Address............. " << config->addr << std::endl;
ost << " Channels................ " << config->chans << std::endl;
ost << " Samples-per-Symbol...... " << config->sps << std::endl;
ost << " Tx Samples-per-Symbol... " << config->tx_sps << std::endl;
ost << " EDGE support............ " << edgestr << std::endl;
ost << " External Reference...... " << refstr << std::endl;
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;
@@ -202,18 +225,23 @@ RadioInterface *makeRadioInterface(struct trx_config *config,
{
RadioInterface *radio = NULL;
if ((config->rx_sps != 1) && (type != RadioDevice::NORMAL)) {
LOG(ALERT) << "Unsupported radio interface configuration";
}
switch (type) {
case RadioDevice::NORMAL:
radio = new RadioInterface(usrp, config->sps, config->chans);
radio = new RadioInterface(usrp, config->tx_sps,
config->rx_sps, config->chans);
break;
case RadioDevice::RESAMP_64M:
case RadioDevice::RESAMP_100M:
radio = new RadioInterfaceResamp(usrp,
config->sps, config->chans);
radio = new RadioInterfaceResamp(usrp, config->tx_sps,
config->chans);
break;
case RadioDevice::DIVERSITY:
radio = new RadioInterfaceDiversity(usrp,
config->sps, config->chans);
radio = new RadioInterfaceDiversity(usrp, config->tx_sps,
config->chans);
break;
default:
LOG(ALERT) << "Unsupported radio interface configuration";
@@ -239,9 +267,10 @@ Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
Transceiver *trx;
VectorFIFO *fifo;
trx = new Transceiver(config->port, config->addr.c_str(), config->sps,
config->chans, GSM::Time(3,0), radio);
if (!trx->init(config->filler, config->rtsc)) {
trx = new Transceiver(config->port, config->addr.c_str(),
config->tx_sps, config->rx_sps, config->chans,
GSM::Time(3,0), radio, config->rssi_offset);
if (!trx->init(config->filler, config->rtsc, config->rach_delay)) {
LOG(ALERT) << "Failed to initialize transceiver";
delete trx;
return NULL;
@@ -286,13 +315,17 @@ static void print_help()
" -l Logging level (%s)\n"
" -i IP address of GSM core\n"
" -p Base port number\n"
" -e Enable EDGE receiver\n"
" -d Enable dual channel diversity receiver\n"
" -x Enable external 10 MHz reference\n"
" -s Samples-per-symbol (1 or 4)\n"
" -c Number of ARFCN channels (default=1)\n"
" -f Enable C0 filler table\n"
" -o Set baseband frequency offset (default=auto)\n"
" -r Random burst test mode with TSC\n",
" -r Random Normal Burst test mode with TSC\n"
" -A Random Access Burst test mode with delay\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");
}
@@ -301,15 +334,20 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
int option;
config->port = 0;
config->sps = DEFAULT_SPS;
config->tx_sps = DEFAULT_TX_SPS;
config->rx_sps = DEFAULT_RX_SPS;
config->chans = DEFAULT_CHANS;
config->rtsc = 0;
config->rach_delay = 0;
config->extref = false;
config->filler = Transceiver::FILLER_ZERO;
config->diversity = false;
config->offset = 0.0;
config->rssi_offset = 0.0;
config->swap_channels = false;
config->edge = 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:A:R:Se")) != -1) {
switch (option) {
case 'h':
print_help();
@@ -343,11 +381,25 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
config->offset = atof(optarg);
break;
case 's':
config->sps = atoi(optarg);
config->tx_sps = atoi(optarg);
break;
case 'r':
config->rtsc = atoi(optarg);
config->filler = Transceiver::FILLER_RAND;
config->filler = Transceiver::FILLER_NORM_RAND;
break;
case 'A':
config->rach_delay = atoi(optarg);
config->filler = Transceiver::FILLER_ACCESS_RAND;
break;
case 'R':
config->rssi_offset = atof(optarg);
break;
case 'S':
config->swap_channels = true;
break;
case 'e':
config->edge = true;
config->rx_sps = 4;
break;
default:
print_help();
@@ -355,8 +407,17 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
}
}
if ((config->sps != 1) && (config->sps != 4)) {
printf("Unsupported samples-per-symbol %i\n\n", config->sps);
if (config->edge && (config->filler == Transceiver::FILLER_NORM_RAND))
config->filler = Transceiver::FILLER_EDGE_RAND;
if ((config->tx_sps != 1) && (config->tx_sps != 4)) {
printf("Unsupported samples-per-symbol %i\n\n", config->tx_sps);
print_help();
exit(0);
}
if (config->edge && (config->tx_sps != 4)) {
printf("EDGE only supported at 4 samples per symbol\n\n");
print_help();
exit(0);
}
@@ -366,6 +427,12 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
print_help();
exit(0);
}
if (config->rach_delay > 68) {
printf("RACH delay is too big %i\n\n", config->rach_delay);
print_help();
exit(0);
}
}
int main(int argc, char *argv[])
@@ -391,9 +458,9 @@ int main(int argc, char *argv[])
srandom(time(NULL));
/* Create the low level device object */
usrp = RadioDevice::make(config.sps, config.chans,
usrp = RadioDevice::make(config.tx_sps, config.rx_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

@@ -37,11 +37,11 @@ class RadioDevice {
/* Radio interface types */
enum RadioInterfaceType { NORMAL, RESAMP_64M, RESAMP_100M, DIVERSITY };
static RadioDevice *make(size_t sps, size_t chans = 1,
static RadioDevice *make(size_t tx_sps, size_t rx_sps = 1, size_t chans = 1,
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

@@ -33,12 +33,12 @@ extern "C" {
#define CHUNK 625
#define NUMCHUNKS 4
RadioInterface::RadioInterface(RadioDevice *wRadio,
size_t sps, size_t chans, size_t diversity,
RadioInterface::RadioInterface(RadioDevice *wRadio, size_t tx_sps,
size_t rx_sps, size_t chans, size_t diversity,
int wReceiveOffset, GSM::Time wStartTime)
: mRadio(wRadio), mSPSTx(sps), mSPSRx(1), mChans(chans), mMIMO(diversity),
sendCursor(0), recvCursor(0), underrun(false), overrun(false),
receiveOffset(wReceiveOffset), mOn(false)
: mRadio(wRadio), mSPSTx(tx_sps), mSPSRx(rx_sps), mChans(chans),
mMIMO(diversity), sendCursor(0), recvCursor(0), underrun(false),
overrun(false), receiveOffset(wReceiveOffset), mOn(false)
{
mClock.set(wStartTime);
}
@@ -262,7 +262,12 @@ bool RadioInterface::driveReceiveRadio()
int recvSz = recvCursor;
int readSz = 0;
const int symbolsPerSlot = gSlotLen + 8;
int burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
int burstSize;
if (mSPSRx == 4)
burstSize = 625;
else
burstSize = symbolsPerSlot + (tN % 4 == 0);
/*
* Pre-allocate head room for the largest correlation size
@@ -297,7 +302,8 @@ bool RadioInterface::driveReceiveRadio()
tN = rcvClock.TN();
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
if (mSPSRx != 4)
burstSize = (symbolsPerSlot + (tN % 4 == 0)) * mSPSRx;
}
if (readSz > 0) {

View File

@@ -87,7 +87,8 @@ public:
/** constructor */
RadioInterface(RadioDevice* wRadio = NULL,
size_t sps = 4, size_t chans = 1, size_t diversity = 1,
size_t tx_sps = 4, size_t rx_sps = 1,
size_t chans = 1, size_t diversity = 1,
int receiveOffset = 3, GSM::Time wStartTime = GSM::Time(0));
/** destructor */

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,11 @@
#include "BitVector.h"
#include "signalVector.h"
/* Burst lengths */
#define NORMAL_BURST_NBITS 148
#define EDGE_BURST_NBITS 444
#define EDGE_BURST_NSYMS (EDGE_BURST_NBITS / 3)
/** Convolution type indicator */
enum ConvType {
START_ONLY,
@@ -49,7 +54,7 @@ float vectorNorm2(const signalVector &x);
float vectorPower(const signalVector &x);
/** Setup the signal processing library */
bool sigProcLibSetup(int sps);
bool sigProcLibSetup();
/** Destroy the signal processing library */
void sigProcLibDestroy(void);
@@ -104,6 +109,25 @@ signalVector *modulateBurst(const BitVector &wBurst,
int guardPeriodLength,
int sps, bool emptyPulse = false);
/** 8-PSK modulate a burst of bits */
signalVector *modulateEdgeBurst(const BitVector &bits,
int sps, bool emptyPulse = false);
/** Generate a EDGE burst with random payload - 4 SPS (625 samples) only */
signalVector *generateEdgeBurst(int tsc);
/** Generate an empty burst - 4 or 1 SPS */
signalVector *generateEmptyBurst(int sps, int tn);
/** Generate a normal GSM burst with random payload - 4 or 1 SPS */
signalVector *genRandNormalBurst(int tsc, int sps, int tn);
/** Generate an access GSM burst with random payload - 4 or 1 SPS */
signalVector *genRandAccessBurst(int delay, int sps, int tn);
/** Generate a dummy GSM burst - 4 or 1 SPS */
signalVector *generateDummyBurst(int sps, int tn);
/** Sinc function */
float sinc(float x);
@@ -151,22 +175,6 @@ complex peakDetect(const signalVector &rxBurst,
void scaleVector(signalVector &x,
complex scale);
/**
Generate a modulated GSM midamble, stored within the library.
@param gsmPulse The GSM pulse used for modulation.
@param sps The number of samples per GSM symbol.
@param TSC The training sequence [0..7]
@return Success.
*/
bool generateMidamble(int sps, int tsc);
/**
Generate a modulated RACH sequence, stored within the library.
@param gsmPulse The GSM pulse used for modulation.
@param sps The number of samples per GSM symbol.
@return Success.
*/
bool generateRACHSequence(int sps);
/**
Energy detector, checks to see if received burst energy is above a threshold.
@param rxBurst The received GSM burst of interest.
@@ -187,13 +195,15 @@ bool energyDetect(signalVector &rxBurst,
@param sps The number of samples per GSM symbol.
@param amplitude The estimated amplitude of received RACH burst.
@param TOA The estimate time-of-arrival of received RACH burst.
@param maxTOA The maximum expected time-of-arrival
@return positive if threshold value is reached, negative on error, zero otherwise
*/
int detectRACHBurst(signalVector &rxBurst,
float detectThreshold,
int sps,
complex *amplitude,
float* TOA);
complex &amplitude,
float &TOA,
unsigned maxTOA);
/**
Normal burst correlator, detector, channel estimator.
@@ -210,15 +220,39 @@ 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 maxTOA,
bool requestChannel = false,
signalVector** channelResponse = NULL,
float *channelResponseOffset = NULL);
unsigned TSC,
float detectThreshold,
int sps,
complex &amplitude,
float &TOA,
unsigned maxTOA);
/**
EDGE burst detector
@param burst The received GSM burst of interest
@param detectThreshold The threshold that the received burst's post-correlator SNR is compared against to determine validity.
@param sps The number of samples per GSM symbol.
@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
@return positive if threshold value is reached, negative on error, zero otherwise
*/
int detectEdgeBurst(signalVector &burst,
unsigned TSC,
float detectThreshold,
int sps,
complex &amplitude,
float &TOA,
unsigned maxTOA);
/**
Downsample 4 SPS to 1 SPS using a polyphase filterbank
@param burst Input burst of at least 624 symbols
@return Decimated signal vector of 156 symbols
*/
signalVector *downsampleBurst(signalVector &burst);
/**
Decimate a vector.
@@ -241,33 +275,14 @@ 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.
Demodulate 8-PSK EDGE burst with soft symbol ooutput
@param rxBurst The burst to be demodulated.
@param sps The number of samples per GSM symbol.
@param channel The amplitude estimate of the received burst.
@param TOA The time-of-arrival of the received burst.
@return The demodulated bit sequence.
*/
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);
SoftVector *demodEdgeBurst(signalVector &rxBurst, int sps,
complex channel, float TOA);
#endif /* SIGPROCLIB_H */

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"], [

18
debian/changelog vendored Normal file
View File

@@ -0,0 +1,18 @@
osmo-trx (0.1.10~1) trusty; urgency=medium
* some EDGE support in master
* fairwaves/rach-filler branch
-- Kirill Zakharenko <earwin@gmail.com> Sun, 27 Mar 2016 19:37:39 +0100
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