Compare commits

...

68 Commits

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

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

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

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

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

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

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:22:41 -07:00
Alexander Chemeris
c052aa1d4c uhd: Fix rounding error in timestamp conversion functions.
Rounding error introduced oscilating timing advance error by regularly
overwriting one bit and then skipping one bit.

This commit also adds an error message to show up in logs if this ever
happens again.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:22:28 -07:00
Alexander Chemeris
130a8007fa sigProcLib: Abstract out common part of Normal/RACH burst detection.
As a side change - get rid of passing toa and amp arguments as pointers and use
references instead.

The commit doesn't change behaviour, but makes the code cleaner.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:22:13 -07:00
Alexander Chemeris
72e8619632 Checking in clockdump.sh utility.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:20:22 -07:00
Alexander Chemeris
2beb1adcea Checking in a more relevant README.
This READMY is from the OpenBTS's TRXManager and actually describes the transceiver
API and behavior.
2015-07-30 14:19:51 -07:00
Alexander Chemeris
2b542100a0 Transceiver: Update noise level only when the timeslot is marked as IDLE.
We can't rely on an assumption that if we can't decode a burst - it's noise.
There are many rasons why we can't decode a burst even if it's well above the
noise level. Just one example is a RACH burst which can be overlapped with
another RACH burst up to a level both are completely unrecognizable. Another
example is when a burst is destroyed by bad multi-path.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:19:32 -07:00
Alexander Chemeris
2268c8558c transceiver: Remove noise/RSSI gating.
It does more harm than good. the current noise calculation is too error
prone, so we can't trust it. And we end up loosing perfectly good bursts
because of that.

Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:19:17 -07:00
Alexander Chemeris
50747dc65d osmo-trx: Add an option to swap channels on UmTRX.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:19:06 -07:00
Alexander Chemeris
1e9b4d57da sigProcLib: Check for bogus TOA before using it.
Signed-off-by: Tom Tsou <tom.tsou@ettus.com>
2015-07-30 14:18:31 -07:00
Alexander Chemeris
954b118bfa Transceiver: Fix clipping detection.
There are two primary changes in this commit:

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

2) Clipping level is not the final verdict now. We still try to demod a burst
and mark it as clipped only if the level is above the clipping level AND we can't
demod it. The reasoning for this is that in real life we want to do as much as
possible to demod the burst, because we want to get as much from our dynamic
range as possible. So a little bit of clipping is fine and is expected. We just
don't want too much of it to break our demod.

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

View File

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

View File

