Compare commits

...

56 Commits

Author SHA1 Message Date
Kirill Zakharenko
b54d46e670 Bumped version to 0.1.9~1 2015-07-23 02:25:25 +02:00
Kirill Zakharenko
8c490f5f0b debian: correct dependency on libuhd, throw away dependency on umtrx-uhd 2015-07-23 02:16:58 +02:00
Kirill Zakharenko
835841853f debian: compile for atom arch with SSE3 optimizations 2015-07-18 01:28:50 +02:00
Kirill Zakharenko
e7c25a3677 transceiver/x86: don’t use -march=native to build x86 specialized code
when this option is used, it is impossible to cross-compile for x86
 variants like atom.
2015-07-18 01:27:03 +02:00
Kirill Zakharenko
6d17a6a7e6 debian: whitespace changes, more correct hardening stanza 2015-07-18 01:25:23 +02:00
Kirill Zakharenko
bb8d46adaa bumped version to 0.1.9 2015-07-16 16:45:36 +02:00
Kirill Zakharenko
20b388973c build with instruction set/tuning for atom processors
fix non-working hardening
2015-07-16 16:45:04 +02:00
Ivan Kluchnikov
0d039e2676 debian: make it possible to install osmo-trx dependencies manually on the system 2015-07-16 14:12:01 +03:00
Ivan Kluchnikov
ad669513d2 debian: update osmo-trx dependencies
Now we use uhd and umtrx-uhd instead of libuhd-dev.
2015-07-16 14:11:27 +03:00
Ivan Kluchnikov
fd978b28bd debian: Add debug package for the osmo-trx 2015-07-16 14:10:39 +03:00
Ivan Kluchnikov
1b6aaae3fa debian: Add debian directory to ease building packages 2015-07-16 14:09:55 +03:00
Alexander Chemeris
facdadc254 uhd: Output Rx/Tx gain limits to log to make it more transparent.
Conflicts:

	Transceiver52M/UHDDevice.cpp
2015-06-22 11:57:38 +03:00
Alexander Chemeris
eefa8e58f7 Logger: Output ERR log messages to stderr as well. 2015-06-22 11:40:34 +03:00
Alexander Chemeris
3ac1cbf40e Transceiver: Add support for OsmoBTS style handover. 2015-06-20 01:38:47 +03:00
Alexander Chemeris
139c84564c Transceiver: Fix whitespace. 2015-06-20 01:37:54 +03:00
Alexander Chemeris
2cb6070e09 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
2015-06-20 00:05:51 +03:00
Alexander Chemeris
f2bdd1a24c transceiver: Add a debug option to dump selected timeslots to disk. 2015-06-12 00:15:31 -04:00
Alexander Chemeris
34e5a3807f Transceiver: Do not update state->SNRestimate if equalization is disabled.
This also fix a bug of using bool type for noise instead of float.
2015-06-10 23:13:33 -04:00
Alexander Chemeris
b49874aa64 uhd: Fix rounding error in timestamp conversion functions.
Rounding error introduced oscilating timing advance error by regularly
overwriting one bit and then skipping one bit.

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

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

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

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

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

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

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

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

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-05-18 16:51:44 -07:00
Alexander Chemeris
90f7a01d1d umtrx: Don't use DSP tuning, because LMS6002D PLL steps are small enough.
We end up with DSP tuning just for 2-3Hz, which is meaningless and
only distort the signal.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-05-18 16:49:24 -07:00
Alexander Chemeris
e171425b99 uhd: Set RF frontend bandwidth for UmTRX to improve signal quality.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-05-18 16:49:05 -07:00
Alexander Chemeris
4d029d8965 UmTRX: Manually set Tx gain stages for the best signal quality.
New UHD versions support split configuration of Tx gain stages.
We utilize this to set the gain configuration, optimal for
the Tx signal quality. From our measurements, VGA1 must be
18dB plus-minus one and VGA2 is the best when 23dB or lower.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

View File