@@ -38,6 +38,14 @@
using namespace std;
// Switches to enable/disable logging targets
// MUST BE DEFINED BEFORE gConfig FOR gLogEarly() TO WORK CORRECTLY
bool gLogToConsole = true;
bool gLogToSyslog = false;
FILE *gLogToFile = NULL;
Mutex gLogToLock;
// Reference to a global config table, used all over the system.
extern ConfigurationTable gConfig;
@@ -67,9 +75,6 @@ const char *levelNames[] = {
"EMERG", "ALERT", "CRIT", "ERR", "WARNING", "NOTICE", "INFO", "DEBUG"
};
int numLevels = 8;
bool gLogToConsole = 0;
FILE *gLogToFile = NULL;
Mutex gLogToLock;
int levelStringToInt(const string& name)
@@ -192,18 +197,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 +222,6 @@ Log::~Log()
if (neednl) {fputc('\n',gLogToFile);}
fflush(gLogToFile);
}
gLogToLock.unlock();
}
}
@@ -243,10 +249,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.
@@ -268,9 +273,32 @@ void gLogInit(const char* name, const char* level, int facility)
void gLogEarly(int level, const char *fmt, ...)
{
va_list args;
va_start(args, fmt);
vsyslog(level | LOG_USER, fmt, args);
if (gLogToSyslog) {
va_list args_copy;
va_copy(args_copy, args);
vsyslog(level | LOG_USER, fmt, args_copy);
va_end(args_copy);
}
if (gLogToConsole) {
va_list args_copy;
va_copy(args_copy, args);
vprintf(fmt, args_copy);
printf("\n");
va_end(args_copy);
}
if (gLogToFile) {
va_list args_copy;
va_copy(args_copy, args);
vfprintf(gLogToFile, fmt, args_copy);
fprintf(gLogToFile, "\n");
va_end(args_copy);
}
va_end(args);
}

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -54,125 +54,27 @@ 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);
void updateNoiseEstimates(double avg);
int chanType[8];
/* Last timestamp of each timeslot's channel estimate */
GSM::Time chanEstimateTime[8];
/* The filler table */
signalVector *fillerTable[102][8];
int fillerModulus[8];
bool mRetrans;
/* Most recent channel estimate of all timeslots */
signalVector *chanResponse[8];
/* Most recent DFE feedback filter of all timeslots */
signalVector *DFEForward[8];
signalVector *DFEFeedback[8];
/* Most recent SNR, timing, and channel amplitude estimates */
float SNRestimate[8];
float chanRespOffset[8];
complex chanRespAmplitude[8];
/* Received noise energy levels */
float mNoiseLev;
noiseVector mNoises;
/* 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 +83,18 @@ public:
@param radioInterface associated radioInterface object
*/
Transceiver(int wBasePort,
const char *TRXAddress,
size_t wSPS, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface);
const char *TRXAddress,
size_t wSPS, size_t chans,
GSM::Time wTransmitLatency,
RadioInterface *wRadioInterface,
double wRssiOffset,
bool wExternalDemod);
/** Destructor */
~Transceiver();
/** 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 +129,109 @@ 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
bool mExternalDemod; ///< Should we internal or external demod
/** modulate and add a burst to the transmit queue */
void addRadioVector(size_t chan, BitVector &bits,
int RSSI, GSM::Time &wTime);
/** 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 *demodSignalVector(signalVector *burst,
CorrType type,
double &timingOffset);
/** choose the channel to use */
signalVector *chooseDiversityPath(radioVector *radio_burst, double &avg);
/** update noise estimate */
double updateNoiseEstimates(TransceiverState *state, double avg);
/** Set modulus for specific timeslot */
void setModulus(size_t timeslot, size_t chan);
/** 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(signalVector &burst,
complex &amp, float &toa);
/** Detect normal bursts */
int detectTSC(signalVector &burst,
complex &amp, float &toa);
int mSPSTx; ///< number of samples per Tx symbol
int mSPSRx; ///< number of samples per Rx symbol
size_t mChans;
bool mOn; ///< flag to indicate that transceiver is powered on
bool 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();
@@ -233,6 +239,19 @@ protected:
/** drive demodulation of GSM bursts */
void driveReceiveFIFO(size_t chan);
/** format a common header for packets with sent over the network */
int formatCommonPacketHeader(GSM::Time burstTime, double dBm, double TOA,
char *burstString);
/** format a packet of soft-bits to be sent over the network */
int formatDemodPacket(GSM::Time burstTime, double dBm, double TOA,
SoftVector *rxBurst, char *burstString);
/** format a packet of raw samples to be sent over the network */
int formatRawPacket(GSM::Time burstTime, double dBm, double TOA,
CorrType burstType, unsigned tsc,
signalVector *rxBurst, char *burstString);
/** drive transmission of GSM bursts */
void driveTxFIFO();

View File