@@ -67,7 +67,8 @@ const char *levelNames[] = {
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
};
int numLevels = 8;
bool gLogToConsole = 0;
bool gLogToConsole = true;
bool gLogToSyslog = false;
FILE *gLogToFile = NULL;
Mutex gLogToLock;
@@ -192,18 +193,20 @@ 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;
}
// Current logging level was already checked by the macro.
// So just log.
syslog(mPriority, "%s", mStream.str().c_str());
// pat added for easy debugging.
// Current logging level was already checked by the macro. So just log.
// Log to syslog
if (gLogToSyslog) {
syslog(mPriority, "%s", mStream.str().c_str());
}
// Log to file and console
if (gLogToConsole||gLogToFile) {
int mlen = mStream.str().size();
int neednl = (mlen==0 || mStream.str()[mlen-1] != '\n');
gLogToLock.lock();
ScopedLock lock(gLogToLock);
if (gLogToConsole) {
// The COUT() macro prevents messages from stomping each other but adds uninteresting thread numbers,
// so just use std::cout.
@@ -215,7 +218,6 @@ Log::~Log()
if (neednl) {fputc('\n',gLogToFile);}
fflush(gLogToFile);
}
gLogToLock.unlock();
}
}
@@ -243,10 +245,9 @@ void gLogInit(const char* name, const char* level, int facility)
gConfig.set("Log.Level",level);
}
// Pat added, tired of the syslog facility.
// Both the transceiver and OpenBTS use this same facility, but only OpenBTS/OpenNodeB may use this log file:
string str = gConfig.getStr("Log.File");
if (gLogToFile==0 && str.length() && 0==strncmp(gCmdName,"Open",4)) {
if (gLogToFile==NULL && str.length() && 0==strncmp(gCmdName,"Open",4)) {
const char *fn = str.c_str();
if (fn && *fn && strlen(fn)>3) { // strlen because a garbage char is getting in sometimes.
gLogToFile = fopen(fn,"w"); // New log file each time we start.

View File

@@ -116,7 +116,8 @@ class Log {
std::ostringstream& get();
};
extern bool gLogToConsole; // Pat added for easy debugging.
extern bool gLogToConsole; // Output log messages to stdout
extern bool gLogToSyslog; // Output log messages to syslog

View File

@@ -172,8 +172,15 @@ class Thread {
void start(void *(*task)(void*), void *arg);
/** Join a thread that will stop on its own. */
void join() { int s = pthread_join(mThread,NULL); assert(!s); mThread = 0; }
void join() {
if (mThread) {
int s = pthread_join(mThread, NULL);
assert(!s);
}
}
/** Send cancelation to thread */
void cancel() { pthread_cancel(mThread); }
};

260
README
View File

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

View File

@@ -22,6 +22,8 @@
*/
#include <stdio.h>
#include <iomanip> // std::setprecision
#include <fstream>
#include "Transceiver.h"
#include <Logger.h>
@@ -43,7 +45,7 @@ using namespace GSM;
#define NOISE_CNT 20
TransceiverState::TransceiverState()
: mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT)
: mRetrans(false), mNoiseLev(0.0), mNoises(NOISE_CNT), mPower(0.0)
{
for (int i = 0; i < 8; i++) {
chanType[i] = Transceiver::NONE;
@@ -69,61 +71,127 @@ TransceiverState::~TransceiverState()
}
}
void TransceiverState::init(size_t slot, signalVector *burst, bool fill)
static BitVector *genRandNormalBurst(size_t tsc)
{
signalVector *filler;
if (tsc > 7)
return NULL;
for (int i = 0; i < 102; i++) {
if (fill)
filler = new signalVector(*burst);
else
filler = new signalVector(burst->size());
BitVector *bits = new BitVector(148);
fillerTable[i][slot] = filler;
size_t i = 0;
/* Tail bits */
for (; i < 4; i++)
(*bits)[i] = 0;
/* Random bits */
for (; i < 61; i++)
(*bits)[i] = rand() % 2;
/* Training sequence */
for (int j = 0; i < 87; i++, j++)
(*bits)[i] = GSM::gTrainingSequence[tsc][j];
/* Random bits */
for (; i < 144; i++)
(*bits)[i] = rand() % 2;
/* Tail bits */
for (; i < 148; i++)
(*bits)[i] = 0;
return bits;
}
bool TransceiverState::init(int filler, size_t sps, float scale, size_t rtsc)
{
BitVector *bits;
signalVector *burst;
if ((sps != 1) && (sps != 4))
return false;
for (size_t n = 0; n < 8; n++) {
size_t guard = 8 + !(n % 4);
size_t len = sps == 4 ? 625 : 148 + guard;
for (size_t i = 0; i < 102; i++) {
switch (filler) {
case Transceiver::FILLER_DUMMY:
burst = modulateBurst(gDummyBurst, guard, sps);
break;
case Transceiver::FILLER_RAND:
bits = genRandNormalBurst(rtsc);
burst = modulateBurst(*bits, guard, sps);
delete bits;
break;
case Transceiver::FILLER_ZERO:
default:
burst = new signalVector(len);
}
scaleVector(*burst, scale);
fillerTable[i][n] = burst;
}
if (filler == Transceiver::FILLER_RAND)
chanType[n] = Transceiver::TSC;
}
return false;
}
Transceiver::Transceiver(int wBasePort,
const char *TRXAddress,
size_t wSPS, size_t wChans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface)
: mBasePort(wBasePort), mAddr(TRXAddress),
mTransmitLatency(wTransmitLatency), mClockSocket(NULL),
mRadioInterface(wRadioInterface), mSPSTx(wSPS), mSPSRx(1), mChans(wChans),
mOn(false), mTxFreq(0.0), mRxFreq(0.0), mPower(-10), mMaxExpectedDelay(0)
const char *wTRXAddress,
size_t wSPS, size_t wChans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface,
double wRssiOffset)
: mBasePort(wBasePort), mAddr(wTRXAddress),
mClockSocket(wBasePort, wTRXAddress, mBasePort + 100),
mTransmitLatency(wTransmitLatency), mRadioInterface(wRadioInterface),
rssiOffset(wRssiOffset),
mSPSTx(wSPS), mSPSRx(1), mChans(wChans), mOn(false),
mTxFreq(0.0), mRxFreq(0.0), mTSC(0), mMaxExpectedDelay(0), mWriteBurstToDiskMask(0)
{
GSM::Time startTime(random() % gHyperframe,0);
mRxLowerLoopThread = new Thread(32768);
mTxLowerLoopThread = new Thread(32768);
mTransmitDeadlineClock = startTime;
mLastClockUpdateTime = startTime;
mLatencyUpdateTime = startTime;
mRadioInterface->getClock()->set(startTime);
txFullScale = mRadioInterface->fullScaleInputValue();
rxFullScale = mRadioInterface->fullScaleOutputValue();
for (int i = 0; i < 8; i++) {
for (int j = 0; j < 8; j++)
mHandover[i][j] = false;
}
}
Transceiver::~Transceiver()
{
stop();
sigProcLibDestroy();
delete mClockSocket;
for (size_t i = 0; i < mChans; i++) {
mControlServiceLoopThreads[i]->cancel();
mControlServiceLoopThreads[i]->join();
delete mControlServiceLoopThreads[i];
mTxPriorityQueues[i].clear();
delete mCtrlSockets[i];
delete mDataSockets[i];
}
}
bool Transceiver::init(bool filler)
/*
* Initialize transceiver
*
* Start or restart the control loop. Any further control is handled through the
* socket API. Randomize the central radio clock set the downlink burst
* counters. Note that the clock will not update until the radio starts, but we
* are still expected to report clock indications through control channel
* activity.
*/
bool Transceiver::init(int filler, size_t rtsc)
{
int d_srcport, d_dstport, c_srcport, c_dstport;
signalVector *burst;
if (!mChans) {
LOG(ALERT) << "No channels assigned";
@@ -137,7 +205,6 @@ bool Transceiver::init(bool filler)
mDataSockets.resize(mChans);
mCtrlSockets.resize(mChans);
mControlServiceLoopThreads.resize(mChans);
mTxPriorityQueueServiceLoopThreads.resize(mChans);
mRxServiceLoopThreads.resize(mChans);
@@ -147,11 +214,10 @@ bool Transceiver::init(bool filler)
mStates.resize(mChans);
/* Filler table retransmissions - support only on channel 0 */
if (filler)
if (filler == FILLER_DUMMY)
mStates[0].mRetrans = true;
mClockSocket = new UDPSocket(mBasePort, mAddr.c_str(), mBasePort + 100);
/* Setup sockets */
for (size_t i = 0; i < mChans; i++) {
c_srcport = mBasePort + 2 * i + 1;
c_dstport = mBasePort + 2 * i + 101;
@@ -162,22 +228,129 @@ bool Transceiver::init(bool filler)
mDataSockets[i] = new UDPSocket(d_srcport, mAddr.c_str(), d_dstport);
}
for (size_t i = 0; i < mChans; i++) {
mControlServiceLoopThreads[i] = new Thread(32768);
mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
mRxServiceLoopThreads[i] = new Thread(32768);
/* Randomize the central clock */
GSM::Time startTime(random() % gHyperframe, 0);
mRadioInterface->getClock()->set(startTime);
mTransmitDeadlineClock = startTime;
mLastClockUpdateTime = startTime;
mLatencyUpdateTime = startTime;
for (size_t n = 0; n < 8; n++) {
burst = modulateBurst(gDummyBurst, 8 + (n % 4 == 0), mSPSTx);
scaleVector(*burst, txFullScale);
mStates[i].init(n, burst, filler && !i);
delete burst;
}
/* Start control threads */
for (size_t i = 0; i < mChans; i++) {
TransceiverChannel *chan = new TransceiverChannel(this, i);
mControlServiceLoopThreads[i] = new Thread(32768);
mControlServiceLoopThreads[i]->start((void * (*)(void*))
ControlServiceLoopAdapter, (void*) chan);
if (i && filler == FILLER_DUMMY)
filler = FILLER_ZERO;
mStates[i].init(filler, mSPSTx, txFullScale, rtsc);
}
return true;
}
/*
* Start the transceiver
*
* Submit command(s) to the radio device to commence streaming samples and
* launch threads to handle sample I/O. Re-synchronize the transmit burst
* counters to the central radio clock here as well.
*/
bool Transceiver::start()
{
ScopedLock lock(mLock);
if (mOn) {
LOG(ERR) << "Transceiver already running";
return true;
}
LOG(NOTICE) << "Starting the transceiver";
GSM::Time time = mRadioInterface->getClock()->get();
mTransmitDeadlineClock = time;
mLastClockUpdateTime = time;
mLatencyUpdateTime = time;
if (!mRadioInterface->start()) {
LOG(ALERT) << "Device failed to start";
return false;
}
/* Device is running - launch I/O threads */
mRxLowerLoopThread = new Thread(32768);
mTxLowerLoopThread = new Thread(32768);
mTxLowerLoopThread->start((void * (*)(void*))
TxLowerLoopAdapter,(void*) this);
mRxLowerLoopThread->start((void * (*)(void*))
RxLowerLoopAdapter,(void*) this);
/* Launch uplink and downlink burst processing threads */
for (size_t i = 0; i < mChans; i++) {
TransceiverChannel *chan = new TransceiverChannel(this, i);
mRxServiceLoopThreads[i] = new Thread(32768);
mRxServiceLoopThreads[i]->start((void * (*)(void*))
RxUpperLoopAdapter, (void*) chan);
chan = new TransceiverChannel(this, i);
mTxPriorityQueueServiceLoopThreads[i] = new Thread(32768);
mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
TxUpperLoopAdapter, (void*) chan);
}
writeClockInterface();
mOn = true;
return true;
}
/*
* Stop the transceiver
*
* Perform stopping by disabling receive streaming and issuing cancellation
* requests to running threads. Most threads will timeout and terminate once
* device is disabled, but the transmit loop may block waiting on the central
* UMTS clock. Explicitly signal the clock to make sure that the transmit loop
* makes it to the thread cancellation point.
*/
void Transceiver::stop()
{
ScopedLock lock(mLock);
if (!mOn)
return;
LOG(NOTICE) << "Stopping the transceiver";
mTxLowerLoopThread->cancel();
mRxLowerLoopThread->cancel();
for (size_t i = 0; i < mChans; i++) {
mRxServiceLoopThreads[i]->cancel();
mTxPriorityQueueServiceLoopThreads[i]->cancel();
}
LOG(INFO) << "Stopping the device";
mRadioInterface->stop();
for (size_t i = 0; i < mChans; i++) {
mRxServiceLoopThreads[i]->join();
mTxPriorityQueueServiceLoopThreads[i]->join();
delete mRxServiceLoopThreads[i];
delete mTxPriorityQueueServiceLoopThreads[i];
mTxPriorityQueues[i].clear();
}
mTxLowerLoopThread->join();
mRxLowerLoopThread->join();
delete mTxLowerLoopThread;
delete mRxLowerLoopThread;
mOn = false;
LOG(NOTICE) << "Transceiver stopped";
}
void Transceiver::addRadioVector(size_t chan, BitVector &bits,
int RSSI, GSM::Time &wTime)
{
@@ -295,9 +468,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:
@@ -307,16 +486,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:
@@ -331,6 +519,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;
@@ -338,6 +528,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;
@@ -367,13 +559,13 @@ Transceiver::CorrType Transceiver::expectedCorrType(GSM::Time currTime,
* Detect RACH synchronization sequence within a burst. No equalization
* is used or available on the RACH channel.
*/
bool Transceiver::detectRACH(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa)
int Transceiver::detectRACH(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa)
{
float threshold = 6.0;
return detectRACHBurst(burst, threshold, mSPSRx, &amp, &toa);
return detectRACHBurst(burst, threshold, mSPSRx, amp, toa);
}
/*
@@ -381,12 +573,13 @@ bool Transceiver::detectRACH(TransceiverState *state,
* state information and channel estimate if necessary. Equalization
* is currently disabled.
*/
bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
complex &amp, float &toa, GSM::Time &time)
int Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
complex &amp, float &toa, GSM::Time &time)
{
int success;
int tn = time.TN();
float chanOffset, threshold = 5.0;
bool noise, needDFE = false, estimateChan = false;
bool needDFE = false, estimateChan = false;
double elapsed = time - state->chanEstimateTime[tn];
signalVector *chanResp;
@@ -401,17 +594,18 @@ bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
}
/* Detect normal burst midambles */
if (!analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, &amp,
&toa, mMaxExpectedDelay, estimateChan,
&chanResp, &chanOffset)) {
return false;
success = analyzeTrafficBurst(burst, mTSC, threshold, mSPSRx, amp,
toa, mMaxExpectedDelay, estimateChan,
&chanResp, &chanOffset);
if (success <= 0) {
return success;
}
noise = state->mNoiseLev;
state->SNRestimate[tn] = amp.norm2() / (noise * noise + 1.0);
/* Set equalizer if unabled */
if (needDFE && estimateChan) {
float noise = state->mNoiseLev;
state->SNRestimate[tn] = amp.norm2() / (noise * noise + 1.0);
state->chanResponse[tn] = chanResp;
state->chanRespOffset[tn] = chanOffset;
state->chanRespAmplitude[tn] = amp;
@@ -424,7 +618,7 @@ bool Transceiver::detectTSC(TransceiverState *state, signalVector &burst,
state->chanEstimateTime[tn] = time;
}
return true;;
return 1;
}
/*
@@ -447,20 +641,33 @@ SoftVector *Transceiver::demodulate(TransceiverState *state,
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 success;
bool equalize = false;
complex amp;
float toa, pow, max = -1.0, avg = 0.0;
int max_i = -1;
signalVector *burst;
SoftVector *bits = NULL;
TransceiverState *state = &mStates[chan];
isRssiValid = false;
/* Blocking FIFO read */
radioVector *radio_burst = mReceiveFIFO[chan]->read();
@@ -471,7 +678,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;
}
@@ -495,7 +710,25 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
/* Average noise on diversity paths and update global levels */
burst = radio_burst->getVector(max_i);
avg = sqrt(avg / radio_burst->chans());
state->mNoiseLev = state->mNoises.avg();
wTime = time;
RSSI = 20.0 * log10(rxFullScale / avg);
/* RSSI estimation are valid */
isRssiValid = true;
if (type == IDLE) {
/* Update noise levels */
state->mNoises.insert(avg);
state->mNoiseLev = state->mNoises.avg();
noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
delete radio_burst;
return NULL;
} else {
/* Do not update noise levels */
noise = 20.0 * log10(rxFullScale / state->mNoiseLev);
}
/* Detect normal or RACH bursts */
if (type == TSC)
@@ -503,39 +736,30 @@ SoftVector *Transceiver::pullRadioVector(GSM::Time &wTime, int &RSSI,
else
success = detectRACH(state, *burst, amp, toa);
if (!success) {
state->mNoises.insert(avg);
/* Alert an error and exit */
if (success <= 0) {
if (success == -SIGERR_CLIP) {
LOG(WARNING) << "Clipping detected on received RACH or Normal Burst";
} else if (success != SIGERR_NONE) {
LOG(WARNING) << "Unhandled RACH or Normal Burst detection error";
}
delete radio_burst;
return NULL;
}
timingOffset = toa / mSPSRx;
/* Demodulate and set output info */
if (equalize && (type != TSC))
equalize = false;
if (avg - state->mNoiseLev > 0.0)
bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
wTime = time;
RSSI = (int) floor(20.0 * log10(rxFullScale / avg));
timingOffset = (int) round(toa * 256.0 / mSPSRx);
bits = demodulate(state, *burst, amp, toa, time.TN(), equalize);
delete radio_burst;
return bits;
}
void Transceiver::start()
{
TransceiverChannel *chan;
for (size_t i = 0; i < mControlServiceLoopThreads.size(); i++) {
chan = new TransceiverChannel(this, i);
mControlServiceLoopThreads[i]->start((void * (*)(void*))
ControlServiceLoopAdapter, (void*) chan);
}
}
void Transceiver::reset()
{
for (size_t i = 0; i < mTxPriorityQueues.size(); i++)
@@ -574,40 +798,32 @@ void Transceiver::driveControl(size_t chan)
LOG(INFO) << "command is " << buffer;
if (strcmp(command,"POWEROFF")==0) {
// turn off transmitter/demod
sprintf(response,"RSP POWEROFF 0");
stop();
sprintf(response,"RSP POWEROFF 0");
}
else if (strcmp(command,"POWERON")==0) {
// turn on transmitter/demod
if (!mTxFreq || !mRxFreq)
if (!start())
sprintf(response,"RSP POWERON 1");
else {
else
sprintf(response,"RSP POWERON 0");
if (!chan && !mOn) {
// Prepare for thread start
mPower = -20;
mRadioInterface->start();
// Start radio interface threads.
mTxLowerLoopThread->start((void * (*)(void*))
TxLowerLoopAdapter,(void*) this);
mRxLowerLoopThread->start((void * (*)(void*))
RxLowerLoopAdapter,(void*) this);
for (size_t i = 0; i < mChans; i++) {
TransceiverChannel *chan = new TransceiverChannel(this, i);
mRxServiceLoopThreads[i]->start((void * (*)(void*))
RxUpperLoopAdapter, (void*) chan);
chan = new TransceiverChannel(this, i);
mTxPriorityQueueServiceLoopThreads[i]->start((void * (*)(void*))
TxUpperLoopAdapter, (void*) chan);
}
writeClockInterface();
mOn = true;
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
@@ -634,28 +850,19 @@ void Transceiver::driveControl(size_t chan)
}
}
else if (!strcmp(command, "SETPOWER")) {
// set output power in dB
int dbPwr;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbPwr);
if (!mOn)
sprintf(response, "RSP SETPOWER 1 %d", dbPwr);
else {
mPower = dbPwr;
mRadioInterface->setPowerAttenuation(mPower, chan);
sprintf(response, "RSP SETPOWER 0 %d", dbPwr);
}
int power;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &power);
power = mRadioInterface->setPowerAttenuation(power, chan);
mStates[chan].mPower = power;
sprintf(response, "RSP SETPOWER 0 %d", power);
}
else if (!strcmp(command,"ADJPOWER")) {
// adjust power in dB steps
int dbStep;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &dbStep);
if (!mOn)
sprintf(response, "RSP ADJPOWER 1 %d", mPower);
else {
mPower += dbStep;
mRadioInterface->setPowerAttenuation(mPower, chan);
sprintf(response, "RSP ADJPOWER 0 %d", mPower);
}
int power, step;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &step);
power = mStates[chan].mPower + step;
power = mRadioInterface->setPowerAttenuation(power, chan);
mStates[chan].mPower = power;
sprintf(response, "RSP ADJPOWER 0 %d", power);
}
else if (strcmp(command,"RXTUNE")==0) {
// tune receiver
@@ -685,7 +892,7 @@ void Transceiver::driveControl(size_t chan)
// set TSC
unsigned TSC;
sscanf(buffer, "%3s %s %d", cmdcheck, command, &TSC);
if (mOn)
if (mOn || (TSC < 0) || (TSC > 7))
sprintf(response, "RSP SETTSC 1 %d", TSC);
else if (chan && (TSC != mTSC))
sprintf(response, "RSP SETTSC 1 %d", TSC);
@@ -696,7 +903,7 @@ void Transceiver::driveControl(size_t chan)
}
}
else if (strcmp(command,"SETSLOT")==0) {
// set TSC
// set slot type
int corrCode;
int timeslot;
sscanf(buffer,"%3s %s %d %d",cmdcheck,command,&timeslot,&corrCode);
@@ -710,8 +917,17 @@ 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");
}
mCtrlSockets[chan]->write(response, strlen(response) + 1);
@@ -734,15 +950,6 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
for (int i = 0; i < 4; i++)
frameNum = (frameNum << 8) | (0x0ff & buffer[i+1]);
// periodically update GSM core clock
LOG(DEBUG) << "mTransmitDeadlineClock " << mTransmitDeadlineClock
<< " mLastClockUpdateTime " << mLastClockUpdateTime;
if (!chan) {
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
writeClockInterface();
}
LOG(DEBUG) << "rcvd. burst at: " << GSM::Time(frameNum,timeSlot);
int RSSI = (int) buffer[5];
@@ -763,34 +970,45 @@ bool Transceiver::driveTxPriorityQueue(size_t chan)
void Transceiver::driveReceiveRadio()
{
if (!mRadioInterface->driveReceiveRadio())
if (!mRadioInterface->driveReceiveRadio()) {
usleep(100000);
} else {
if (mTransmitDeadlineClock > mLastClockUpdateTime + GSM::Time(216,0))
writeClockInterface();
}
}
void Transceiver::driveReceiveFIFO(size_t chan)
{
SoftVector *rxBurst = NULL;
int RSSI;
int TOA; // in 1/256 of a symbol
double RSSI; // in dBFS
double dBm; // in dBm
double TOA; // in symbols
int TOAint; // in 1/256 symbols
double noise; // noise level in dBFS
GSM::Time burstTime;
bool isRssiValid; // are RSSI, noise and burstTime valid
rxBurst = pullRadioVector(burstTime, RSSI, TOA, chan);
rxBurst = pullRadioVector(burstTime, RSSI, isRssiValid, TOA, noise, chan);
if (rxBurst) {
dBm = RSSI+rssiOffset;
TOAint = (int) (TOA * 256.0 + 0.5); // round to closest integer
LOG(DEBUG) << std::fixed << std::right
<< " time: " << burstTime
<< " RSSI: " << std::setw(5) << std::setprecision(1) << RSSI << "dBFS/" << std::setw(6) << -dBm << "dBm"
<< " noise: " << std::setw(5) << std::setprecision(1) << noise << "dBFS/" << std::setw(6) << -(noise+rssiOffset) << "dBm"
<< " TOA: " << std::setw(5) << std::setprecision(2) << TOA
<< " bits: " << *rxBurst;
LOG(DEBUG) << "burst parameters: "
<< " time: " << burstTime
<< " RSSI: " << RSSI
<< " TOA: " << TOA
<< " bits: " << *rxBurst;
char burstString[gSlotLen+10];
burstString[0] = burstTime.TN();
for (int i = 0; i < 4; i++)
burstString[1+i] = (burstTime.FN() >> ((3-i)*8)) & 0x0ff;
burstString[5] = RSSI;
burstString[6] = (TOA >> 8) & 0x0ff;
burstString[7] = TOA & 0x0ff;
burstString[5] = (int)dBm;
burstString[6] = (TOAint >> 8) & 0x0ff;
burstString[7] = TOAint & 0x0ff;
SoftVector::iterator burstItr = rxBurst->begin();
for (unsigned int i = 0; i < gSlotLen; i++) {
@@ -865,7 +1083,7 @@ void Transceiver::writeClockInterface()
LOG(INFO) << "ClockInterface: sending " << command;
mClockSocket->write(command, strlen(command) + 1);
mClockSocket.write(command, strlen(command) + 1);
mLastClockUpdateTime = mTransmitDeadlineClock;
@@ -933,15 +1151,7 @@ void *TxUpperLoopAdapter(TransceiverChannel *chan)
trx->setPriority(0.40);
while (1) {
bool stale = false;
// Flush the UDP packets until a successful transfer.
while (!trx->driveTxPriorityQueue(num)) {
stale = true;
}
if (!num && stale) {
// If a packet was stale, remind the GSM stack of the clock.
trx->writeClockInterface();
}
trx->driveTxPriorityQueue(num);
pthread_testcancel();
}
return NULL;

View File

@@ -54,7 +54,7 @@ struct TransceiverState {
~TransceiverState();
/* Initialize a multiframe slot in the filler table */
void init(size_t slot, signalVector *burst, bool fill);
bool init(int filler, size_t sps, float scale, size_t rtsc);
int chanType[8];
@@ -81,98 +81,14 @@ struct TransceiverState {
/* Received noise energy levels */
float mNoiseLev;
noiseVector mNoises;
/* Shadowed downlink attenuation */
int mPower;
};
/** The Transceiver class, responsible for physical layer of basestation */
class Transceiver {
private:
int mBasePort;
std::string mAddr;
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
UDPSocket *mClockSocket; ///< socket for writing clock updates to GSM core
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO
Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
RadioInterface *mRadioInterface; ///< associated radioInterface object
double txFullScale; ///< full scale input to radio
double rxFullScale; ///< full scale output to radio
/** Codes for burst types of received bursts*/
typedef enum {
OFF, ///< timeslot is off
TSC, ///< timeslot should contain a normal burst
RACH, ///< timeslot should contain an access burst
IDLE ///< timeslot is an idle (or dummy) burst
} CorrType;
/** modulate and add a burst to the transmit queue */
void addRadioVector(size_t chan, BitVector &bits,
int RSSI, GSM::Time &wTime);
/** Update filler table */
void updateFillerTable(size_t chan, radioVector *burst);
/** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
void pushRadioVector(GSM::Time &nowTime);
/** Pull and demodulate a burst from the receive FIFO */
SoftVector *pullRadioVector(GSM::Time &wTime, int &RSSI,
int &timingOffset, size_t chan = 0);
/** Set modulus for specific timeslot */
void setModulus(size_t timeslot, size_t chan);
/** return the expected burst type for the specified timestamp */
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
/** send messages over the clock socket */
void writeClockInterface(void);
/** Detect RACH bursts */
bool detectRACH(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa);
/** Detect normal bursts */
bool detectTSC(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa, GSM::Time &time);
/** Demodulat burst and output soft bits */
SoftVector *demodulate(TransceiverState *state,
signalVector &burst, complex amp,
float toa, size_t tn, bool equalize);
int mSPSTx; ///< number of samples per Tx symbol
int mSPSRx; ///< number of samples per Rx symbol
size_t mChans;
bool mOn; ///< flag to indicate that transceiver is powered on
double mTxFreq; ///< the transmit frequency
double mRxFreq; ///< the receive frequency
int mPower; ///< the transmit power in dB
unsigned mTSC; ///< the midamble sequence code
unsigned mMaxExpectedDelay; ///< maximum expected time-of-arrival offset in GSM symbols
std::vector<TransceiverState> mStates;
public:
/** Transceiver constructor
@param wBasePort base port number of UDP sockets
@param TRXAddress IP address of the TRX manager, as a string
@@ -181,17 +97,17 @@ public:
@param radioInterface associated radioInterface object
*/
Transceiver(int wBasePort,
const char *TRXAddress,
size_t wSPS, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface);
const char *TRXAddress,
size_t wSPS, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface,
double wRssiOffset);
/** Destructor */
~Transceiver();
/** start the Transceiver */
void start();
bool init(bool filler);
/** Start the control loop */
bool init(int filler, size_t rtsc);
/** attach the radioInterface receive FIFO */
bool receiveFIFO(VectorFIFO *wFIFO, size_t chan)
@@ -226,6 +142,108 @@ public:
LOOPBACK ///< similar go VII, used in loopback testing
} ChannelCombination;
/** Codes for burst types of received bursts*/
typedef enum {
OFF, ///< timeslot is off
TSC, ///< timeslot should contain a normal burst
RACH, ///< timeslot should contain an access burst
IDLE ///< timeslot is an idle (or dummy) burst
} CorrType;
enum FillerType {
FILLER_DUMMY,
FILLER_ZERO,
FILLER_RAND,
};
private:
int mBasePort;
std::string mAddr;
std::vector<UDPSocket *> mDataSockets; ///< socket for writing to/reading from GSM core
std::vector<UDPSocket *> mCtrlSockets; ///< socket for writing/reading control commands from GSM core
UDPSocket mClockSocket; ///< socket for writing clock updates to GSM core
std::vector<VectorQueue> mTxPriorityQueues; ///< priority queue of transmit bursts received from GSM core
std::vector<VectorFIFO *> mReceiveFIFO; ///< radioInterface FIFO of receive bursts
std::vector<Thread *> mRxServiceLoopThreads; ///< thread to pull bursts into receive FIFO
Thread *mRxLowerLoopThread; ///< thread to pull bursts into receive FIFO
Thread *mTxLowerLoopThread; ///< thread to push bursts into transmit FIFO
std::vector<Thread *> mControlServiceLoopThreads; ///< thread to process control messages from GSM core
std::vector<Thread *> mTxPriorityQueueServiceLoopThreads; ///< thread to process transmit bursts from GSM core
GSM::Time mTransmitLatency; ///< latency between basestation clock and transmit deadline clock
GSM::Time mLatencyUpdateTime; ///< last time latency was updated
GSM::Time mTransmitDeadlineClock; ///< deadline for pushing bursts into transmit FIFO
GSM::Time mLastClockUpdateTime; ///< last time clock update was sent up to core
RadioInterface *mRadioInterface; ///< associated radioInterface object
double txFullScale; ///< full scale input to radio
double rxFullScale; ///< full scale output to radio
double rssiOffset; ///< RSSI to dBm conversion offset
/** modulate and add a burst to the transmit queue */
void addRadioVector(size_t chan, BitVector &bits,
int RSSI, GSM::Time &wTime);
/** Update filler table */
void updateFillerTable(size_t chan, radioVector *burst);
/** Push modulated burst into transmit FIFO corresponding to a particular timestamp */
void pushRadioVector(GSM::Time &nowTime);
/** Pull and demodulate a burst from the receive FIFO */
SoftVector *pullRadioVector(GSM::Time &wTime, double &RSSI, bool &isRssiValid,
double &timingOffset, double &noise,
size_t chan = 0);
/** Set modulus for specific timeslot */
void setModulus(size_t timeslot, size_t chan);
/** return the expected burst type for the specified timestamp */
CorrType expectedCorrType(GSM::Time currTime, size_t chan);
/** send messages over the clock socket */
void writeClockInterface(void);
/** Detect RACH bursts */
int detectRACH(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa);
/** Detect normal bursts */
int detectTSC(TransceiverState *state,
signalVector &burst,
complex &amp, float &toa, GSM::Time &time);
/** Demodulat burst and output soft bits */
SoftVector *demodulate(TransceiverState *state,
signalVector &burst, complex amp,
float toa, size_t tn, bool equalize);
int mSPSTx; ///< number of samples per Tx symbol
int mSPSRx; ///< number of samples per Rx symbol
size_t mChans;
bool mOn; ///< flag to indicate that transceiver is powered on
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 mWriteBurstToDiskMask; ///< debug: bitmask to indicate which timeslots to dump to disk
std::vector<TransceiverState> mStates;
/** Start and stop I/O threads through the control socket API */
bool start();
void stop();
/** Protect destructor accessable stop call */
Mutex mLock;
protected:
/** drive lower receive I/O and burst generation */
void driveReceiveRadio();

View File

@@ -34,12 +34,25 @@
#define B2XX_CLK_RT 26e6
#define E1XX_CLK_RT 52e6
#define B2XX_BASE_RT GSMRATE
#define B100_BASE_RT 400000
#define USRP2_BASE_RT 390625
#define TX_AMPL 0.3
#define USRP_TX_AMPL 0.3
#define UMTRX_TX_AMPL 0.7
#define SAMPLE_BUF_SZ (1 << 20)
/*
* UHD timeout value on streaming (re)start
*
* Allow some time for streaming to commence after the start command is issued,
* but consider a wait beyond one second to be a definite error condition.
*/
#define UHD_RESTART_TIMEOUT 1.0
/*
* UmTRX specific settings
*/
#define UMTRX_VGA1_DEF -18
enum uhd_dev_type {
USRP1,
USRP2,
@@ -47,6 +60,8 @@ enum uhd_dev_type {
B200,
B210,
E1XX,
E3XX,
X3XX,
UMTRX,
NUM_USRP_TYPES,
};
@@ -81,6 +96,10 @@ static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
{ B210, 4, 6.9248e-5, "B210 4 SPS" },
{ E1XX, 1, 9.5192e-5, "E1XX 1 SPS" },
{ E1XX, 4, 6.5571e-5, "E1XX 4 SPS" },
{ E3XX, 1, 1.5000e-4, "E3XX 1 SPS" },
{ E3XX, 4, 1.2740e-4, "E3XX 4 SPS" },
{ X3XX, 1, 1.5360e-4, "X3XX 1 SPS"},
{ X3XX, 4, 1.1264e-4, "X3XX 4 SPS"},
{ UMTRX, 1, 9.9692e-5, "UmTRX 1 SPS" },
{ UMTRX, 4, 7.3846e-5, "UmTRX 4 SPS" },
};
@@ -97,7 +116,7 @@ static struct uhd_dev_offset special_offsets[] = {
static double get_dev_offset(enum uhd_dev_type type,
int sps, bool diversity = false)
{
struct uhd_dev_offset *offset;
struct uhd_dev_offset *offset = NULL;
/* Reject USRP1 */
if (type == USRP1) {
@@ -121,17 +140,21 @@ static double get_dev_offset(enum uhd_dev_type type,
offset = &special_offsets[1];
}
} else {
/* Normal operation */
switch (sps) {
case 1:
offset = &uhd_offsets[2 * type + 0];
break;
case 4:
default:
offset = &uhd_offsets[2 * type + 1];
/* Search for matching offset value */
for (int i = 0; i < NUM_USRP_TYPES * 2; i++) {
if ((type == uhd_offsets[i].type) &&
(sps == uhd_offsets[i].sps)) {
offset = &uhd_offsets[i];
break;
}
}
}
if (!offset) {
LOG(ERR) << "Invalid device configuration";
return 0.0;
}
std::cout << "-- Setting " << offset->desc << std::endl;
return offset->offset;
@@ -156,12 +179,14 @@ static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
switch (type) {
case USRP2:
case X3XX:
return USRP2_BASE_RT * sps;
case B100:
return B100_BASE_RT * sps;
case B200:
case B210:
case E1XX:
case E3XX:
case UMTRX:
return GSMRATE * sps;
default:
@@ -185,8 +210,7 @@ uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
{
TIMESTAMP ticks = ts.get_full_secs() * rate;
return ts.get_tick_count(rate) + ticks;
return (TIMESTAMP)(ts.get_real_secs() * rate + 0.5);
}
/*
@@ -225,7 +249,7 @@ public:
/** Buffer status string
@return a formatted string describing internal buffer state
*/
std::string str_status() const;
std::string str_status(size_t ts) const;
/** Formatted error string
@param code an error code
@@ -265,10 +289,10 @@ public:
uhd_device(size_t sps, size_t chans, bool diversity, double offset);
~uhd_device();
int open(const std::string &args, bool extref);
int open(const std::string &args, bool extref, bool swap_channels);
bool start();
bool stop();
void restart();
bool restart();
void setPriority(float prio);
enum TxWindowType getWindowType() { return tx_window; }
@@ -286,8 +310,8 @@ public:
inline TIMESTAMP initialWriteTimestamp() { return ts_initial * sps; }
inline TIMESTAMP initialReadTimestamp() { return ts_initial; }
inline double fullScaleInputValue() { return 32000 * TX_AMPL; }
inline double fullScaleOutputValue() { return 32000; }
double fullScaleInputValue();
double fullScaleOutputValue();
double setRxGain(double db, size_t chan);
double getRxGain(size_t chan);
@@ -313,8 +337,9 @@ public:
enum err_code {
ERROR_TIMING = -1,
ERROR_UNRECOVERABLE = -2,
ERROR_UNHANDLED = -3,
ERROR_TIMEOUT = -2,
ERROR_UNRECOVERABLE = -3,
ERROR_UNHANDLED = -4,
};
private:
@@ -358,8 +383,9 @@ private:
uhd::tune_request_t select_freq(double wFreq, size_t chan, bool tx);
bool set_freq(double freq, size_t chan, bool tx);
Thread async_event_thrd;
Thread *async_event_thrd;
bool diversity;
Mutex tune_lock;
};
void *async_event_loop(uhd_device *dev)
@@ -396,6 +422,12 @@ void uhd_msg_handler(uhd::msg::type_t type, const std::string &msg)
}
}
static void thread_enable_cancel(bool cancel)
{
cancel ? pthread_setcancelstate(PTHREAD_CANCEL_ENABLE, NULL) :
pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, NULL);
}
uhd_device::uhd_device(size_t sps, size_t chans, bool diversity, double offset)
: tx_gain_min(0.0), tx_gain_max(0.0),
rx_gain_min(0.0), rx_gain_max(0.0),
@@ -421,21 +453,43 @@ void uhd_device::init_gains()
{
uhd::gain_range_t range;
range = usrp_dev->get_tx_gain_range();
tx_gain_min = range.start();
tx_gain_max = range.stop();
if (dev_type == UMTRX) {
std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
if (gain_stages[0] == "VGA") {
LOG(WARNING) << "Update your UHD version for a proper Tx gain support";
}
if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
range = usrp_dev->get_tx_gain_range();
tx_gain_min = range.start();
tx_gain_max = range.stop();
} else {
range = usrp_dev->get_tx_gain_range("VGA2");
tx_gain_min = UMTRX_VGA1_DEF + range.start();
tx_gain_max = UMTRX_VGA1_DEF + range.stop();
}
} else {
range = usrp_dev->get_tx_gain_range();
tx_gain_min = range.start();
tx_gain_max = range.stop();
}
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);
}
@@ -474,7 +528,7 @@ int uhd_device::set_rates(double tx_rate, double rx_rate)
double tx_offset, rx_offset;
/* B2XX and E1xx are the only device where we set FPGA clocking */
if ((dev_type == B200) || (dev_type == B210)) {
if ((dev_type == B200) || (dev_type == B210) || (dev_type == E3XX)) {
if (set_master_clk(B2XX_CLK_RT) < 0)
return -1;
}
@@ -514,10 +568,26 @@ double uhd_device::setTxGain(double db, size_t chan)
return 0.0f;
}
usrp_dev->set_tx_gain(db, chan);
if (dev_type == UMTRX) {
std::vector<std::string> gain_stages = usrp_dev->get_tx_gain_names(0);
if (gain_stages[0] == "VGA" || gain_stages[0] == "PA") {
usrp_dev->set_tx_gain(db, chan);
} else {
// New UHD versions support split configuration of
// Tx gain stages. We utilize this to set the gain
// configuration, optimal for the Tx signal quality.
// From our measurements, VGA1 must be 18dB plus-minus
// one and VGA2 is the best when 23dB or lower.
usrp_dev->set_tx_gain(UMTRX_VGA1_DEF, "VGA1", chan);
usrp_dev->set_tx_gain(db-UMTRX_VGA1_DEF, "VGA2", chan);
}
} else {
usrp_dev->set_tx_gain(db, chan);
}
tx_gains[chan] = usrp_dev->get_tx_gain(chan);
LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB";
LOG(INFO) << "Set TX gain to " << tx_gains[chan] << "dB (asked for " << db << "dB)";
return tx_gains[chan];
}
@@ -532,7 +602,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];
}
@@ -557,8 +627,8 @@ bool uhd_device::parse_dev_type()
{
std::string mboard_str, dev_str;
uhd::property_tree::sptr prop_tree;
size_t usrp1_str, usrp2_str, e100_str, e110_str,
b100_str, b200_str, b210_str, umtrx_str;
size_t usrp1_str, usrp2_str, e100_str, e110_str, e310_str,
b100_str, b200_str, b210_str, x300_str, x310_str, umtrx_str;
prop_tree = usrp_dev->get_device()->get_tree();
dev_str = prop_tree->access<std::string>("/name").get();
@@ -571,6 +641,9 @@ bool uhd_device::parse_dev_type()
b210_str = mboard_str.find("B210");
e100_str = mboard_str.find("E100");
e110_str = mboard_str.find("E110");
e310_str = mboard_str.find("E310");
x300_str = mboard_str.find("X300");
x310_str = mboard_str.find("X310");
umtrx_str = dev_str.find("UmTRX");
if (usrp1_str != std::string::npos) {
@@ -598,6 +671,15 @@ bool uhd_device::parse_dev_type()
} else if (usrp2_str != std::string::npos) {
tx_window = TX_WINDOW_FIXED;
dev_type = USRP2;
} else if (e310_str != std::string::npos) {
tx_window = TX_WINDOW_FIXED;
dev_type = E3XX;
} else if (x300_str != std::string::npos) {
tx_window = TX_WINDOW_FIXED;
dev_type = X3XX;
} else if (x310_str != std::string::npos) {
tx_window = TX_WINDOW_FIXED;
dev_type = X3XX;
} else if (umtrx_str != std::string::npos) {
tx_window = TX_WINDOW_FIXED;
dev_type = UMTRX;
@@ -617,7 +699,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);
@@ -630,9 +712,9 @@ int uhd_device::open(const std::string &args, bool extref)
// Use the first found device
LOG(INFO) << "Using discovered UHD device " << dev_addrs[0].to_string();
try {
usrp_dev = uhd::usrp::multi_usrp::make(dev_addrs[0]);
usrp_dev = uhd::usrp::multi_usrp::make(addr);
} catch(...) {
LOG(ALERT) << "UHD make failed, device " << dev_addrs[0].to_string();
LOG(ALERT) << "UHD make failed, device " << args;
return -1;
}
@@ -643,7 +725,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) {
@@ -673,6 +755,16 @@ int uhd_device::open(const std::string &args, bool extref)
if (set_rates(_tx_rate, _rx_rate) < 0)
return -1;
// Set RF frontend bandwidth
if (dev_type == UMTRX) {
// Setting LMS6002D LPF to 500kHz gives us the best signal quality
for (size_t i = 0; i < chans; i++) {
usrp_dev->set_tx_bandwidth(500*1000*2, i);
if (!diversity)
usrp_dev->set_rx_bandwidth(500*1000*2, i);
}
}
/* Create TX and RX streamers */
uhd::stream_args_t stream_args("sc16");
for (size_t i = 0; i < chans; i++)
@@ -712,10 +804,12 @@ int uhd_device::open(const std::string &args, bool extref)
case B100:
return RESAMP_64M;
case USRP2:
case X3XX:
return RESAMP_100M;
case B200:
case B210:
case E1XX:
case E3XX:
default:
break;
}
@@ -727,7 +821,7 @@ bool uhd_device::flush_recv(size_t num_pkts)
{
uhd::rx_metadata_t md;
size_t num_smpls;
float timeout = 0.1f;
float timeout = UHD_RESTART_TIMEOUT;
std::vector<std::vector<short> >
pkt_bufs(chans, std::vector<short>(2 * rx_spp));
@@ -743,6 +837,8 @@ bool uhd_device::flush_recv(size_t num_pkts)
if (!num_smpls) {
switch (md.error_code) {
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
LOG(ALERT) << "Device timed out";
return false;
default:
continue;
}
@@ -756,7 +852,7 @@ bool uhd_device::flush_recv(size_t num_pkts)
return true;
}
void uhd_device::restart()
bool uhd_device::restart()
{
/* Allow 100 ms delay to align multi-channel streams */
double delay = 0.1;
@@ -771,7 +867,7 @@ void uhd_device::restart()
usrp_dev->issue_stream_cmd(cmd);
flush_recv(1);
return flush_recv(10);
}
bool uhd_device::start()
@@ -787,10 +883,12 @@ bool uhd_device::start()
uhd::msg::register_handler(&uhd_msg_handler);
// Start asynchronous event (underrun check) loop
async_event_thrd.start((void * (*)(void*))async_event_loop, (void*)this);
async_event_thrd = new Thread();
async_event_thrd->start((void * (*)(void*))async_event_loop, (void*)this);
// Start streaming
restart();
if (!restart())
return false;
// Display usrp time
double time_now = usrp_dev->get_time_now().get_real_secs();
@@ -810,6 +908,10 @@ bool uhd_device::stop()
usrp_dev->issue_stream_cmd(stream_cmd);
async_event_thrd->cancel();
async_event_thrd->join();
delete async_event_thrd;
started = false;
return true;
}
@@ -830,6 +932,7 @@ int uhd_device::check_rx_md_err(uhd::rx_metadata_t &md, ssize_t num_smpls)
switch (md.error_code) {
case uhd::rx_metadata_t::ERROR_CODE_TIMEOUT:
LOG(ALERT) << "UHD: Receive timed out";
return ERROR_TIMEOUT;
case uhd::rx_metadata_t::ERROR_CODE_OVERFLOW:
case uhd::rx_metadata_t::ERROR_CODE_LATE_COMMAND:
case uhd::rx_metadata_t::ERROR_CODE_BROKEN_CHAIN:
@@ -885,7 +988,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
rc = rx_buffers[0]->avail_smpls(timestamp);
if (rc < 0) {
LOG(ERR) << rx_buffers[0]->str_code(rc);
LOG(ERR) << rx_buffers[0]->str_status();
LOG(ERR) << rx_buffers[0]->str_status(timestamp);
return 0;
}
@@ -899,8 +1002,11 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
// Receive samples from the usrp until we have enough
while (rx_buffers[0]->avail_smpls(timestamp) < len) {
thread_enable_cancel(false);
size_t num_smpls = rx_stream->recv(pkt_ptrs, rx_spp,
metadata, 0.1, true);
thread_enable_cancel(true);
rx_pkt_cnt++;
// Check for errors
@@ -910,6 +1016,9 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
LOG(ALERT) << "UHD: Version " << uhd::get_version_string();
LOG(ALERT) << "UHD: Unrecoverable error, exiting...";
exit(-1);
case ERROR_TIMEOUT:
// Assume stopping condition
return 0;
case ERROR_TIMING:
restart();
case ERROR_UNHANDLED:
@@ -927,7 +1036,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
// Continue on local overrun, exit on other errors
if ((rc < 0)) {
LOG(ERR) << rx_buffers[i]->str_code(rc);
LOG(ERR) << rx_buffers[i]->str_status();
LOG(ERR) << rx_buffers[i]->str_status(timestamp);
if (rc != smpl_buf::ERROR_OVERFLOW)
return 0;
}
@@ -939,7 +1048,7 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
rc = rx_buffers[i]->read(bufs[i], len, timestamp);
if ((rc < 0) || (rc != len)) {
LOG(ERR) << rx_buffers[i]->str_code(rc);
LOG(ERR) << rx_buffers[i]->str_status();
LOG(ERR) << rx_buffers[i]->str_status(timestamp);
return 0;
}
}
@@ -988,7 +1097,10 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
}
}
thread_enable_cancel(false);
size_t num_smpls = tx_stream->send(bufs, len, metadata);
thread_enable_cancel(true);
if (num_smpls != (unsigned) len) {
LOG(ALERT) << "UHD: Device send timed out";
}
@@ -1007,7 +1119,20 @@ uhd::tune_request_t uhd_device::select_freq(double freq, size_t chan, bool tx)
std::vector<double> freqs;
uhd::tune_request_t treq(freq);
if ((chans == 1) || ((chans == 2) && dev_type == UMTRX)) {
if (dev_type == UMTRX) {
if (offset != 0.0)
return uhd::tune_request_t(freq, offset);
// Don't use DSP tuning, because LMS6002D PLL steps are small enough.
// We end up with DSP tuning just for 2-3Hz, which is meaningless and
// only distort the signal (because cordic is not ideal).
treq.target_freq = freq;
treq.rf_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
treq.rf_freq = freq;
treq.dsp_freq_policy = uhd::tune_request_t::POLICY_MANUAL;
treq.dsp_freq = 0.0;
return treq;
} else if (chans == 1) {
if (offset == 0.0)
return treq;
@@ -1087,6 +1212,7 @@ bool uhd_device::setTxFreq(double wFreq, size_t chan)
LOG(ALERT) << "Requested non-existent channel " << chan;
return false;
}
ScopedLock lock(tune_lock);
return set_freq(wFreq, chan, true);
}
@@ -1097,6 +1223,7 @@ bool uhd_device::setRxFreq(double wFreq, size_t chan)
LOG(ALERT) << "Requested non-existent channel " << chan;
return false;
}
ScopedLock lock(tune_lock);
return set_freq(wFreq, chan, false);
}
@@ -1121,10 +1248,27 @@ double uhd_device::getRxFreq(size_t chan)
return rx_freqs[chan];
}
double uhd_device::fullScaleInputValue()
{
if (dev_type == UMTRX)
return (double) SHRT_MAX * UMTRX_TX_AMPL;
else
return (double) SHRT_MAX * USRP_TX_AMPL;
}
double uhd_device::fullScaleOutputValue()
{
return (double) SHRT_MAX;
}
bool uhd_device::recv_async_msg()
{
uhd::async_metadata_t md;
if (!usrp_dev->get_device()->recv_async_msg(md))
thread_enable_cancel(false);
bool rc = usrp_dev->get_device()->recv_async_msg(md);
thread_enable_cancel(true);
if (!rc)
return false;
// Assume that any error requires resynchronization
@@ -1292,6 +1436,19 @@ ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
if ((timestamp + len) <= time_end)
return ERROR_TIMESTAMP;
if (timestamp < time_end) {
LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
uhd::time_spec_t ts = convert_time(timestamp, clk_rt);
LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << convert_time(ts, clk_rt) << ") rate=" << clk_rt;
// Do not return error here, because it's a rounding error and is not fatal
}
if (timestamp > time_end && time_end != 0) {
LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
uhd::time_spec_t ts = convert_time(timestamp, clk_rt);
LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << convert_time(ts, clk_rt) << ") rate=" << clk_rt;
// Do not return error here, because it's a rounding error and is not fatal
}
// Starting index
size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
@@ -1326,11 +1483,12 @@ ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
return write(buf, len, convert_time(ts, clk_rt));
}
std::string smpl_buf::str_status() const
std::string smpl_buf::str_status(size_t ts) const
{
std::ostringstream ost("Sample buffer: ");
ost << "length = " << buf_len;
ost << "timestamp = " << ts;
ost << ", length = " << buf_len;
ost << ", time_start = " << time_start;
ost << ", time_end = " << time_end;
ost << ", data_start = " << data_start;

View File

@@ -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,7 @@ bool USRPDevice::setTxFreq(double wFreq) { return true;};
bool USRPDevice::setRxFreq(double wFreq) { return true;};
#endif
RadioDevice *RadioDevice::make(size_t sps, size_t chans, bool diversity)
RadioDevice *RadioDevice::make(size_t sps, size_t chans, bool diversity, double)
{
return new USRPDevice(sps, chans, diversity);
}

View File

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

View File

@@ -65,10 +65,13 @@ struct trx_config {
unsigned port;
unsigned sps;
unsigned chans;
unsigned rtsc;
bool extref;
bool filler;
Transceiver::FillerType filler;
bool diversity;
double offset;
double rssi_offset;
bool swap_channels;
};
ConfigurationTable gConfig;
@@ -154,19 +157,23 @@ bool trx_setup_config(struct trx_config *config)
config->diversity = DEFAULT_DIVERSITY;
}
if (!config->sps)
config->sps = DEFAULT_SPS;
if (!config->chans)
config->chans = DEFAULT_CHANS;
/* Diversity only supported on 2 channels */
if (config->diversity)
config->chans = 2;
refstr = config->extref ? "Enabled" : "Disabled";
fillstr = config->filler ? "Enabled" : "Disabled";
divstr = config->diversity ? "Enabled" : "Disabled";
switch (config->filler) {
case Transceiver::FILLER_DUMMY:
fillstr = "Dummy bursts";
break;
case Transceiver::FILLER_ZERO:
fillstr = "Disabled";
break;
case Transceiver::FILLER_RAND:
fillstr = "Normal busrts with random payload";
break;
}
std::ostringstream ost("");
ost << "Config Settings" << std::endl;
@@ -180,6 +187,8 @@ bool trx_setup_config(struct trx_config *config)
ost << " C0 Filler Table......... " << fillstr << std::endl;
ost << " Diversity............... " << divstr << std::endl;
ost << " Tuning offset........... " << config->offset << std::endl;
ost << " RSSI to dBm offset...... " << config->rssi_offset << std::endl;
ost << " Swap channels........... " << config->swap_channels << std::endl;
std::cout << ost << std::endl;
return true;
@@ -235,8 +244,8 @@ Transceiver *makeTransceiver(struct trx_config *config, RadioInterface *radio)
VectorFIFO *fifo;
trx = new Transceiver(config->port, config->addr.c_str(), config->sps,
config->chans, GSM::Time(3,0), radio);
if (!trx->init(config->filler)) {
config->chans, GSM::Time(3,0), radio, config->rssi_offset);
if (!trx->init(config->filler, config->rtsc)) {
LOG(ALERT) << "Failed to initialize transceiver";
delete trx;
return NULL;
@@ -286,7 +295,10 @@ static void print_help()
" -s Samples-per-symbol (1 or 4)\n"
" -c Number of ARFCN channels (default=1)\n"
" -f Enable C0 filler table\n"
" -o Set baseband frequency offset (default=auto)\n",
" -o Set baseband frequency offset (default=auto)\n"
" -r Random burst test mode with TSC\n"
" -R RSSI to dBm offset in dB (default=0)\n"
" -S Swap channels (UmTRX only)\n",
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
}
@@ -295,14 +307,17 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
int option;
config->port = 0;
config->sps = 0;
config->chans = 0;
config->sps = DEFAULT_SPS;
config->chans = DEFAULT_CHANS;
config->rtsc = 0;
config->extref = false;
config->filler = false;
config->filler = Transceiver::FILLER_ZERO;
config->diversity = false;
config->offset = 0.0;
config->rssi_offset = 0.0;
config->swap_channels = false;
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:")) != -1) {
while ((option = getopt(argc, argv, "ha:l:i:p:c:dxfo:s:r:R:S")) != -1) {
switch (option) {
case 'h':
print_help();
@@ -330,24 +345,41 @@ static void handle_options(int argc, char **argv, struct trx_config *config)
config->extref = true;
break;
case 'f':
config->filler = true;
config->filler = Transceiver::FILLER_DUMMY;
break;
case 'o':
config->offset = atof(optarg);
break;
case 's':
config->sps = atoi(optarg);
if ((config->sps != 1) && (config->sps != 4)) {
printf("Unsupported samples-per-symbol\n\n");
print_help();
exit(0);
}
break;
case 'r':
config->rtsc = atoi(optarg);
config->filler = Transceiver::FILLER_RAND;
break;
case 'R':
config->rssi_offset = atof(optarg);
break;
case 'S':
config->swap_channels = true;
break;
default:
print_help();
exit(0);
}
}
if ((config->sps != 1) && (config->sps != 4)) {
printf("Unsupported samples-per-symbol %i\n\n", config->sps);
print_help();
exit(0);
}
if (config->rtsc > 7) {
printf("Invalid training sequence %i\n\n", config->rtsc);
print_help();
exit(0);
}
}
int main(int argc, char *argv[])
@@ -375,7 +407,7 @@ int main(int argc, char *argv[])
/* Create the low level device object */
usrp = RadioDevice::make(config.sps, config.chans,
config.diversity, config.offset);
type = usrp->open(config.dev_args, config.extref);
type = usrp->open(config.dev_args, config.extref, config.swap_channels);
if (type < 0) {
LOG(ALERT) << "Failed to create radio device" << std::endl;
goto shutdown;
@@ -391,8 +423,6 @@ int main(int argc, char *argv[])
if (!trx)
goto shutdown;
trx->start();
chans = trx->numChans();
std::cout << "-- Transceiver active with "
<< chans << " channel(s)" << std::endl;

View File

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

View File

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

View File

@@ -106,23 +106,27 @@ double RadioInterface::fullScaleOutputValue(void) {
return mRadio->fullScaleOutputValue();
}
void RadioInterface::setPowerAttenuation(double atten, size_t chan)
int RadioInterface::setPowerAttenuation(int atten, size_t chan)
{
double rfGain, digAtten;
if (chan >= mChans) {
LOG(ALERT) << "Invalid channel requested";
return;
return -1;
}
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - atten, chan);
digAtten = atten - mRadio->maxTxGain() + rfGain;
if (atten < 0.0)
atten = 0.0;
rfGain = mRadio->setTxGain(mRadio->maxTxGain() - (double) atten, chan);
digAtten = (double) atten - mRadio->maxTxGain() + rfGain;
if (digAtten < 1.0)
powerScaling[chan] = 1.0;
else
powerScaling[chan] = 1.0 / sqrt(pow(10, digAtten / 10.0));
return atten;
}
int RadioInterface::radioifyVector(signalVector &wVector,
@@ -167,15 +171,23 @@ bool RadioInterface::tuneRx(double freq, size_t chan)
return mRadio->setRxFreq(freq, chan);
}
void RadioInterface::start()
bool RadioInterface::start()
{
LOG(INFO) << "Starting radio";
if (mOn)
return true;
LOG(INFO) << "Starting radio device";
#ifdef USRP1
mAlignRadioServiceLoopThread.start((void * (*)(void*))AlignRadioServiceLoopAdapter,
(void*)this);
#endif
mRadio->start();
if (!mRadio->start())
return false;
recvCursor = 0;
sendCursor = 0;
writeTimestamp = mRadio->initialWriteTimestamp();
readTimestamp = mRadio->initialReadTimestamp();
@@ -184,6 +196,23 @@ void RadioInterface::start()
mOn = true;
LOG(INFO) << "Radio started";
return true;
}
/*
* Stop the radio device
*
* This is a pass-through call to the device interface. Because the underlying
* stop command issuance generally doesn't return confirmation on device status,
* this call will only return false if the device is already stopped.
*/
bool RadioInterface::stop()
{
if (!mOn || !mRadio->stop())
return false;
mOn = false;
return true;
}
#ifdef USRP1