@@ -34,19 +34,35 @@
#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,
B100,
B200,
B205,
B210,
E1XX,
E3XX,
X3XX,
UMTRX,
NUM_USRP_TYPES,
};
@@ -58,6 +74,17 @@ struct uhd_dev_offset {
const std::string desc;
};
/*
* USRP version dependent device timings
*/
#ifdef USE_UHD_3_9
#define B2XX_TIMING_1SPS 1.7153e-4
#define B2XX_TIMING_4SPS 1.1696e-4
#else
#define B2XX_TIMING_1SPS 9.9692e-5
#define B2XX_TIMING_4SPS 6.9248e-5
#endif
/*
* Tx / Rx sample offset values. In a perfect world, there is no group delay
* though analog components, and behaviour through digital filters exactly
@@ -75,12 +102,18 @@ static struct uhd_dev_offset uhd_offsets[NUM_USRP_TYPES * 2] = {
{ USRP2, 4, 8.0230e-5, "N2XX 4 SPS" },
{ B100, 1, 1.2104e-4, "B100 1 SPS" },
{ B100, 4, 7.9307e-5, "B100 4 SPS" },
{ B200, 1, 9.9692e-5, "B200 1 SPS" },
{ B200, 4, 6.9248e-5, "B200 4 SPS" },
{ B210, 1, 9.9692e-5, "B210 1 SPS" },
{ B210, 4, 6.9248e-5, "B210 4 SPS" },
{ B200, 1, B2XX_TIMING_1SPS, "B200 1 SPS" },
{ B200, 4, B2XX_TIMING_4SPS, "B200 4 SPS" },
{ B205, 1, B2XX_TIMING_1SPS, "B200-mini 1 SPS" },
{ B205, 4, B2XX_TIMING_4SPS, "B200-mini 4 SPS" },
{ B210, 1, B2XX_TIMING_1SPS, "B210 1 SPS" },
{ B210, 4, B2XX_TIMING_4SPS, "B210 4 SPS" },
{ E1XX, 1, 9.5192e-5, "E1XX 1 SPS" },
{ E1XX, 4, 6.5571e-5, "E1XX 4 SPS" },
{ E3XX, 1, 1.5000e-4, "E3XX 1 SPS" },
{ 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 +130,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 +154,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 +193,15 @@ 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 B205:
case B210:
case E1XX:
case E3XX:
case UMTRX:
return GSMRATE * sps;
default:
@@ -172,23 +212,6 @@ static double select_rate(uhd_dev_type type, int sps, bool diversity = false)
return -9999.99;
}
/** Timestamp conversion
@param timestamp a UHD or OpenBTS timestamp
@param rate sample rate
@return the converted timestamp
*/
uhd::time_spec_t convert_time(TIMESTAMP ticks, double rate)
{
double secs = (double) ticks / rate;
return uhd::time_spec_t(secs);
}
TIMESTAMP convert_time(uhd::time_spec_t ts, double rate)
{
TIMESTAMP ticks = ts.get_full_secs() * rate;
return ts.get_tick_count(rate) + ticks;
}
/*
Sample Buffer - Allows reading and writing of timed samples using OpenBTS
or UHD style timestamps. Time conversions are handled
@@ -225,7 +248,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 +288,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 +309,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 +336,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 +382,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 +421,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 +452,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 +527,7 @@ int uhd_device::set_rates(double tx_rate, double rx_rate)
double tx_offset, rx_offset;
/* B2XX and E1xx are the only device where we set FPGA clocking */
if ((dev_type == B200) || (dev_type == B210)) {
if ((dev_type == B200) || (dev_type == B205) || (dev_type == B210) || (dev_type == E3XX)) {
if (set_master_clk(B2XX_CLK_RT) < 0)
return -1;
}
@@ -514,10 +567,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 +601,7 @@ double uhd_device::setRxGain(double db, size_t chan)
usrp_dev->set_rx_gain(db, chan);
rx_gains[chan] = usrp_dev->get_rx_gain(chan);
LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB";
LOG(INFO) << "Set RX gain to " << rx_gains[chan] << "dB (asked for " << db << "dB)";
return rx_gains[chan];
}
@@ -557,8 +626,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, b205_str, b210_str, x300_str, x310_str, umtrx_str;
prop_tree = usrp_dev->get_device()->get_tree();
dev_str = prop_tree->access<std::string>("/name").get();
@@ -568,9 +637,13 @@ bool uhd_device::parse_dev_type()
usrp2_str = dev_str.find("USRP2");
b100_str = mboard_str.find("B100");
b200_str = mboard_str.find("B200");
b205_str = mboard_str.find("B205");
b210_str = mboard_str.find("B210");
e100_str = mboard_str.find("E100");
e110_str = mboard_str.find("E110");
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) {
@@ -586,6 +659,9 @@ bool uhd_device::parse_dev_type()
} else if (b200_str != std::string::npos) {
tx_window = TX_WINDOW_USRP1;
dev_type = B200;
} else if (b205_str != std::string::npos) {
tx_window = TX_WINDOW_USRP1;
dev_type = B205;
} else if (b210_str != std::string::npos) {
tx_window = TX_WINDOW_USRP1;
dev_type = B210;
@@ -598,6 +674,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 +702,7 @@ bool uhd_device::parse_dev_type()
return true;
}
int uhd_device::open(const std::string &args, bool extref)
int uhd_device::open(const std::string &args, bool extref, bool swap_channels)
{
// Find UHD devices
uhd::device_addr_t addr(args);
@@ -630,9 +715,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 +728,7 @@ int uhd_device::open(const std::string &args, bool extref)
// Verify and set channels
if ((dev_type == B210) && (chans == 2)) {
} else if ((dev_type == UMTRX) && (chans == 2)) {
uhd::usrp::subdev_spec_t subdev_spec("A:0 B:0");
uhd::usrp::subdev_spec_t subdev_spec(swap_channels?"B:0 A:0":"A:0 B:0");
usrp_dev->set_tx_subdev_spec(subdev_spec);
usrp_dev->set_rx_subdev_spec(subdev_spec);
} else if (chans != 1) {
@@ -673,6 +758,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 +807,13 @@ 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 B205:
case B210:
case E1XX:
case E3XX:
default:
break;
}
@@ -727,7 +825,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,12 +841,14 @@ 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;
}
}
ts_initial = convert_time(md.time_spec, rx_rate);
ts_initial = md.time_spec.to_ticks(rx_rate);
}
LOG(INFO) << "Initial timestamp " << ts_initial << std::endl;
@@ -756,7 +856,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 +871,7 @@ void uhd_device::restart()
usrp_dev->issue_stream_cmd(cmd);
flush_recv(1);
return flush_recv(10);
}
bool uhd_device::start()
@@ -787,10 +887,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 +912,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 +936,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:
@@ -878,14 +985,14 @@ int uhd_device::readSamples(std::vector<short *> &bufs, int len, bool *overrun,
// Shift read time with respect to transmit clock
timestamp += ts_offset;
ts = convert_time(timestamp, rx_rate);
ts = uhd::time_spec_t::from_ticks(timestamp, rx_rate);
LOG(DEBUG) << "Requested timestamp = " << ts.get_real_secs();
// Check that timestamp is valid
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 +1006,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 +1020,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 +1040,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 +1052,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;
}
}
@@ -954,7 +1067,7 @@ int uhd_device::writeSamples(std::vector<short *> &bufs, int len, bool *underrun
metadata.has_time_spec = true;
metadata.start_of_burst = false;
metadata.end_of_burst = false;
metadata.time_spec = convert_time(timestamp, tx_rate);
metadata.time_spec = uhd::time_spec_t::from_ticks(timestamp, tx_rate);
*underrun = false;
@@ -988,7 +1101,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 +1123,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 +1216,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 +1227,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 +1252,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
@@ -1233,7 +1381,7 @@ ssize_t smpl_buf::avail_smpls(TIMESTAMP timestamp) const
ssize_t smpl_buf::avail_smpls(uhd::time_spec_t timespec) const
{
return avail_smpls(convert_time(timespec, clk_rt));
return avail_smpls(timespec.to_ticks(clk_rt));
}
ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
@@ -1279,7 +1427,7 @@ ssize_t smpl_buf::read(void *buf, size_t len, TIMESTAMP timestamp)
ssize_t smpl_buf::read(void *buf, size_t len, uhd::time_spec_t ts)
{
return read(buf, len, convert_time(ts, clk_rt));
return read(buf, len, ts.to_ticks(clk_rt));
}
ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
@@ -1292,6 +1440,19 @@ ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
if ((timestamp + len) <= time_end)
return ERROR_TIMESTAMP;
if (timestamp < time_end) {
LOG(ERR) << "Overwriting old buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
// Do not return error here, because it's a rounding error and is not fatal
}
if (timestamp > time_end && time_end != 0) {
LOG(ERR) << "Skipping buffer data: timestamp="<<timestamp<<" time_end="<<time_end;
uhd::time_spec_t ts = uhd::time_spec_t::from_ticks(timestamp, clk_rt);
LOG(DEBUG) << "Requested timestamp = " << timestamp << " (real_sec=" << std::fixed << ts.get_real_secs() << " = " << ts.to_ticks(clk_rt) << ") rate=" << clk_rt;
// Do not return error here, because it's a rounding error and is not fatal
}
// Starting index
size_t write_start = (data_start + (timestamp - time_start)) % buf_len;
@@ -1323,14 +1484,15 @@ ssize_t smpl_buf::write(void *buf, size_t len, TIMESTAMP timestamp)
ssize_t smpl_buf::write(void *buf, size_t len, uhd::time_spec_t ts)
{
return write(buf, len, convert_time(ts, clk_rt));
return write(buf, len, ts.to_ticks(clk_rt));
}
std::string smpl_buf::str_status() 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

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

View File

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

View File

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

View File

@@ -65,10 +65,14 @@ 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;
bool external_demod;
};
ConfigurationTable gConfig;
@@ -154,19 +158,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 +188,9 @@ bool trx_setup_config(struct trx_config *config)
ost << " C0 Filler Table......... " << fillstr << std::endl;
ost << " Diversity............... " << divstr << std::endl;
ost << " Tuning offset........... " << config->offset << std::endl;
ost << " RSSI to dBm offset...... " << config->rssi_offset << std::endl;
ost << " Swap channels........... " << config->swap_channels << std::endl;
ost << " External demodulator.... " << config->external_demod << std::endl;
std::cout << ost << std::endl;
return true;
@@ -235,8 +246,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, config->external_demod);
if (!trx->init(config->filler, config->rtsc)) {
LOG(ALERT) << "Failed to initialize transceiver";
delete trx;
return NULL;
@@ -286,8 +297,12 @@ 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",
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
" -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"
" -e External demodulator - stream raw samples instead of soft bits (default=internal)\n",
"EMERG, ALERT, CRT, ERR, WARNING, NOTICE, INFO, DEBUG");
}
static void handle_options(int argc, char **argv, struct trx_config *config)
@@ -295,14 +310,18 @@ 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;
config->external_demod = 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:Se")) != -1) {
switch (option) {
case 'h':
print_help();
@@ -330,24 +349,44 @@ 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;
case 'e':
config->external_demod = 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 +414,7 @@ int main(int argc, char *argv[])
/* Create the low level device object */
usrp = RadioDevice::make(config.sps, config.chans,
config.diversity, config.offset);
type = usrp->open(config.dev_args, config.extref);
type = usrp->open(config.dev_args, config.extref, config.swap_channels);
if (type < 0) {
LOG(ALERT) << "Failed to create radio device" << std::endl;
goto shutdown;
@@ -391,8 +430,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];
@@ -143,64 +147,8 @@ void sigProcLibDestroy()
GSMPulse1 = NULL;
}
// dB relative to 1.0.
// if > 1.0, then return 0 dB
float dB(float x) {
float arg = 1.0F;
float dB = 0.0F;
if (x >= 1.0F) return 0.0F;
if (x <= 0.0F) return -200.0F;
float prevArg = arg;
float prevdB = dB;
float stepSize = 16.0F;
float dBstepSize = 12.0F;
while (stepSize > 1.0F) {
do {
prevArg = arg;
prevdB = dB;
arg /= stepSize;
dB -= dBstepSize;
} while (arg > x);
arg = prevArg;
dB = prevdB;
stepSize *= 0.5F;
dBstepSize -= 3.0F;
}
return ((arg-x)*(dB-3.0F) + (x-arg*0.5F)*dB)/(arg - arg*0.5F);
}
// 10^(-dB/10), inverse of dB func.
float dBinv(float x) {
float arg = 1.0F;
float dB = 0.0F;
if (x >= 0.0F) return 1.0F;
if (x <= -200.0F) return 0.0F;
float prevArg = arg;
float prevdB = dB;
float stepSize = 16.0F;
float dBstepSize = 12.0F;
while (stepSize > 1.0F) {
do {
prevArg = arg;
prevdB = dB;
arg /= stepSize;
dB -= dBstepSize;
} while (dB > x);
arg = prevArg;
dB = prevdB;
stepSize *= 0.5F;
dBstepSize -= 3.0F;
}
return ((dB-x)*(arg*0.5F)+(x-(dB-3.0F))*(arg))/3.0F;
double dB2(double x) {
return 20.0 * log10(x);
}
float vectorNorm2(const signalVector &x)
@@ -723,7 +671,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 +1229,12 @@ static float computePeakRatio(signalVector *corr,
complex *peak;
float rms, avg = 0.0;
peak = corr->begin() + (int) rint(toa);
/* Check for bogus results */
if ((toa < 0.0) || (toa > corr->size()))
return 0.0;
peak = corr->begin() + (int) rint(toa);
for (int i = 2 * sps; i <= 5 * sps; i++) {
if (peak - i >= corr->begin()) {
avg += (peak - i)->norm2();
@@ -1369,6 +1317,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 +1394,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 +1422,23 @@ int detectRACHBurst(signalVector &rxBurst,
* tail: Search 4 symbols + maximum expected delay
*/
int analyzeTrafficBurst(signalVector &rxBurst, unsigned tsc, float thresh,
int sps, complex *amp, float *toa, unsigned max_toa,
bool chan_req, signalVector **chan, float *chan_offset)
int sps, complex &amp, float &toa, unsigned max_toa)
{
int rc, start, target, head, tail, len;
complex _amp;
float _toa;
signalVector *corr;
int rc, target, head, tail;
CorrelationSequence *sync;
if ((tsc < 0) || (tsc > 7) || ((sps != 1) && (sps != 4)))
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;
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 */
_toa -= head * sps;
if (toa)
*toa = _toa;
if (amp)
*amp = _amp;
/* Equalization not currently supported */
if (chan_req) {
*chan = new signalVector(6 * sps);
if (chan_offset)
*chan_offset = 0.0;
}
return 1;
return rc;
}
signalVector *decimateVector(signalVector &wVector, size_t factor)
@@ -1539,166 +1494,6 @@ SoftVector *demodulateBurst(signalVector &rxBurst, int sps,
return bits;
}
// Assumes symbol-spaced sampling!!!
// Based upon paper by Al-Dhahir and Cioffi
bool designDFE(signalVector &channelResponse,
float SNRestimate,
int Nf,
signalVector **feedForwardFilter,
signalVector **feedbackFilter)
{
signalVector G0(Nf);
signalVector G1(Nf);
signalVector::iterator G0ptr = G0.begin();
signalVector::iterator G1ptr = G1.begin();
signalVector::iterator chanPtr = channelResponse.begin();
int nu = channelResponse.size()-1;
*G0ptr = 1.0/sqrtf(SNRestimate);
for(int j = 0; j <= nu; j++) {
*G1ptr = chanPtr->conj();
G1ptr++; chanPtr++;
}
signalVector *L[Nf];
signalVector::iterator Lptr;
float d = 1.0;
for(int i = 0; i < Nf; i++) {
d = G0.begin()->norm2() + G1.begin()->norm2();
L[i] = new signalVector(Nf+nu);
Lptr = L[i]->begin()+i;
G0ptr = G0.begin(); G1ptr = G1.begin();
while ((G0ptr < G0.end()) && (Lptr < L[i]->end())) {
*Lptr = (*G0ptr*(G0.begin()->conj()) + *G1ptr*(G1.begin()->conj()) )/d;
Lptr++;
G0ptr++;
G1ptr++;
}
complex k = (*G1.begin())/(*G0.begin());
if (i != Nf-1) {
signalVector G0new = G1;
scaleVector(G0new,k.conj());
addVector(G0new,G0);
signalVector G1new = G0;
scaleVector(G1new,k*(-1.0));
addVector(G1new,G1);
delayVector(&G1new, &G1new, -1.0);
scaleVector(G0new,1.0/sqrtf(1.0+k.norm2()));
scaleVector(G1new,1.0/sqrtf(1.0+k.norm2()));
G0 = G0new;
G1 = G1new;
}
}
*feedbackFilter = new signalVector(nu);
L[Nf-1]->segmentCopyTo(**feedbackFilter,Nf,nu);
scaleVector(**feedbackFilter,(complex) -1.0);
conjugateVector(**feedbackFilter);
signalVector v(Nf);
signalVector::iterator vStart = v.begin();
signalVector::iterator vPtr;
*(vStart+Nf-1) = (complex) 1.0;
for(int k = Nf-2; k >= 0; k--) {
Lptr = L[k]->begin()+k+1;
vPtr = vStart + k+1;
complex v_k = 0.0;
for (int j = k+1; j < Nf; j++) {
v_k -= (*vPtr)*(*Lptr);
vPtr++; Lptr++;
}
*(vStart + k) = v_k;
}
*feedForwardFilter = new signalVector(Nf);
signalVector::iterator w = (*feedForwardFilter)->end();
for (int i = 0; i < Nf; i++) {
delete L[i];
complex w_i = 0.0;
int endPt = ( nu < (Nf-1-i) ) ? nu : (Nf-1-i);
vPtr = vStart+i;
chanPtr = channelResponse.begin();
for (int k = 0; k < endPt+1; k++) {
w_i += (*vPtr)*(chanPtr->conj());
vPtr++; chanPtr++;
}
*--w = w_i/d;
}
return true;
}
// Assumes symbol-rate sampling!!!!
SoftVector *equalizeBurst(signalVector &rxBurst,
float TOA,
int sps,
signalVector &w, // feedforward filter
signalVector &b) // feedback filter
{
signalVector *postForwardFull;
if (!delayVector(&rxBurst, &rxBurst, -TOA))
return NULL;
postForwardFull = convolve(&rxBurst, &w, NULL,
CUSTOM, 0, rxBurst.size() + w.size() - 1);
if (!postForwardFull)
return NULL;
signalVector* postForward = new signalVector(rxBurst.size());
postForwardFull->segmentCopyTo(*postForward,w.size()-1,rxBurst.size());
delete postForwardFull;
signalVector::iterator dPtr = postForward->begin();
signalVector::iterator dBackPtr;
signalVector::iterator rotPtr = GMSKRotationN->begin();
signalVector::iterator revRotPtr = GMSKReverseRotationN->begin();
signalVector *DFEoutput = new signalVector(postForward->size());
signalVector::iterator DFEItr = DFEoutput->begin();
// NOTE: can insert the midamble and/or use midamble to estimate BER
for (; dPtr < postForward->end(); dPtr++) {
dBackPtr = dPtr-1;
signalVector::iterator bPtr = b.begin();
while ( (bPtr < b.end()) && (dBackPtr >= postForward->begin()) ) {
*dPtr = *dPtr + (*bPtr)*(*dBackPtr);
bPtr++;
dBackPtr--;
}
*dPtr = *dPtr * (*revRotPtr);
*DFEItr = *dPtr;
// make decision on symbol
*dPtr = (dPtr->real() > 0.0) ? 1.0 : -1.0;
//*DFEItr = *dPtr;
*dPtr = *dPtr * (*rotPtr);
DFEItr++;
rotPtr++;
revRotPtr++;
}
vectorSlicer(DFEoutput);
SoftVector *burstBits = new SoftVector(postForward->size());
SoftVector::iterator burstItr = burstBits->begin();
DFEItr = DFEoutput->begin();
for (; DFEItr < DFEoutput->end(); DFEItr++)
*burstItr++ = DFEItr->real();
delete postForward;
delete DFEoutput;
return burstBits;
}
bool sigProcLibSetup(int sps)
{
if ((sps != 1) && (sps != 4))

View File

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

View File

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

View File

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

View File

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

View File

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