View File

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

View File

@@ -28,6 +28,7 @@
#include "sigProcLib.h"
#include "GSMCommon.h"
#include "Logger.h"
extern "C" {
#include "convolve.h"
@@ -40,6 +41,9 @@ using namespace GSM;
#define TABLESIZE 1024
#define DELAYFILTS 64
/* Clipping detection threshold */
#define CLIP_THRESH 30000.0f
/** Lookup tables for trigonometric approximation */
float cosTable[TABLESIZE+1]; // add 1 element for wrap around
float sinTable[TABLESIZE+1];
@@ -723,7 +727,7 @@ static signalVector *modulateBurstLaurent(const BitVector &bits,
c1_itr = c1_burst->begin();
/* Padded differential start bits */
*c0_itr = 2.0 * (0x00 & 0x01) - 1.0;
*c0_itr = 2.0 * (0x01 & 0x01) - 1.0;
c0_itr += sps;
/* Main burst bits */
@@ -1281,12 +1285,12 @@ static float computePeakRatio(signalVector *corr,
complex *peak;
float rms, avg = 0.0;
peak = corr->begin() + (int) rint(toa);
/* Check for bogus results */
if ((toa < 0.0) || (toa > corr->size()))
return 0.0;
peak = corr->begin() + (int) rint(toa);
for (int i = 2 * sps; i <= 5 * sps; i++) {
if (peak - i >= corr->begin()) {
avg += (peak - i)->norm2();
@@ -1369,6 +1373,74 @@ static int detectBurst(signalVector &burst,
return 1;
}
static float maxAmplitude(signalVector &burst)
{
float max = 0.0;
for (size_t i = 0; i < burst.size(); i++) {
if (fabs(burst[i].real()) > max)
max = fabs(burst[i].real());
if (fabs(burst[i].imag()) > max)
max = fabs(burst[i].imag());
}
return max;
}
/*
* RACH/Normal burst detection with clipping detection
*
* Correlation window parameters:
* target: Tail bits + burst length
* head: Search symbols before target
* tail: Search symbols after target
*/
int detectGeneralBurst(signalVector &rxBurst,
float thresh,
int sps,
complex &amp,
float &toa,
int target, int head, int tail,
CorrelationSequence *sync)
{
int rc, start, len;
bool clipping = false;
signalVector *corr;
if ((sps != 1) && (sps != 4))
return -SIGERR_UNSUPPORTED;
// Detect potential clipping
// We still may be able to demod the burst, so we'll give it a try
// and only report clipping if we can't demod.
float maxAmpl = maxAmplitude(rxBurst);
if (maxAmpl > CLIP_THRESH) {
LOG(DEBUG) << "max burst amplitude: " << maxAmpl << " is above the clipping threshold: " << CLIP_THRESH << std::endl;
clipping = true;
}
start = (target - head) * sps - 1;
len = (head + tail) * sps;
corr = new signalVector(len);
rc = detectBurst(rxBurst, *corr, sync,
thresh, sps, &amp, &toa, start, len);
delete corr;
if (rc < 0) {
return -SIGERR_INTERNAL;
} else if (!rc) {
amp = 0.0f;
toa = 0.0f;
return clipping?-SIGERR_CLIP:SIGERR_NONE;
}
/* Subtract forward search bits from delay */
toa -= head * sps;
return 1;
}
/*
* RACH burst detection
*
@@ -1378,50 +1450,23 @@ static int detectBurst(signalVector &burst,
* tail: Search 10 symbols after target
*/
int detectRACHBurst(signalVector &rxBurst,
float thresh,
int sps,
complex *amp,
float *toa)
float thresh,
int sps,
complex &amp,
float &toa)
{
int rc, start, target, head, tail, len;
float _toa;
complex _amp;
signalVector *corr;
int rc, target, head, tail;
CorrelationSequence *sync;
if ((sps != 1) && (sps != 4))
return -1;
target = 8 + 40;
head = 4;
tail = 10;
start = (target - head) * sps - 1;
len = (head + tail) * sps;
sync = gRACHSequence;
corr = new signalVector(len);
rc = detectBurst(rxBurst, *corr, sync,
thresh, sps, &_amp, &_toa, start, len);
delete corr;
rc = detectGeneralBurst(rxBurst, thresh, sps, amp, toa,
target, head, tail, sync);
if (rc < 0) {
return -1;
} else if (!rc) {
if (amp)
*amp = 0.0f;
if (toa)
*toa = 0.0f;
return 0;
}
/* Subtract forward search bits from delay */
if (toa)
*toa = _toa - head * sps;
if (amp)
*amp = _amp;
return 1;
return rc;
}
/*
@@ -1433,57 +1478,32 @@ int detectRACHBurst(signalVector &rxBurst,
* tail: Search 4 symbols + maximum expected delay
*/
int analyzeTrafficBurst(signalVector &rxBurst, unsigned tsc, float thresh,
int sps, complex *amp, float *toa, unsigned max_toa,
int sps, complex &amp, float &toa, unsigned max_toa,
bool chan_req, signalVector **chan, float *chan_offset)
{
int rc, start, target, head, tail, len;
complex _amp;
float _toa;
signalVector *corr;
int rc, target, head, tail;
CorrelationSequence *sync;
if ((tsc < 0) || (tsc > 7) || ((sps != 1) && (sps != 4)))
return -1;
if ((tsc < 0) || (tsc > 7))
return -SIGERR_UNSUPPORTED;
target = 3 + 58 + 16 + 5;
head = 4;
tail = 4 + max_toa;
start = (target - head) * sps - 1;
len = (head + tail) * sps;
sync = gMidambles[tsc];
corr = new signalVector(len);
rc = detectBurst(rxBurst, *corr, sync,
thresh, sps, &_amp, &_toa, start, len);
delete corr;
if (rc < 0) {
return -1;
} else if (!rc) {
if (amp)
*amp = 0.0f;
if (toa)
*toa = 0.0f;
return 0;
}
/* Subtract forward search bits from delay */
_toa -= head * sps;
if (toa)
*toa = _toa;
if (amp)
*amp = _amp;
rc = detectGeneralBurst(rxBurst, thresh, sps, amp, toa,
target, head, tail, sync);
/* Equalization not currently supported */
if (chan_req) {
if (rc > 0 && chan_req) {
*chan = new signalVector(6 * sps);
if (chan_offset)
*chan_offset = 0.0;
}
return 1;
return rc;
}
signalVector *decimateVector(signalVector &wVector, size_t factor)

View File

@@ -28,6 +28,14 @@ enum ConvType {
UNDEFINED,
};
enum signalError {
SIGERR_NONE,
SIGERR_BOUNDS,
SIGERR_CLIP,
SIGERR_UNSUPPORTED,
SIGERR_INTERNAL,
};
/** Convert a linear number to a dB value */
float dB(float x);
@@ -184,8 +192,8 @@ bool energyDetect(signalVector &rxBurst,
int detectRACHBurst(signalVector &rxBurst,
float detectThreshold,
int sps,
complex *amplitude,
float* TOA);
complex &amplitude,
float &TOA);
/**
Normal burst correlator, detector, channel estimator.
@@ -202,15 +210,15 @@ int detectRACHBurst(signalVector &rxBurst,
@return positive if threshold value is reached, negative on error, zero otherwise
*/
int analyzeTrafficBurst(signalVector &rxBurst,
unsigned TSC,
float detectThreshold,
int sps,
complex *amplitude,
float *TOA,
unsigned TSC,
float detectThreshold,
int sps,
complex &amplitude,
float &TOA,
unsigned maxTOA,
bool requestChannel = false,
signalVector** channelResponse = NULL,
float *channelResponseOffset = NULL);
signalVector** channelResponse = NULL,
float *channelResponseOffset = NULL);
/**
Decimate a vector.

View File

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

View File

@@ -29,7 +29,7 @@ AC_CANONICAL_BUILD
AC_CANONICAL_HOST
AC_CANONICAL_TARGET
AM_INIT_AUTOMAKE
AM_INIT_AUTOMAKE([subdir-objects])
dnl Linux kernel KBuild style compile messages
m4_ifdef([AM_SILENT_RULES], [AM_SILENT_RULES([yes])])
@@ -78,6 +78,11 @@ AC_ARG_WITH(neon-vfpv4, [
[enable ARM NEON FMA support])
])
AC_ARG_WITH(sse, [
AS_HELP_STRING([--with-sse],
[enable x86 SSE support (default)])
])
AS_IF([test "x$with_neon" = "xyes"], [
AC_DEFINE(HAVE_NEON, 1, Support ARM NEON)
])
@@ -101,7 +106,9 @@ AS_IF([test "x$with_singledb" = "xyes"], [
])
# Find and define supported SIMD extensions
AX_EXT
AS_IF([test "x$with_sse" != "xno"], [
AX_EXT
])
AM_CONDITIONAL(USRP1, [test "x$with_usrp1" = "xyes"])
AM_CONDITIONAL(ARCH_ARM, [test "x$with_neon" = "xyes" || test "x$with_neon_vfpv4" = "xyes"])

19
debian/changelog vendored Normal file
View File

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

1
debian/compat vendored Normal file
View File

@@ -0,0 +1 @@
9

24
debian/control vendored Normal file
View File

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

25
debian/copyright vendored Normal file
View File

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

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

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

15
debian/rules vendored Executable file
View File

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

1
debian/source/format vendored Normal file
View File

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

3
utils/clockdump.sh Executable file
View File

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